Kubernetes (External Secrets Operator)
There are two ways to get seekrit secrets into a Kubernetes workload:
- Inject at runtime — run your process through
seekrit run, which resolves and decrypts into the process environment. Nothing is written to the cluster. This is the CI/CD guide's Kubernetes pattern. - Sync into a Kubernetes
Secret— declaratively, with the External Secrets Operator (ESO), so your pods consume a normalSecretviaenvFrom/valueFromand nothing in them knows about seekrit. That's this guide.
How it works
seekrit is zero-knowledge: GET /v1/resolve returns only ciphertext plus a data
key wrapped to your token's public key, and decryption happens client-side. ESO,
which expects to pull plaintext from a provider, therefore can't read from the
seekrit API directly.
The seekrit-eso Helm chart bridges that gap. It deploys a small in-cluster
sidecar (seekrit-sdk-server) that holds a service token, resolves and decrypts
locally, caches the result, and serves it over a tiny authed HTTP API — then
generates a webhook SecretStore that points ESO at the sidecar. You run stock,
unmodified ESO and write only ExternalSecret resources.
ExternalSecret ─▶ ESO (stock) ──webhook──▶ seekrit-sdk-server ──/v1/resolve──▶ seekrit API
(you write) (unmodified) (the chart) (ciphertext only)
The seekrit API still never sees plaintext — decryption stays inside your
cluster, exactly as it does for the CLI and seekrit-run. The sidecar is
simply "another principal" holding a grant.
Prerequisites
1. External Secrets Operator, installed once per cluster:
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
-n external-secrets --create-namespace
2. A seekrit service token bound to the app environment you want to sync. Mint one with the CLI (see Service tokens):
seekrit token create --name eso --app storefront --env production
# prints: skt_XXXXXXXX_… (save it now)
The token is bound to that one environment and auto-granted its keys (plus any composed group slices), so it can read exactly that environment and nothing else.
Install the chart
helm install seekrit-eso oci://registry-1.docker.io/seekritdev/seekrit-eso \
--version 0.1.0 \
-n seekrit-system --create-namespace \
--set seekrit.token=skt_XXXXXXXX_…
That deploys the sidecar and creates a SecretStore named seekrit. The chart
also generates the sidecar's API key automatically and preserves it across
helm upgrade.
Managing secrets with GitOps? Don't put the token in --set. Create a Secret
yourself (sealed-secrets, SOPS, etc.) and point the chart at it with
--set seekrit.existingSecret=my-token-secret.
Write an ExternalSecret
Now the payoff — the only thing your team writes per app:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: storefront
namespace: default
spec:
refreshInterval: 1m
secretStoreRef:
name: seekrit
kind: SecretStore
target:
name: storefront-secrets # the Kubernetes Secret ESO creates/manages
data:
- secretKey: DATABASE_URL # key in the resulting Secret
remoteRef:
key: DATABASE_URL # secret name in your seekrit environment
- secretKey: STRIPE_API_KEY
remoteRef:
key: STRIPE_API_KEY
ESO creates a storefront-secrets Secret and keeps it in sync. Consume it like
any other Secret:
envFrom:
- secretRef:
name: storefront-secrets
Check status:
kubectl get externalsecret storefront
kubectl -n seekrit-system logs deploy/seekrit-eso
Pulling a whole environment
ESO's webhook provider fetches one key at a time, so list the keys you want in
data[] (the sidecar caches the decrypted environment, so each fetch is cheap).
To pull everything without enumerating keys, template against the sidecar's
whole-environment map with dataFrom + target.template — the sidecar exposes
GET /v1/secrets returning {"data": {NAME: value, …}}.
A native seekrit ESO provider (first-class provider: {seekrit:} with built-in
dataFrom support) is planned. It reuses this same sidecar, so when it lands
the chart swaps its generated SecretStore with no change to your
ExternalSecrets.
Rotation & refresh timing
Two intervals stack. The sidecar re-resolves from seekrit every
refreshInterval (chart value, default 60s), and ESO re-reads the sidecar
every spec.refreshInterval on the ExternalSecret. So a rotated secret reaches
your pods within roughly the sum of the two. Lower either for faster propagation.
Security posture
ESO writes decrypted values into Kubernetes Secret objects (base64 in etcd).
That is inherent to how ESO works — seekrit's control plane stays
zero-knowledge, but the cluster becomes a trusted decryption endpoint, the same
as running seekrit-run on a CI box.
Harden accordingly:
- Enable etcd encryption-at-rest so the synced Secrets aren't stored in plaintext.
- Restrict who can reach the sidecar. It decrypts everything the token can
read, gated by an API key. Turn on the chart's
NetworkPolicyto allow ingress only from your ESO pods:--set networkPolicy.enabled=true \ --set 'networkPolicy.from[0].namespaceSelector.matchLabels.kubernetes\.io/metadata\.name=external-secrets' - One token, one environment. The token's scope is the sidecar's blast
radius. To sync another app or environment, install a second release with its
own token and
secretStore.name— don't broaden one token.
Reference
- Chart values and options: the
seekrit-esochart README. - Minting and scoping tokens: Service tokens.
- Runtime injection instead of syncing:
seekrit runand CI/CD & containers.