Skip to content

Security Hardening

This page covers the three security areas that must be configured before a production deployment: encryption keys for data at rest, TLS for data in transit, and authentication for access control.

Encryption Keys

The controller uses AES-GCM 256-bit keys to encrypt sensitive data at rest. Providing at least one encryption key is required when developmentMode is set to false.

Generate a 256-bit AES key:

openssl rand -out default-aesgcm256-v1.bin 32

Create the Kubernetes secret:

kubectl create secret generic gateway-encryption-keys \
  --namespace <your-namespace> \
  --from-file=default-aesgcm256-v1.bin=./default-aesgcm256-v1.bin

Clean up the local key file:

rm ./default-aesgcm256-v1.bin

Warning

Do not commit the key file to source control. Remove it immediately after creating the Kubernetes secret.

Reference the secret in values:

gateway:
  controller:
    encryptionKeys:
      enabled: true
      secretName: gateway-encryption-keys
      mountPath: /app/data/aesgcm-keys
gateway:
  config:
    controller:
      encryption:
        providers:
          - type: aesgcm
            keys:
              - version: aesgcm256-v1
                file: /app/data/aesgcm-keys/default-aesgcm256-v1.bin

The version field must match the filename stem of the secret key (default-aesgcm256-v1.bin → version aesgcm256-v1).

Key rotation:

  1. Generate a new key with openssl rand.
  2. Add it to the Kubernetes secret with an incremented version name.
  3. Update the encryption.providers list in values to include the new key entry.
  4. Run helm upgrade to redeploy — the controller picks up the new key on startup.

Note

Keep old key versions in the secret until all data encrypted with the previous key has been re-encrypted or is no longer needed.


TLS Configuration

TLS must be configured before exposing the gateway externally. Choose one of the options below based on how certificates are managed in your environment.

cert-manager automates TLS certificate provisioning and renewal within the cluster. Install it if you do not already manage certificates externally.

Install cert-manager:

helm repo add jetstack https://charts.jetstack.io --force-update
helm repo update

helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true

Verify all cert-manager pods are running:

kubectl get pods -n cert-manager

Configure the chart to use cert-manager:

gateway:
  controller:
    tls:
      enabled: true
      certificateProvider: cert-manager
      certManager:
        create: true
        createIssuer: false          # Use your own ClusterIssuer
        issuerRef:
          name: letsencrypt-prod    # Your ClusterIssuer name
          kind: ClusterIssuer
        commonName: gateway.example.com
        dnsNames:
          - gateway.example.com
        duration: 2160h             # 90 days
        renewBefore: 720h           # Renew 30 days before expiry

Use this option if certificates are managed externally, for example, via a corporate PKI or HashiCorp Vault.

Create the TLS secret:

kubectl create secret tls gateway-tls \
  --namespace <your-namespace> \
  --cert=./gateway.crt \
  --key=./gateway.key

Configure the chart to reference the secret:

gateway:
  controller:
    tls:
      enabled: true
      certificateProvider: secret
      secret:
        name: gateway-tls
        certKey: tls.crt
        keyKey: tls.key

Upstream Custom CA Certificates

If backend services use certificates signed by a private CA, mount the CA bundle into the controller so it can verify upstream TLS connections.

kubectl create configmap gateway-upstream-certs \
  --namespace <your-namespace> \
  --from-file=private-ca.crt=./my-ca.crt
gateway:
  controller:
    upstreamCerts:
      enabled: true
      configMapName: gateway-upstream-certs

Authentication

Warning

The default credentials (admin/admin) must be replaced before deploying to any non-development environment. The controller logs a warning at startup if default or no authentication is configured.

Choose an authentication strategy based on your organizational requirements.

This is the safest option. No credentials to manage in the cluster. Authentication is delegated entirely to your identity provider.

gateway:
  config:
    controller:
      auth:
        basic:
          enabled: false
        idp:
          enabled: true
          jwks_url: "https://idp.example.com/.well-known/jwks.json"
          issuer: "https://idp.example.com"
          roles_claim: "scope"
          role_mapping:
            admin: ["gateway:admin"]
            developer: ["gateway:developer"]
            consumer: ["gateway:consumer"]

Note

Role mapping values must correspond to claims present in the JWT issued by your IDP. See the Gateway Controller OpenAPI reference for the full list of supported roles and their permissions.

If basic auth is required, never store plain-text passwords. The controller supports bcrypt hashes, which are safe to include in Helm values (the hash is not reversible). Store the plain password separately in a Kubernetes secret for rotation reference only.

Generate a bcrypt hash:

Requires apache2-utils (Debian/Ubuntu) or httpd-tools (RHEL/CentOS):

htpasswd -nbB admin 'your-secure-password' | cut -d: -f2
# Output: $2y$10$...

On macOS without htpasswd:

docker run --rm httpd:alpine htpasswd -nbB admin 'your-secure-password' | cut -d: -f2

Store the plain password in a Kubernetes secret:

Keep the plain password in a secret for rotation reference. It must never appear in Helm values or ConfigMaps:

kubectl create secret generic gateway-admin-credentials \
  --namespace <your-namespace> \
  --from-literal=username=admin \
  --from-literal=password='your-secure-password'

Configure the chart with the bcrypt hash:

Only the hash goes into the Helm values. This is what ends up in the ConfigMap:

gateway:
  config:
    controller:
      auth:
        basic:
          enabled: true
          users:
            - username: "admin"
              password: "$2y$10$..."   # bcrypt hash — safe to store in ConfigMap
              password_hashed: true
              roles: ["admin"]

Note

Basic auth users are an array of structs and cannot be overridden via environment variables. The hash must be supplied through Helm values. Rotate credentials by generating a new hash, updating the values, and running helm upgrade.


← Production Deployment Overview  |  Database Configuration →