Writing about Cloud, architecture, AWS, GCP and software engineering.

How to access your AWS Secret Manager secrets in an Elastic Kubernetes Service cluster

November 3, 2021

By using the Kubernetes Secrets Store CSI Driver you can provide pods with secrets from the AWS Secret Manager. This allows you to use the features the Secrets Manager has to offer within your EKS cluster.

The Secrets Store CSI driver mounts secrets from external stores into your pods as volumes. Secret Store providers are available for AWS, Azure, Google and HashiCorp Vault. These providers allow secrets store integration with your Kubernetes cluster. This means your application doesn’t have to implement custom code to interact with these secret stores.

The Secrets Store CSI driver allows you to sync secrets with the Kubernetes Secrets by enabling the Secret Sync so they can be defined as environment variables in pods. Also supported is key rotation but as of writing this its still in Alpha.

To integrate the AWS Secret Manager with Kubernetes you use the ‘AWS Secrets and Configuration Provider’ (ASCP), a plugin for the Secrets Store CSI driver. The provider retrieves the secrets from the Secret Manager and parameters from the Parameters store and passes them to the Secrets Store CSI driver.

Installation guide

In the guide below you will find how to set up the CSI driver with the AWS Secrets Manager. The Secret sync is enabled, so you can define secrets from the Secrets Manager as environment variables in your pods.

1. Secrets Store CSI Driver

To get a more detailed installation guide for the Secrets Store CSI Driver see installation

Or run the following commands (syncSecret parameter is set to true):

helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver --set syncSecret.enabled=true --namespace kube-system

Optional helm parameters

The following features are not enabled by default and can be enabled by setting helm parameters.

Feature Helm Parameter
Sync as Kubernetes secret syncSecret.enabled=true
Secret Auto rotation enableSecretRotation=true

For a list of all values that can be customized when running helm install see helm configuration

2. AWS Secrets and Configuration Provider (ASCP)

To install the ASCP use to following command:

kubectl apply -f https://raw.githubusercontent.com/aws/secrets-store-csi-driver-provider-aws/main/deployment/aws-provider-installer.yaml

3. IAM role

Create an IAM role with the following policy to allow access to the Secret Manager. This IAM role will be attached to a Kubernetes ServiceAccount.

Minimal policy

Minimal permissions needed to get secrets from the Secret Manager.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret"
            ],
            "Resource": "arn:*:secretsmanager:*:*:secret:MySecret-??????"
        }
    ]
}

Policy for secrets encrypted with KMS

When secrets are encrypted with KMS the user requesting the secrets must be able to get the KMS key used to encrypt the secret. The permissions needed for decrypting the secrets are kms:GenerateDataKey & kms:Decrypt

To make sure the KMS key is only accessible by the Secrets Manager use the kms:ViaService condition key with the value secretsmanager.AWS_REGION.amazonaws.com.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
              "kms:GenerateDataKey",
              "kms:Decrypt"
            ],
            "Resource": "*",
            "Condition": {
              "StringEquals": {
                "kms:CallerAccount": [
                  "AWS_ACCOUNT_ID"
                ],
                "kms:ViaService": [
                  "secretsmanager.AWS_REGION.amazonaws.com"
                ]
              }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret"
            ],
            "Resource": "arn:*:secretsmanager:*:*:secret:MySecret-??????"
        }
    ]
}

4. Kubernetes service account

The ServiceAccount gives your pods access to the Secret Manager with the previously created role.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: secret-manager-service-account
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/<IAM_ROLE_NAME>

5. Create SecretProviderClass

You configure one or more secrets you need, through the Kubernetes custom resource SecretProviderClass. The pod mounts the secrets as a volume from this custom resource.

The secretObjects is used to sync the secrets with Kubernetes Secrets. (only works when Secret Sync is enabled)

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: secrets-provider
spec:
  provider: aws
  secretObjects:
    - secretName: database_password
      type: Opaque
      data:
        - objectName: "MySecretPassword"
          key: password
  parameters:
    objects: |
      - objectName: arn:*:secretsmanager:*:*:secret:MySecret-??????
        objectAlias: "MySecretPassword"      

JSON formatted secret

If your secret from the AWS Secrets Manager is a JSON-formatted secret use jmesPath. This allows you to retrieve a specific key-value pair from the JSON.

Example JSON secret:

{
    "username": "username",
    "password": "password"
}

To retrieve the username and password from the JSON use jmesPath as follows:

  parameters:
    objects: |
      - objectName: arn:*:secretsmanager:*:*:secret:MySecret-??????
        jmesPath:
            - path: "username"
              objectAlias: "MySecretUsername"
            - path: "password"
              objectAlias: "MySecretPassword"      

6. Configure the volume for the pod

Below you will find a Kubernetes manifest which show how to add the SecretProviderClass as a volume. Making the secrets available as a file which can be found in /mnt/secrets-store. Or the secrets can be defined as environment variables in a pod.

kind: Pod
apiVersion: v1
metadata:
  name: secrets-store-inline
spec:
  serviceAccountName: secret-manager-service-account # The ServiceAccount with permissions to access the Secret Manager secret 
  containers:
    volumeMounts:
    - name: secrets-store-inline
      mountPath: "/mnt/secrets-store"
      readOnly: true
    env:
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: database_password # Name of the secret in Kubernetes Secrets, which has been set in the secretProviderClass 
          key: password
  volumes:
  - name: secrets-store-inline
    csi:
      driver: secrets-store.csi.k8s.io
      readOnly: true
      volumeAttributes:
        secretProviderClass: "secrets-provider" # Name of the secretProviderClass

Sources