Mounting AWS Secrets Manager secret on Kubernetes Pod
Now that we have a secret stored in AWS Secrets Manager and synchronized with a Kubernetes Secret, let's mount it inside the Pod. First, we should examine the catalog Deployment and the existing Secrets in the catalog namespace.
Currently, the catalog Deployment accesses database credentials from the catalog-db secret via environment variables:
RETAIL_CATALOG_PERSISTENCE_USERRETAIL_CATALOG_PERSISTENCE_PASSWORD
This is done by referencing a Secret with envFrom:
- configMapRef:
name: catalog
- secretRef:
name: catalog-db
The catalog Deployment currently has no additional volumes or volumeMounts except for an emptyDir mounted at /tmp:
- emptyDir:
medium: Memory
name: tmp-volume
- mountPath: /tmp
name: tmp-volume
Let's modify the catalog Deployment to use the secret stored in AWS Secrets Manager as the source for credentials:
- Kustomize Patch
 - Deployment/catalog
 - Diff
 
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../../../base-application/catalog
patches:
  - path: deployment.yaml
  - path: serviceaccount.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/created-by: eks-workshop
    app.kubernetes.io/type: app
  name: catalog
  namespace: catalog
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/component: service
      app.kubernetes.io/instance: catalog
      app.kubernetes.io/name: catalog
  template:
    metadata:
      annotations:
        prometheus.io/path: /metrics
        prometheus.io/port: "8080"
        prometheus.io/scrape: "true"
      labels:
        app.kubernetes.io/component: service
        app.kubernetes.io/created-by: eks-workshop
        app.kubernetes.io/instance: catalog
        app.kubernetes.io/name: catalog
    spec:
      containers:
        - env:
            - name: RETAIL_CATALOG_PERSISTENCE_USER
              valueFrom:
                secretKeyRef:
                  key: username
                  name: catalog-secret
            - name: RETAIL_CATALOG_PERSISTENCE_PASSWORD
              valueFrom:
                secretKeyRef:
                  key: password
                  name: catalog-secret
          envFrom:
            - configMapRef:
                name: catalog
          image: public.ecr.aws/aws-containers/retail-store-sample-catalog:1.2.1
          imagePullPolicy: IfNotPresent
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 3
          name: catalog
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            periodSeconds: 5
            successThreshold: 3
          resources:
            limits:
              memory: 512Mi
            requests:
              cpu: 250m
              memory: 512Mi
          securityContext:
            capabilities:
              drop:
                - ALL
            readOnlyRootFilesystem: true
            runAsNonRoot: true
            runAsUser: 1000
          volumeMounts:
            - mountPath: /etc/catalog-secret
              name: catalog-secret
              readOnly: true
            - mountPath: /tmp
              name: tmp-volume
      securityContext:
        fsGroup: 1000
      serviceAccountName: catalog
      volumes:
        - csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: catalog-spc
          name: catalog-secret
        - emptyDir:
            medium: Memory
          name: tmp-volume
         app.kubernetes.io/instance: catalog
         app.kubernetes.io/name: catalog
     spec:
       containers:
-        - envFrom:
+        - env:
+            - name: RETAIL_CATALOG_PERSISTENCE_USER
+              valueFrom:
+                secretKeyRef:
+                  key: username
+                  name: catalog-secret
+            - name: RETAIL_CATALOG_PERSISTENCE_PASSWORD
+              valueFrom:
+                secretKeyRef:
+                  key: password
+                  name: catalog-secret
+          envFrom:
             - configMapRef:
                 name: catalog
-            - secretRef:
-                name: catalog-db
           image: public.ecr.aws/aws-containers/retail-store-sample-catalog:1.2.1
           imagePullPolicy: IfNotPresent
           livenessProbe:
             httpGet:
[...]
             readOnlyRootFilesystem: true
             runAsNonRoot: true
             runAsUser: 1000
           volumeMounts:
+            - mountPath: /etc/catalog-secret
+              name: catalog-secret
+              readOnly: true
             - mountPath: /tmp
               name: tmp-volume
       securityContext:
         fsGroup: 1000
       serviceAccountName: catalog
       volumes:
+        - csi:
+            driver: secrets-store.csi.k8s.io
+            readOnly: true
+            volumeAttributes:
+              secretProviderClass: catalog-spc
+          name: catalog-secret
         - emptyDir:
             medium: Memory
           name: tmp-volume
We'll mount the AWS Secrets Manager secret using the CSI driver with the SecretProviderClass we validated earlier at the /etc/catalog-secret mountPath inside the Pod. This will trigger AWS Secrets Manager to synchronize the stored secret contents with Amazon EKS and create a Kubernetes Secret that can be consumed as environment variables in the Pod.
Let's verify the changes made in the catalog namespace.
The Deployment now has a new volume and corresponding volumeMount that uses the CSI Secret Store Driver and is mounted at /etc/catalog-secret:
- csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: catalog-spc
name: catalog-secret
- emptyDir:
medium: Memory
name: tmp-volume
- mountPath: /etc/catalog-secret
name: catalog-secret
readOnly: true
- mountPath: /tmp
name: tmp-volume
Mounted Secrets provide a secure way to access sensitive information as files inside the Pod's container filesystem. This approach offers several benefits including not exposing secret values as environment variables and automatic updates when the source Secret is modified.
Let's examine the contents of the mounted Secret inside the Pod:
eks-workshop-catalog-secret-WDD8yS
password
username
{"username":"catalog", "password":"dYmNfWV4uEvTzoFu"}catalog
dYmNfWV4uEvTzoFu
When mounting secrets from AWS Secrets Manager using the CSI driver, three files are created in the mountPath:
- A file with the name of your AWS secret containing the complete JSON value
 - Individual files for each key extracted via jmesPath expressions as defined in the SecretProviderClass
 
The environment variables are now sourced from the newly created catalog-secret, which was automatically created by the SecretProviderClass via the CSI Secret Store driver:
- name: RETAIL_CATALOG_PERSISTENCE_USER
valueFrom:
secretKeyRef:
key: username
name: catalog-secret
- name: RETAIL_CATALOG_PERSISTENCE_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: catalog-secret
NAME TYPE DATA AGE
catalog-db Opaque 2 15h
catalog-secret Opaque 2 43s
We can confirm the environment variables are set correctly in the running pod:
RETAIL_CATALOG_PERSISTENCE_ENDPOINT=catalog-mysql:3306
RETAIL_CATALOG_PERSISTENCE_PASSWORD=dYmNfWV4uEvTzoFu
RETAIL_CATALOG_PERSISTENCE_PROVIDER=mysql
RETAIL_CATALOG_PERSISTENCE_DB_NAME=catalog
RETAIL_CATALOG_PERSISTENCE_USER=catalog
We now have a Kubernetes Secret fully integrated with AWS Secrets Manager that can leverage secret rotation, a best practice for secrets management. When a secret is rotated or updated in AWS Secrets Manager, we can roll out a new version of the Deployment allowing the CSI Secret Store driver to synchronize the Kubernetes Secret contents with the updated value.