skip to content

How does Spring Cloud Kubernetes expose Kubernetes Secrets to the Environment, and what security defaults matter?

level: middleimportance: should knowfreq 45%

answer

  1. Secret = base64 (encoding, not encryption)
  2. SecretsPropertySource decodes into Environment
  3. enableApi=false by default -> prefer mounted paths
  4. API path needs RBAC get/watch on secrets
  5. env-injected secrets don't hot-update; volume does

basics

~10 s

It contributes a Secrets PropertySource so Secret keys become Spring properties. Secret values are base64-decoded automatically. Reading Secrets via the Kubernetes API is opt-in (enableApi); by default it reads Secrets from mounted file paths.

solid answer

~40 s

A Kubernetes `Secret` is like a ConfigMap but for sensitive data, stored base64-encoded. Spring Cloud Kubernetes adds a `SecretsPropertySource` (`spring.cloud.kubernetes.secrets.*`) that decodes values and merges them into the `Environment`, so a DB password lands in `spring.datasource.password` like any property. Security nuance: consuming Secrets over the Kubernetes API is disabled by default (`enableApi=false`) because it requires broad RBAC and pulls secrets over the wire; the recommended path is to mount the Secret as a volume and point `spring.cloud.kubernetes.secrets.paths` at it, or use env vars. Names default to `spring.application.name`; `sources`, `namespace`, and label selectors let you target specific Secrets. Reload works for Secrets too when enabled.

code

yaml · 19 lines
yaml
spring:
  cloud:
    kubernetes:
      secrets:
        enabled: true
        enableApi: false          # default; read from mounts, not the API
        paths:
          - /etc/secrets           # where the Secret volume is mounted
        # sources:
        #   - name: db-credentials
        #     namespace: prod
---
apiVersion: v1
kind: Secret
metadata:
  name: orders-service
type: Opaque
data:
  spring.datasource.password: c3VwZXItc2VjcmV0    # base64('super-secret')

go deeper

for a junior

Know Secrets become Spring properties and values are base64, not encrypted.

for a middle

Explain enableApi default-off, mounted paths vs API mode, and RBAC implications.

for a senior

Discuss precedence vs ConfigMaps, label selectors, reload behavior, and env-vs-volume update semantics.

for a principal

Weigh Kubernetes Secrets vs external secret stores, /env actuator leakage, and RBAC blast radius in the threat model.

**What a Secret is.** A Kubernetes `Secret` mirrors a ConfigMap but is intended for sensitive values (passwords, tokens, keys). Its `data:` values are base64-encoded (encoding, NOT encryption — etcd encryption-at-rest is a separate cluster setting). **The Secrets property source.** Spring Cloud Kubernetes provides `SecretsPropertySource` / `KubernetesClientSecretsPropertySource`, configured under `spring.cloud.kubernetes.secrets.*`. It base64-decodes each value and contributes it to the `Environment`. So a Secret key `spring.datasource.password` becomes usable by `@Value("${spring.datasource.password}")` with no manual decoding. **Two consumption modes — the key security default:** 1. **Mounted volume / env (default, recommended).** Kubernetes projects the Secret into the pod filesystem (or env vars). Spring Cloud Kubernetes reads from `spring.cloud.kubernetes.secrets.paths` (a list of mount directories). No API call, no extra RBAC, secret never traverses the app's API connection. 2. **Kubernetes API (`spring.cloud.kubernetes.secrets.enableApi=true`, off by default).** The app calls the API to fetch the Secret. Convenient, but requires `get`/`list`/`watch` RBAC on `secrets` — a broad, high-value permission — so it is intentionally opt-in. **Targeting which Secret.** Like config: `name` (defaults to app name), `namespace`, `sources` (explicit list), and `labels` (a selector to pick Secrets by label). `enabled` defaults to `true`, but that only controls the property source; `enableApi` separately gates the API path. **Precedence.** Secret properties are contributed alongside ConfigMap properties. When the same key exists in both, ordering determines the winner; be deliberate — don't accidentally let a ConfigMap override a Secret. **Reload.** If `spring.cloud.kubernetes.reload.enabled=true` and Secrets monitoring is on, changing a Secret can fire a refresh just like a ConfigMap (event/watch mode needs `watch` RBAC on `secrets`; polling re-reads periodically). Note: a Secret mounted as a volume is eventually updated by the kubelet, but env-var-injected Secrets are NOT updated in a running container. **Gotchas.** - base64 is not security; protect via RBAC + etcd encryption + not logging values. - Enabling the API path widens RBAC — prefer mounted paths in production. - `@ConfigurationProperties` holding a password must be inside a `@RefreshScope`-managed bean (or be a `@ConfigurationProperties` bean, which is rebound) for reload to take effect. - Logging the whole `Environment` or a `/env` actuator endpoint can leak decoded secrets — sanitize. **When to use.** You keep secrets in Kubernetes and want them as first-class Spring properties. For stronger secret management many teams instead use an external store (Vault) — but that's a different integration.

  • Why is reading Secrets via the Kubernetes API disabled by default?
    Because it requires granting the pod's service account `get`/`list`/`watch` on `secrets` — a powerful permission — and pulls secret material over the API connection. Mounting the Secret as a volume avoids both, so it's the safer default.
  • Does Spring Cloud Kubernetes decrypt Secret values?
    It base64-decodes them (Kubernetes stores Secrets base64-encoded, not encrypted). Actual encryption at rest is a cluster-level etcd feature, independent of Spring.

saying these in an interview costs you the question

  • Claiming base64 encoding is encryption / provides confidentiality
  • Assuming API-mode secret reading is on by default
  • Expecting env-var-injected secrets to hot-reload in a running container

context