Skip to content

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