Examples

Syncing Docker Registry Credentials to Kubernetes

If a Pod being defined in Kubernetes has an image in a private registry, you'll need to specify the imagePullSecrets attribute on the definition or Kubernetes won't know where to fetch the image from or what credentials to use. This guide walks through how to set this up so your credentials are stored in Doppler and synced to Kubernetes through our operator.

For the purpose of this example, we'll be assuming you're using AWS ECR. If you're using another registry, be sure to adjust the example values accordingly.

1. Create memcached repository

In this example we'll be using the public memcached image. You would simply replace this image with your own and otherwise follow the same process in production. Note that if you already have a repository named memcached in your registry you can namespace it or alter the name. Just be sure to make those adjustments wherever the image is referenced.

  1. Create the repository:

    aws ecr create-repository --repository-name memcached

  2. Pull the public image:

    docker pull memcached

  3. Tag the image:

    docker memcached:latest YOUR_REGISTRY_ID.dkr.ecr.YOUR_AWS_REGION.amazonaws.com/memcached:latest

  4. Push the image to your registry:

    docker push YOUR_REGISTRY_ID.dkr.ecr.YOUR_AWS_REGION.amazonaws.com/memcached:latest

2. Create secrets in Doppler

Create the following secrets in the Doppler project of your choice.

  • DOCKER_USERNAME

    For AWS ECR, this should be AWS. If you're using another registry, be sure to set it to the appropriate value.

  • DOCKER_PASSWORD

    You can obtain a valid token to use here by running aws ecr get-login-password --region YOUR_AWS_REGION. The value you get will be base64-encoded and should be stored in Doppler as such. Keep in mind that these tokens expire after 12 hours, so it needs to be updated on a schedule to ensure your credentials stay valid (e.g., use a GitHub Actions cronjob to update the secret in Doppler every 6-10 hours).

  • DOCKER_AUTH

    This secret should be the base64-encoded value of ${DOCKER_USERNAME}:${DOCKER_PASSWORD}. You can obtain this by running echo -n "${DOCKER_USERNAME}:${DOCKER_PASSWORD}" | base64 (make sure those environment variables are either set correctly when running this or that you've replaced those with the static values when running this).

  • DOCKER_HOST

    The host of your registry. For AWS ECR, it will look something like this: YOUR_REGISTRY_ID.dkr.ecr.YOUR_AWS_REGION.amazonaws.com.

  • DOCKER_IMAGE

    The fully qualified image name (with tag). This should look something like this: ${DOCKER_HOST}/memcached:latest.

  • DOCKER_CONFIG_JSON

    This is the actual JSON that will get populated in the kubernetes secret that's storing the dockerconfigjson. It should look like this:

    {
      "auths": {
        "${DOCKER_HOST}": {
          "username": "${DOCKER_USERNAME}",
          "password": "${DOCKER_PASSWORD}",
          "auth": "${DOCKER_AUTH}"
        }
      }
    }
    

3. Install and setup the operator

If you haven't already, follow our instructions for installing the operator.

Make sure you have a kubernetes secret created containing a Doppler token. This guide expects it to be called doppler-token-secret. For testing purposes, you can use your local Doppler CLI token:

kubectl create secret generic doppler-token-secret \
  --namespace doppler-operator-system \
  --from-literal=serviceToken=$(doppler configure get token --plain)

4. Create DopplerSecret resource to sync secrets

Now, create a new DopplerSecret resource to get your secrets syncing (be sure to replace YOUR_DOPPLER_PROJECT and YOUR_DOPPLER_CONFIG with the project and config you're using here):

apiVersion: secrets.doppler.com/v1alpha1
kind: DopplerSecret
metadata:
  name: memcached-docker-registry
  namespace: doppler-operator-system
spec:
  tokenSecret:
    name: doppler-token-secret
    namespace: doppler-operator-system
  project: YOUR_DOPPLER_PROJECT
  config: YOUR_DOPPLER_CONFIG
  managedSecret:
    name: memcached-docker-registry
    namespace: default
    type: kubernetes.io/dockerconfigjson
  secrets:
    - DOCKER_CONFIG_JSON
  processors:
    DOCKER_CONFIG_JSON:
      asName: .dockerconfigjson
      type: plain

This will create a new DopplerSecret resource called memcached-docker-registry in the doppler-operator-system namespace. It will then create a kubernetes secret called memcached-docker-registry in the default namespace with the type kubernetes.io/dockerconfigjson. The secrets attribute allows you to specify a subset of secrets you want to sync from Doppler. If specified, only the those secrets and the DOPPLER_* managed secrets will be synced over. We then use the processors attribute to ensure the DOCKER_CONFIG_JSON secret is renamed to .dockerconfigjson. The kubernetes.io/dockerconfigjson secret type is handled specially too and will not include the DOPPLER_* secrets like it would with other types.

Note that once you create this resource, it may take up to a minute before the kubernetes secret is synced to. Be sure it exists and has the expected value before proceeding. You can try fetching the secret using this command:

kubectl describe secret memcached-docker-registry

Once it exists, you should see something similar to the following:

Name:         memcached-docker-registry
Namespace:    default
Labels:       secrets.doppler.com/subtype=dopplerSecret
.
.
.
Type:  kubernetes.io/dockerconfigjson

Data
====
.dockerconfigjson:  4156 bytes

5. Create Pod resource that uses private registry

Now, it's time to create a new Pod that uses the registry credentials secret we just synced over. We'll use secret injection templates to inject the DOCKER_IMAGE secret into the resource definition.

apiVersion: v1
kind: Pod
metadata:
  name: memcached
  namespace: default
spec:
  containers:
    - name: memcached
      image: {{.DOCKER_IMAGE}}
  imagePullSecrets:
    - name: memcached-docker-registry

To apply this run:

kubectl apply -f <(doppler secrets substitute -p YOUR_DOPPLER_PROJECT -c YOUR_DOPPLER_CONFIG pod.yml)

6. Keeping the DOCKER_PASSWORD from expiring

The aws ecr get-login-password command returns a token that's valid for 12 hours. This obviously won't work inside your kubernetes cluster. To keep this up-to-date, you need to run a task in a cron-like environment that allows you to run scheduled jobs. Inside that job, you need to execute the following command (make sure DOCKER_USERNAME is passed in via the environment along with your AWS and Doppler credentials):

new_password=$(aws ecr get-login-password --region YOUR_AWS_REGION)
new_auth=$(echo -n "$DOCKER_USERNAME:$new_password" | base64)
doppler secrets set -p YOUR_DOPPLER_PROJECT -c YOUR_DOPPLER_CONFIG DOCKER_PASSWORD=$new_password DOCKER_AUTH=$new_auth

This task should run at least once every 12 hours (and realistically, you'll probably want it to run every 10 hours or so to ensure the token doesn't expire before it's updated). When you update the secrets in Doppler, they'll automatically get synced to your kubernetes cluster.