KeyEnvKeyEnv

Kubernetes Integration

Sync KeyEnv secrets to Kubernetes using External Secrets Operator (ESO).

Kubernetes Integration

Sync KeyEnv secrets to Kubernetes using the External Secrets Operator (ESO).

Overview

The External Secrets Operator is a Kubernetes operator that synchronizes secrets from external providers into Kubernetes Secrets. KeyEnv provides native ESO webhook endpoints, allowing you to sync your secrets directly into Kubernetes without deploying additional infrastructure.

How It Works

  1. ESO calls KeyEnv's webhook endpoints to fetch secret values
  2. KeyEnv authenticates the request using your service token
  3. ESO creates or updates Kubernetes Secrets with the fetched values
  4. Secrets are automatically refreshed based on your configured interval

Prerequisites

Before setting up the integration, ensure you have:

  • Kubernetes cluster with kubectl access
  • External Secrets Operator installed (installation guide)
  • KeyEnv service token with read access to your project

Creating a Service Token

  1. Navigate to your project in the KeyEnv dashboard
  2. Go to Settings → Service Tokens
  3. Create a new token with Read scope
  4. Copy the token (starts with env_)

Setup

Step 1: Create a Kubernetes Secret with Your KeyEnv Token

Store your KeyEnv service token as a Kubernetes Secret:

apiVersion: v1
kind: Secret
metadata:
  name: keyenv-token
  namespace: external-secrets
stringData:
  token: "env_your_service_token_here"

Apply with:

kubectl apply -f keyenv-token.yaml

Step 2: Create a ClusterSecretStore

Create a ClusterSecretStore that configures ESO to communicate with KeyEnv:

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: keyenv
spec:
  provider:
    webhook:
      url: "https://api.keyenv.dev/api/v1/eso/secret?project=<PROJECT_ID>&env={{ .remoteRef.property }}&key={{ .remoteRef.key }}"
      result:
        jsonPath: "$.value"
      headers:
        Authorization: "Bearer {{ .auth.token }}"
      secrets:
        - name: auth
          secretRef:
            namespace: external-secrets
            name: keyenv-token
            key: token

Replace <PROJECT_ID> with your KeyEnv project ID or slug.

Apply with:

kubectl apply -f keyenv-secretstore.yaml

Step 3: Create an ExternalSecret

Create an ExternalSecret to sync specific secrets from KeyEnv:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-app-secrets
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: keyenv
    kind: ClusterSecretStore
  target:
    name: my-app-secrets
  data:
    - secretKey: DATABASE_URL
      remoteRef:
        key: DATABASE_URL
        property: production
    - secretKey: API_KEY
      remoteRef:
        key: API_KEY
        property: production

This creates a Kubernetes Secret named my-app-secrets containing:

  • DATABASE_URL - fetched from KeyEnv's production environment
  • API_KEY - fetched from KeyEnv's production environment

The property field specifies the KeyEnv environment, and key specifies the secret name.

Bulk Export with dataFrom

To sync all secrets from an environment at once, use the bulk export endpoint with dataFrom.

Create a Bulk ClusterSecretStore

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: keyenv-bulk
spec:
  provider:
    webhook:
      url: "https://api.keyenv.dev/api/v1/eso/secrets?project=<PROJECT_ID>&env={{ .remoteRef.key }}"
      result:
        jsonPath: "$.data"
      headers:
        Authorization: "Bearer {{ .auth.token }}"
      secrets:
        - name: auth
          secretRef:
            namespace: external-secrets
            name: keyenv-token
            key: token

Create an ExternalSecret with dataFrom

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: all-production-secrets
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: keyenv-bulk
    kind: ClusterSecretStore
  target:
    name: production-secrets
  dataFrom:
    - extract:
        key: production

This syncs all secrets from the production environment into a single Kubernetes Secret named production-secrets.

Environment Inheritance

KeyEnv supports environment inheritance, and this is fully supported by the ESO integration:

  • Single secret: If the requested secret is not found in the target environment, the inheritance chain is searched upward to parent environments.
  • Bulk export: Returns all secrets from the target environment and its parent chain, with child values taking precedence over parent values for duplicate keys.

If you've explicitly hidden an inherited secret in KeyEnv (overriding without a value), that secret will not be returned by the ESO endpoints for that environment.

API Reference

KeyEnv provides two ESO-compatible webhook endpoints:

GET /api/v1/eso/secret

Fetch a single secret value.

ParameterRequiredDescription
projectYesProject ID or slug
envYesEnvironment name or ID
keyYesSecret key name

Response:

{ "value": "secret-value-here" }

GET /api/v1/eso/secrets

Fetch all secrets as a key-value map (for ESO dataFrom).

ParameterRequiredDescription
projectYesProject ID or slug
envYesEnvironment name or ID

Response:

{
  "data": {
    "DATABASE_URL": "postgres://...",
    "API_KEY": "sk_live_..."
  }
}

Error Codes

StatusDescription
400Missing required parameters or circular inheritance detected
401Invalid or missing token
403Token lacks access to project
404Project, environment, or secret not found
500Internal server error

Troubleshooting

ExternalSecret Status Shows "SecretSyncedError"

Check the ExternalSecret status for details:

kubectl describe externalsecret my-app-secrets

Common causes:

  • 401 Unauthorized: Verify your KeyEnv token is correct and hasn't expired
  • 403 Forbidden: Ensure the token has read access to the specified project
  • 404 Not Found: Check that the secret key and environment name are correct

Secrets Not Updating

  1. Check the refreshInterval on your ExternalSecret
  2. Verify ESO controller is running:
    kubectl get pods -n external-secrets
  3. Check ESO controller logs:
    kubectl logs -n external-secrets -l app.kubernetes.io/name=external-secrets

ClusterSecretStore Shows "NotReady"

Validate the ClusterSecretStore status:

kubectl describe clustersecretstore keyenv

Common issues:

  • Token secret not found in the specified namespace
  • Incorrect secret key name (should be token)
  • Network connectivity issues to api.keyenv.dev

Testing the Connection

Test the KeyEnv endpoint directly:

curl -H "Authorization: Bearer env_your_token" \
  "https://api.keyenv.dev/api/v1/eso/secret?project=my-project&env=production&key=DATABASE_URL"

A successful response returns:

{ "value": "your-secret-value" }

Best Practices

  1. Use dedicated service tokens - Create a token specifically for Kubernetes with minimal permissions
  2. Set appropriate refresh intervals - Balance between freshness and API load (1h is a good default)
  3. Use dataFrom for many secrets - More efficient than individual secret syncs
  4. Monitor ESO status - Set up alerts for sync failures
  5. Test in non-production first - Verify your setup works before deploying to production

On this page