Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Kubernetes secrets

SecretAs a fileAs an environment variable
whole configurationconfig.toml mounted in /config-
users, rolesusers_file, roles_file-
SSH host keyshost_keys-
admin TLS pair[admin.tls] cert_file, key_fileCRAFT_FILE_GATE_ADMIN_TLS_CERT, CRAFT_FILE_GATE_ADMIN_TLS_KEY
JWT secretCRAFT_FILE_GATE_JWT_SECRET_FILECRAFT_FILE_GATE_JWT_SECRET
admin token (fallback; forbidden by allow_static_token = false, which the chart sets)CRAFT_FILE_GATE_ADMIN_BEARER_TOKEN_FILECRAFT_FILE_GATE_ADMIN_BEARER_TOKEN
S3 keyscredentials = { type = "static", ... }AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY with type = "iam_role"
admin console session key[admin.session] key_fileCRAFT_FILE_GATE_ADMIN_SESSION_SECRET_NAME: the shared Secret, filled by the pods (below)
  • A Secret mounted as a volume is updated by Kubernetes, which repoints the ..data link: configuration, users, roles and TLS pair are reloaded at runtime. See Hot reload.
  • A Secret mounted with subPath is never updated: avoid subPath.
  • Mount Secrets and ConfigMaps readOnly: true, as the chart does, never on a writable emptyDir or hostPath (Trust files and TLS).
  • A Secret passed through envFrom (extraEnvFrom) is read only at startup: run kubectl rollout restart for a new value.
  • The server does not read Secrets through the Kubernetes API: mount them. The only exception is the pods’ shared Secret (below).

A file rather than a variable

Mount the Secret as a file through the chart values, and name it with the _FILE variable (the same for the admin token, with config.allowStaticToken: true):

extraVolumes:
  - name: jwt
    secret: { secretName: sftp-jwt, defaultMode: 0440 }
extraVolumeMounts:
  - { name: jwt, mountPath: /secrets/jwt, readOnly: true }
extraEnv:
  - { name: CRAFT_FILE_GATE_JWT_SECRET_FILE, value: /secrets/jwt/jwt-secret }

The file is read once, at startup; /proc/<pid>/environ shows only a path. See Environment variables. users.toml is mounted the same way, named by users_file: see Kubernetes.

The pods’ shared Secret

The pods of a release share the key that signs admin console sessions, so that a pod accepts the token another pod issued. The chart creates an empty Secret and gives the pods get and update on that name only. The first pod that does not find the admin-session.key entry generates it and writes it; the others read it. Each entry has its own life: the certificate of the channel between pods lives there too (tls.crt, tls.key).

Chart valueEffect
clusterSecret.enabledtrue by default, with [admin] or the channel between pods: the Secret, the Role (get, update on that name), the service account token; with [admin], CRAFT_FILE_GATE_ADMIN_SESSION_SECRET_NAME
clusterSecret.name<release>-cluster by default
adminRevocations.backendempty by default: configmap with the shared Secret, memory otherwise; configmap creates the access ConfigMap, where the revocations live, its Role (get, update, patch on that name), and sets CRAFT_FILE_GATE_ADMIN_SESSION_BACKEND, CRAFT_FILE_GATE_ADMIN_SESSION_REVOCATION_CONFIGMAP_NAME
adminGrants.backendempty by default: configmap with [admin] and the shared Secret or several replicas, memory otherwise; temporary accesses in the same ConfigMap, CRAFT_FILE_GATE_ADMIN_GRANTS_BACKEND, CRAFT_FILE_GATE_ADMIN_GRANTS_GRANT_CONFIGMAP_NAME; memory, or file without persist_file under [admin.grants], with several replicas makes rendering fail
access.configmapNamethe access ConfigMap, <release>-access by default

With replicaCount above 1, clusterSecret.enabled: false requires a key_file under [admin.session] in config.inline, and a shared key requires shared revocations (adminRevocations.backend: configmap, or persist_file under [admin.session]).

To change the key: remove the entry (kubectl patch secret ... --type=json -p '[{"op":"remove","path":"/data/admin-session.key"}]') then restart the pods; open sessions are lost.

SSH host key

Create the key once, keep it in a Secret, mount it. Do not let the server generate it (generate_host_key) in a pod: each pod, or each restart without a volume, would have its own key, and clients would see “host key changed”.

ssh-keygen -t ed25519 -f host_ed25519 -N ""
ssh-keygen -l -f host_ed25519.pub          # the fingerprint to give to clients
kubectl create secret generic sftp-host-keys --from-file=host_ed25519

hostKeys.existingSecret: sftp-host-keys mounts it in /keys (The chart), and the configuration points to it: host_keys = ["/keys/host_ed25519"]. The fingerprint at startup (loaded host key, field fingerprint) and in GET /admin/config is the one from ssh-keygen -l.