Search code examples
kubernetesgoogle-secret-manager

Example of how to use secrets-init container in a Kubernetes deployment to access secrets from Google Secret Manager?


This is similar to questions that have been asked before (for example How to inject secrets from Google Secret Manager into K8s pod? and Loading secrets as ENV from init container), but I'm looking for an actual example of a deployment.yaml file.

Let's say I have this Kubernetes yaml file:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    name: my-app
spec:  
  selector:
    matchLabels:
      name: "my-app"
  template:
    metadata:
      labels:
        name: "my-app"
        version: "1.0.0"
    spec:
      serviceAccountName: my-app
      containers:
        - name: "my-app"
          env:
            - name: DB_USER
              value: postgres
            - name: DB_PASS
              value: password 
            - name: DB_NAME
              value: postgres
          image: "my-app:1.0.0" 
...

We have stored the "DB_USER" and "DB_PASS" in Google Secret Manager and are looking for a way to pass these secrets to "my-app" as environment variables. I've found the ghcr.io/doitintl/secrets-init:0.4.7 image which seems very promising, but I cannot seem to find any example of how to actually use it with Google Cloud Secret Manager in Kubernetes. What I'm looking for is how to modify the example above to load the secrets "DB_USER" and "DB_PASS" from Goolge Secret Manager and pass them as environment variables.


Solution

  • So I managed to get it to work using ideas from https://itnext.io/kubernetes-and-secrets-management-in-cloud-858533c20dca The main hint being

    "In order to use secrets-init with Kubernetes object (Pod/Deployment/Job/etc) without modifying Docker image, consider injecting secrets-init into a target Pod through initContainer. Copy secrets-init binary from init container to a common shared volume and change Pod command to run secrets-init as a first command."

    Your deployment.yaml would look like this

      apiVersion: apps/v1
      kind: Deployment
      metadata:
       name: my-app
       labels:
         name: my-app
      spec:  
       selector:
         matchLabels:
           name: "my-app"
       template:
         metadata:
           labels:
             name: "my-app"
              version: "1.0.0"
         spec:
           serviceAccountName: my-app
           volumes:
            - name: secrets-init-volume
              emptyDir: { }
           initContainers:
            - name: secrets-init
              image: doitintl/secrets-init:0.3.6
              command:
                - sh
              args:
                - -c
                - "cp /usr/local/bin/secrets-init /secrets-init/bin/"
              volumeMounts:
                - mountPath: /secrets-init/bin
                  name: secrets-init-volume
           containers:
            - name: "my-app"
                env:
                 - name: DB_USER
                   value: postgres
                 - name: DB_PASS
                  value: gcp:secretmanager:projects/PROJECT_ID/secrets/SECRET_NAME
                 - name: DB_NAME
                  value: postgres
              image: "my-app:1.0.0" 
              command:
                  - "/secrets-init/bin/secrets-init"
              args:
                  - "--provider=google"
                  - YOUR_ACTUAL_STARTUP_SCRIPT_FOR_YOUR_IMAGE
    

    Main Points

    • An initContainer with the secrets-init image is added, it copies the binary secret-init into a shared volume (in this case secrets-init-volume)
    • Your main container uses the shared volume, and uses the binary as the very first command. In this case the path to binary is /secrets/init/secrets-init which becomes the entrypoint command
    • "--provider=google" has to be passed as the first argument to secrets-init command as aws is the default provider.
    • The serviceAccountName that you specify (in your case my-app) should have the IAM permission to access Secret Manager in GCP