Skip to content

Managing Application Secrets

Securely managing application secrets, such as API keys, database passwords, and tokens, is a critical aspect of building secure and maintainable software. This guide covers the best practices for handling secrets on the Contain Platform, focusing on how your application should consume them once they are in your Kubernetes namespace.

Why Proper Secret Management Matters

It is essential to treat secrets as distinct from your application's source code. You should never:

  • Hardcode secrets directly in your code.
  • Store secrets in plain text in your Git repository.
  • Store secrets in your container images.

Storing secrets in these ways creates significant security risks and makes rotating secrets a difficult and error-prone process.

The Platform's Secrets Management Model

The platform provides a secure and automated workflow for managing secrets, which can be summarized in three steps:

  1. Store: A human or a system places a secret in a central, highly secure vault (e.g. OpenBao). This is the single source of truth.
  2. Synchronize: The Secrets Service (ExternalDNS) automatically detects the secret in the vault and creates a corresponding native Kubernetes Secret object in your application's namespace.
  3. Consume: Your application's pod consumes the native Kubernetes Secret as either an environment variable or a mounted file.

This model works well because your application remains completely decoupled from the underlying secret store. It only needs to know how to read standard Kubernetes Secret objects.

How to Consume Secrets in Your Pods

There are two primary patterns for consuming secrets in your application's pods. The one you choose depends on your application's needs and configuration.

Pattern 1: As Environment Variables

This is the most common pattern and is well-suited for applications that follow the 12-Factor App methodology. The secret's key-value pairs are injected directly into the container as environment variables.

Example

In this example, we assume a Kubernetes Secret named my-app-credentials exists and contains the keys DATABASE_USER and DATABASE_PASSWORD.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-application
spec:
  # ... other deployment specs
  template:
    spec:
      containers:
      - name: app
        image: my-app-image
        # This section tells Kubernetes to load all key-value pairs
        # from the specified secret as environment variables.
        envFrom:
          - secretRef:
              name: my-app-credentials

Your application can now access process.env.DATABASE_USER (in Node.js) or os.Getenv("DATABASE_USER") (in Go) to get the secret value.

Pod Restart Required

If the secret value is updated in the Kubernetes Secret, the pod must be restarted for the new environment variable value to be reflected.

Pattern 2: As Mounted Files

Mounting secrets as files in a volume is a more secure and flexible pattern. The secrets are exposed as files inside your container, and their values can often be updated automatically without requiring a pod restart.

Example

Using the same my-app-credentials secret, this Deployment mounts each key as a separate file inside the /etc/secrets directory.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-application
spec:
  # ... other deployment specs
  template:
    spec:
      containers:
      - name: app
        image: my-app-image
        # Mount the volume containing the secrets into the container
        volumeMounts:
        - name: secret-volume
          mountPath: "/etc/secrets"
          readOnly: true
      # Define the volume that will be populated by the secret's data
      volumes:
      - name: secret-volume
        secret:
          secretName: my-app-credentials

Inside the container, you would now have the following file structure:

/etc/secrets/
├── DATABASE_USER
└── DATABASE_PASSWORD

Your application would then read the contents of /etc/secrets/DATABASE_PASSWORD to get the secret value.

Next Step: Creating the Secret

This guide focuses on how your application should consume a Kubernetes Secret. To learn the specific steps for creating the ExternalSecret resource that populates this Secret from the central OpenBao store, please see the service-specific guide:

Getting Started with Secrets