# Kubernetes (External Secrets Operator)

There are two ways to get seekrit secrets into a Kubernetes workload:

1. **Inject at runtime** — run your process through [`seekrit run`](/docs/guides/run),
   which resolves and decrypts into the process environment. Nothing is written
   to the cluster. This is the [CI/CD guide's Kubernetes pattern](/docs/guides/ci-cd#kubernetes).
2. **Sync into a Kubernetes `Secret`** — declaratively, with the
   [External Secrets Operator](https://external-secrets.io) (ESO), so your pods
   consume a normal `Secret` via `envFrom`/`valueFrom` and 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)
```

> **Note:** 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:

```bash
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](/docs/guides/service-tokens)):

```bash
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

```bash
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`.

> **Tip:** 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:

```yaml
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:

```yaml
envFrom:
  - secretRef:
      name: storefront-secrets
```

Check status:

```bash
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, …}}`.

> **Note:** 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 `ExternalSecret`s.

## 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

> **Warning:** 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](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/)**
  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 `NetworkPolicy` to allow ingress
  only from your ESO pods:
  ```bash
  --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-eso` chart README](https://github.com/mileszim/seekrit/tree/main/charts/seekrit-eso).
- Minting and scoping tokens: [Service tokens](/docs/guides/service-tokens).
- Runtime injection instead of syncing: [`seekrit run`](/docs/guides/run) and
  [CI/CD & containers](/docs/guides/ci-cd#kubernetes).
