For the complete documentation index, see llms.txt. This page is also available as Markdown.

Kubernetes Secrets Injector

Automatically injects secrets from Keeper Secrets Manager into your Kubernetes pods at runtime

Overview

The Keeper Injector automatically injects secrets from Keeper Secrets Manager (KSM) into your Kubernetes pods at runtime. It is a mutating admission webhook that adds a lightweight sidecar to annotated pods; the sidecar authenticates to Keeper, fetches the requested secrets, and makes them available to your application - by default on a memory-backed (tmpfs) volume that never touches etcd or disk.

The injector is configured entirely with pod annotations - there are no Custom Resources to manage.

The injector is distributed as an official Helm chart, published to ArtifactHub. It connects only to Keeper Secrets Manager; if you need a multi-backend operator, see the External Secrets Operator.

How it works

When a pod carries the keeper.security/inject: "true" annotation, the webhook mutates it to add:

  • an init container that fetches the secrets once before your app starts, and

  • a sidecar container that refreshes them on an interval (unless you opt into init-only).

The sidecar can deliver secrets three ways - you choose per pod with annotations:

Mode

How the app consumes it

Secrets in etcd?

Files (default)

Read files under /keeper/secrets/ from a memory-backed tmpfs volume

No

Environment variables

Standard env vars in your container

Yes (in the pod spec)

Kubernetes Secret

A native Secret object via secretKeyRef or a volume

Yes

File injection is the most secure default (memory-only, per-pod). Use environment variables or a Kubernetes Secret when an application or controller specifically requires them.

Keeper Injector vs. External Secrets Operator

We offer two methods of injecting secrets from Keeper Secrets Manager into Kubernetes - using this Keeper Injector and using the External Secrets Operator. The implementation and use case is different in the following ways:

Keeper Injector

External Secrets Operator

Creates Kubernetes Secret objects

Optional (default: no)

Yes (core model)

Where secrets live

Pod tmpfs (memory) by default

etcd

Configuration

Pod annotations

Custom Resources (CRDs)

Runtime rotation

Yes (sidecar)

On sync interval

Per-pod isolation

Yes

Shared Secret objects

Backends

Keeper only

35+ providers

Choose the Keeper Injector when you want secrets to stay out of etcd (delivered to pod memory), with per-pod isolation and in-place rotation without pod restarts, configured with a couple of pod annotations and no custom resources.

Choose the External Secrets Operator when it fits how your cluster already works - for example, you already run ESO as your standard secrets operator (often across multiple backends) and want Keeper to plug into that same CRD workflow; you want secrets reconciled into long-lived, shared Secret objects consumed by other controllers or GitOps tooling; or you need ESO-specific capabilities such as write-back (PushSecret). ESO ships a maintained first-class Keeper provider.

For the legacy init-container approach, see Kubernetes (alternative).

Prerequisites

  • A Kubernetes cluster, version 1.25 or later

  • A Keeper Secrets Manager Application and a device configuration (see Quick Start Guide)

  • kubectl access to the target cluster

Install the Keeper Injector

Install the webhook and sidecar controller into a dedicated namespace.

TLS certificates for the webhook are auto-generated by default (tls.autoGenerate=true); cert-manager is optionally supported via tls.certManager.enabled=true.

Create the KSM authentication secret

The sidecar authenticates to Keeper with a Secrets Manager device configuration, stored in a Kubernetes Secret (key config) in the namespace where your workloads run.

The simplest way to get the configuration is from the Keeper Web Vault: Secrets Manager → your Application → add a device → Configuration File method → Base64 type, and copy the Base64 string. Then:

You can also generate the configuration with the Secrets Manager CLI or Commander and load a JSON file instead (--from-file=config=ksm-config.json). Either form works - the Secret's key must be config.

Inject secrets

Files (default)

Add the injection annotations to the pod template. The injector adds the sidecar and a shared tmpfs volume, and writes each requested record as a file under /keeper/secrets/.

The record is written to /keeper/secrets/database-credentials.json, where your application reads it:

Injected files are written read-only (0440) and owned by the sidecar's user. Containers that run as root or as the nobody user read them with no extra configuration; if your application runs as a different fixed non-root UID, set a matching pod-level securityContext.fsGroup so the group-readable files are accessible.

Environment variables

Set keeper.security/inject-env-vars: "true" to inject the record's fields as environment variables instead of (or in addition to) files. Field names become uppercased env keys; add a prefix with keeper.security/env-prefix.

The login and password fields become APP_LOGIN and APP_PASSWORD.

Kubernetes Secret

Set keeper.security/inject-as-k8s-secret: "true" to have the injector create a native Kubernetes Secret from the record, which your workloads consume the standard way.

Each record field becomes a key in the Secret. Use keeper.security/k8s-secret-mode to control conflicts (overwrite, merge, skip-if-exists, fail) and keeper.security/k8s-secret-type to set the type (for example kubernetes.io/tls).

The created Secret is written to the pod's own namespace and is not automatically deleted when the pod is removed - delete it explicitly or remove the namespace when you no longer need it.

Output formats and templates

For anything beyond the default JSON file, use the keeper.security/config annotation - a small YAML document that describes one or more secrets, each with its own output path, format, or Go template.

Supported format values: json (default), env, yaml, properties, ini, and raw. Templates use Go text/template with the Sprig function library (string manipulation, base64/hex, hashing, conditionals, and more), with the record's fields available as top-level variables (.login, .password, …).

Multiple secrets, folders, and Keeper Notation

Inject several records, a whole folder, or a single field with Keeper Notation:

Keeper Notation (keeper://…) is parsed by the per-secret keeper.security/secret-<name> annotation and inside the keeper.security/config block — not by the plain keeper.security/secret annotation, which always treats its value as a record title. A folder can be referenced by path (keeper.security/folder) or by UID (keeper.security/folder-uid).

File attachments

Download a file attached to a Keeper record with keeper.security/file-<name>, in the form record:filename:/output/path:

Rotation and refresh

In sidecar mode the agent re-fetches secrets on an interval and rewrites the files in place - no pod restart is required, and applications that re-read the files pick up the new values automatically.

Set keeper.security/init-only: "true" to fetch once at startup with no refresh sidecar - appropriate for environment-variable injection, or when you don't need rotation.

Corporate proxy / custom CA

If outbound traffic is intercepted by an SSL-inspecting proxy (Zscaler, Palo Alto, Cisco Umbrella, …), provide the proxy's CA certificate so the sidecar trusts it. Store the CA in a Secret or ConfigMap and reference it:

Cloud authentication (optional)

Instead of a Kubernetes Secret, the sidecar can fetch the KSM configuration from a cloud secret store at runtime. Set keeper.security/auth-method and the provider's locator; omit keeper.security/ksm-config.

Provider
Annotations

AWS Secrets Manager

keeper.security/auth-method: "aws-secrets-manager", keeper.security/aws-secret-id: "<id/arn>", keeper.security/aws-region: "<region>"

GCP Secret Manager

keeper.security/auth-method: "gcp-secret-manager", keeper.security/gcp-secret-id: "<resource>"

Azure Key Vault

keeper.security/auth-method: "azure-key-vault", keeper.security/azure-vault-name: "<vault>", keeper.security/azure-secret-name: "<name>"

Cloud authentication pairs with file-based injection. It uses the cloud provider's own workload identity (IRSA, Workload Identity, Azure Workload Identity) to read the stored KSM config.

Annotation reference

Annotation
Description
Example

keeper.security/inject

Enable injection for this pod

"true"

keeper.security/ksm-config

Kubernetes Secret holding the KSM config (key config)

"keeper-credentials"

keeper.security/secret

A single record to inject, by title

"my-secret"

keeper.security/secrets

Multiple records, comma-separated (by title)

"db-creds, api-keys"

keeper.security/secret-<name>

One record (by title or keeper:// notation) to a chosen path

keeper.security/secret-db: "keeper://UID/field/password:/keeper/secrets/db.txt"

keeper.security/config

YAML block for formats, templates, and per-secret paths

(see above)

keeper.security/folder / keeper.security/folder-uid

Fetch every record in a folder (by path or UID)

"Production/Databases"

keeper.security/folder-path

Output directory for folder records

"/keeper/secrets/db"

keeper.security/file-<name>

Download a file attachment (record:filename:/path)

"tls:server.crt:/keeper/secrets/server.crt"

keeper.security/inject-env-vars

Inject fields as environment variables

"true"

keeper.security/env-prefix

Prefix for injected env var names

"APP_"

keeper.security/inject-as-k8s-secret

Create a native Kubernetes Secret

"true"

keeper.security/k8s-secret-name

Name of the created Secret

"app-secrets"

keeper.security/k8s-secret-mode

Conflict behavior: overwrite, merge, skip-if-exists, fail

"merge"

keeper.security/k8s-secret-type

Secret type

"kubernetes.io/tls"

keeper.security/refresh-interval

Sidecar re-fetch interval

"5m"

keeper.security/init-only

Fetch once at startup, no refresh sidecar

"true"

keeper.security/fail-on-error

Fail pod startup if a secret can't be fetched (default "true")

"false"

keeper.security/ca-cert-secret / keeper.security/ca-cert-configmap

Custom CA for an SSL-inspecting proxy

"corporate-ca"

keeper.security/auth-method

Cloud auth provider (omit for K8s-Secret auth)

"aws-secrets-manager"

Helm configuration

Parameter
Description
Default

replicaCount

Number of webhook replicas (HA)

2

image.repository / image.tag

Webhook image / tag

keeper/injector-webhook / chart appVersion

sidecar.repository / sidecar.tag

Sidecar image / tag

keeper/injector-sidecar / chart appVersion

metrics.enabled

Expose Prometheus metrics

true

tls.autoGenerate

Auto-generate the webhook's TLS certificate

true

tls.certManager.enabled

Use cert-manager to issue the webhook certificate

false

rbac.create

Create the controller's RBAC role and binding

true

See the chart's values.yaml for the full set of options.

Verifying the setup

Confirm the injector controller is running:

Deploy an annotated workload, then confirm the sidecar was injected and the secret is present:

Troubleshooting

Symptom
Check

No sidecar injected

The namespace isn't labeled keeper.security/inject=disabled; the controller pods in keeper-security are running; the pod has keeper.security/inject: "true".

Pod won't start / secret missing

Inspect the init container logs: kubectl logs <pod> -c keeper-secrets-init. Confirm the record title matches exactly and the auth Secret has a config key.

Refresh issues at runtime

Inspect the sidecar logs: kubectl logs <pod> -c keeper-secrets-sidecar.

App can't read the file (permission denied)

The app container runs as a UID that isn't root/nobody; set a matching pod securityContext.fsGroup.

Want a soft failure

Set keeper.security/fail-on-error: "false" so the pod starts even if a secret can't be fetched.

Uninstall

Removing the controller stops new injections; existing pods keep their injected sidecars until they are recreated. Files on tmpfs disappear with their pods, but any Secret objects created by the K8s-Secret mode persist - delete them explicitly.

Example Configuration

A detailed example of using Kubernetes Secrets Injector can be found on the next page.

Examples

Last updated

Was this helpful?