Authentication¶
ALPCRUN.CH uses API key-based authentication for all client and worker connections.
Overview¶
All gRPC requests to ALPCRUN.CH services require authentication via:
- API Key: Secret key passed in gRPC metadata
- TLS: Transport security (recommended for production)
API Key Setup¶
Generate API Key¶
Generate a secure random key:
# Using openssl
openssl rand -hex 32
# Using Python
python3 -c "import secrets; print(secrets.token_hex(32))"
# Using pwgen
pwgen -s 64 1
Example output:
a7f3d9e8c2b4a1f6e9d8c3b5a2f7e0d9c4b8a3f1e6d9c5b2a8f4e1d7c6b9a5f2
Configure Services¶
Queue Manager:
export METALCORE_API_KEY="a7f3d9e8c2b4a1f6e9d8c3b5a2f7e0d9c4b8a3f1e6d9c5b2a8f4e1d7c6b9a5f2"
./queue-manager
Central Cache:
export METALCORE_API_KEY="a7f3d9e8c2b4a1f6e9d8c3b5a2f7e0d9c4b8a3f1e6d9c5b2a8f4e1d7c6b9a5f2"
./central-cache
Node Cache:
export METALCORE_API_KEY="a7f3d9e8c2b4a1f6e9d8c3b5a2f7e0d9c4b8a3f1e6d9c5b2a8f4e1d7c6b9a5f2"
./node-cache
Client/Worker Configuration¶
Set the same API key for clients and workers:
export METALCORE_API_KEY="a7f3d9e8c2b4a1f6e9d8c3b5a2f7e0d9c4b8a3f1e6d9c5b2a8f4e1d7c6b9a5f2"
Authentication in Code¶
Go Client¶
import (
"context"
"os"
"google.golang.org/grpc/metadata"
)
func main() {
// Get API key from environment
apiKey := os.Getenv("METALCORE_API_KEY")
if apiKey == "" {
log.Fatal("METALCORE_API_KEY not set")
}
// Create metadata with API key
md := metadata.New(map[string]string{
"api_key": apiKey,
})
// Add to context
ctx := metadata.NewOutgoingContext(context.Background(), md)
// Use context for all gRPC calls
session, err := queueClient.CreateSession(ctx, &pb.Session{...})
}
Using Connection Helpers¶
The connection helpers automatically handle API key authentication:
import "github.com/limelabs/metalcore-neo/pkg/grpccon"
// API key read from METALCORE_API_KEY environment variable
queueConn, queueClient, err := grpccon.ConnectToQueue(
"queue-manager:1337",
"/certs/ca.crt",
true)
The helper internally does:
func ConnectToQueue(addr, caCert string, useTLS bool) (*grpc.ClientConn, pb.QueueClient, error) {
// Read API key
apiKey := os.Getenv("METALCORE_API_KEY")
// Create interceptor that adds API key to all requests
authInterceptor := func(ctx context.Context, ...) {
ctx = metadata.AppendToOutgoingContext(ctx, "api_key", apiKey)
return invoker(ctx, method, req, reply, cc, opts...)
}
// Connect with interceptor
conn, err := grpc.Dial(addr,
grpc.WithUnaryInterceptor(authInterceptor),
grpc.WithStreamInterceptor(authStreamInterceptor),
...)
client := pb.NewQueueClient(conn)
return conn, client, nil
}
Security Best Practices¶
Key Management¶
Development:
# Local .env file (don't commit!)
echo "METALCORE_API_KEY=dev-key-123" > .env
source .env
Production:
# Kubernetes Secret
kubectl create secret generic alpcrun-secrets \
--from-literal=api-key='a7f3d9e8c2b4a1f6e9d8c3b5a2f7e0d9c4b8a3f1e6d9c5b2a8f4e1d7c6b9a5f2'
# Reference in deployment
env:
- name: METALCORE_API_KEY
valueFrom:
secretKeyRef:
name: alpcrun-secrets
key: api-key
Key Rotation: 1. Generate new API key 2. Update services with new key (rolling update) 3. Update clients/workers with new key 4. Invalidate old key
Key Storage¶
DO NOT: - Commit keys to version control - Log keys in plain text - Share keys via email/chat - Hardcode keys in source code
DO: - Use environment variables - Use secret management services (Vault, AWS Secrets Manager) - Rotate keys regularly - Use different keys per environment
TLS Configuration¶
Certificate Setup¶
Generate certificates:
# CA certificate
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 365 -key ca.key -out ca.crt \
-subj "/CN=ALPCRUN.CH CA"
# Server certificate
openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr \
-subj "/CN=queue-manager.alpcrun.svc.cluster.local"
# Sign with CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 365 \
-extfile <(printf "subjectAltName=DNS:queue-manager.alpcrun.svc.cluster.local,DNS:queue-manager")
Server Configuration¶
./queue-manager \
--tls \
--tlsCert=/certs/server.crt \
--tlsKey=/certs/server.key
Client Configuration¶
export METALCORE_CA_CERT=/certs/ca.crt
# Connection helper uses CA cert automatically
queueConn, queueClient, err := grpccon.ConnectToQueue(
"queue-manager:1337",
os.Getenv("METALCORE_CA_CERT"),
true) // TLS enabled
Mutual TLS (mTLS)¶
For enhanced security, use mutual TLS where clients also present certificates:
Generate Client Certificate¶
# Client certificate
openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr \
-subj "/CN=client"
# Sign with CA
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out client.crt -days 365
Server Configuration¶
./queue-manager \
--tls \
--tlsCert=/certs/server.crt \
--tlsKey=/certs/server.key \
--tlsClientAuth=require \
--tlsClientCA=/certs/ca.crt
Client Configuration¶
import (
"crypto/tls"
"crypto/x509"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
// Load certificates
cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
caCert, err := ioutil.ReadFile("ca.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Create TLS config
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}
// Connect with mTLS
creds := credentials.NewTLS(tlsConfig)
conn, err := grpc.Dial("queue-manager:1337",
grpc.WithTransportCredentials(creds))
Error Handling¶
Authentication Failures¶
Unauthenticated (missing API key):
code: UNAUTHENTICATED
message: "api_key not provided in metadata"
Permission Denied (invalid API key):
code: PERMISSION_DENIED
message: "invalid api_key"
Handle in Code¶
session, err := queueClient.CreateSession(ctx, &pb.Session{...})
if err != nil {
if status.Code(err) == codes.Unauthenticated {
log.Fatal("Authentication failed: check METALCORE_API_KEY")
}
if status.Code(err) == codes.PermissionDenied {
log.Fatal("Invalid API key")
}
}
Kubernetes Integration¶
Using Secrets¶
apiVersion: v1
kind: Secret
metadata:
name: alpcrun-secrets
type: Opaque
stringData:
api-key: "a7f3d9e8c2b4a1f6e9d8c3b5a2f7e0d9c4b8a3f1e6d9c5b2a8f4e1d7c6b9a5f2"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: queue-manager
spec:
template:
spec:
containers:
- name: queue-manager
image: alpcrun/queue-manager:latest
env:
- name: METALCORE_API_KEY
valueFrom:
secretKeyRef:
name: alpcrun-secrets
key: api-key
Using External Secrets¶
With External Secrets Operator:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: alpcrun-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: alpcrun-secrets
data:
- secretKey: api-key
remoteRef:
key: alpcrun/api-key
Monitoring¶
Failed Authentication Attempts¶
Monitor metrics for authentication failures:
rate(grpc_server_handled_total{grpc_code="Unauthenticated"}[5m])
rate(grpc_server_handled_total{grpc_code="PermissionDenied"}[5m])
Alert on Failures¶
# Prometheus alert
- alert: HighAuthFailureRate
expr: rate(grpc_server_handled_total{grpc_code="Unauthenticated"}[5m]) > 10
annotations:
summary: "High authentication failure rate"
Troubleshooting¶
Common Issues¶
"api_key not provided":
- Ensure METALCORE_API_KEY is set
- Check metadata is included in context
"invalid api_key": - Verify key matches server configuration - Check for whitespace/newlines in key - Ensure key hasn't been rotated
TLS errors:
- Verify certificate validity (openssl x509 -in cert.crt -text)
- Check certificate hostname matches service address
- Ensure CA certificate is correct
Debug Logging¶
Enable debug logging to see authentication flow:
# Server
./queue-manager --loglevel debug
# Client
export GRPC_GO_LOG_VERBOSITY_LEVEL=99
export GRPC_GO_LOG_SEVERITY_LEVEL=info
Next Steps¶
- Configuration Reference: Service configuration
- API Reference: Complete API documentation
- Getting Started: Setup guide