Skip to content

Managing Kubernetes secrets with AWS Parameter Store

Published: at 12:00 AM

Managing secrets in a Kubernetes cluster can be overwhelming at a large scale having more configuration for microservices. Ever wonder how you store those there? As the majority of the running workloads inside the Kubernetes cluster need some kind of configuration or secrets. Kubernetes secrets help to decouple those secrets from the application codebase and reuse them inside the container pod runtime when required. You won’t be using every time imperative way to create those Kubernetes secrets objects by using the kubectl client to hit on those Kube API requests. What if you need to change or edit secrets often which are stored in the etcd database? It’s good to store on it rather than the pod definition or container image itself either private or public itself.

Secrets are stored by default in the insecure base64 format.

What if those Kubernetes resources required external secrets, especially from third parties like hashicorp vault, aws secret manager, or any other platform? It doesn’t come up natively support on Kubernetes. Hence External Secret Operator came into the big picture of the bond between those bridges. It integrates external secrets platform to make API calls to retrieve secret data and injects it as a native Kubernetes secret object. External Secret Operator uses the operator/controller patterns that extend the Kubernetes running application using Custom Resources Definition.

The External Secret Operator manages secrets via Custom Resource Definitions. ExternalSecret, SecretStore, and ClusterSecretStore are user-friendly wrappers around the external API that store and manage secrets on your behalf.

If secrets from external API got changed, the controller reconciles the state in the cluster and updates the secret. In this blog, we will be managing the secrets with the aws parameter store via External Secret Operator on Elastic Kubernetes Service. We will create the deployment where we will mount secrets retrieving from the parameter store and expose it into the pod via environment variables.

Ensure you have the following components and tools

To achieve our requirements, we will create an IAM role with the attached policy to read secrets which will associate it with Kubernetes Service Account. The following commands will deploy an External Secrets Operator to my EKS Cluster.

helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets

As pod’s container can able to call API requests to AWS Service via cli or SDK with AWS Identity and Access management permission. Instead of creating aws credentials, you can associate the IAM role with a Kubernetes service account and configure your pods to use the service account. We will create an IAM role, and bind the policy to be able to read secrets from the parameter store for the Kubernetes service account. To use AWS Identity and Access Management (IAM) roles for service accounts, an IAM OIDC provider must exist for your cluster. Here I will be using eksctl, you can provision via declarative way using terraform too.

eksctl utils associate-iam-oidc-provider --cluster your-cluster --approve --region your-region

We use the principle of least privilege on every environment to limit the permissions for the external-secrets IAM user to only access the path of the needed secret.

cat >policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameter"
        "ssm:GetParametersByPath"
      ],
      "Resource": "arn:aws:ssm:region:id:parameter/*"
    }
  ]
}
EOF

Create an IAM policy

aws iam create-policy --policy-name my-policy --policy-document file://policy.json

Create an IAM role and associate it with a Kubernetes service account. The following command ‘eksctl create iamserviceaccount’ takes an IAM policy arn as an argument, creates an IAM role associated with the given policy, and maps a service account to that role.

eksctl create iamserviceaccount --name my-service-account --namespace your-namespace --cluster your-cluster --role-name "my-role" \
    --attach-policy-arn arn:aws:iam::id:policy/get-parameter-store-policy --approve

Confirm that the Kubernetes service account is annotated with the role.

kubectl describe serviceaccount my-service-account -n your-namespace

Create and deploy SecretProviderClass custom resource and by using provider: aws

Demo here we make a simple deployment in the EKS cluster running out the single replica of Nginx mounting the secrets retrieved via ExternalSecret object. Let’s create some env on the AWS parameter store.

cat <<EOF | kubectl apply -f -
apiVersion: kubernetes-client.io/v1
kind: ExternalSecret
metadata:
  name: secret-env
spec:
  backendType: systemManager
  dataFrom:
    - /development/nginx-secret/env
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secret-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      nginx: secret
  template:
    metadata:
      annotations:
      labels:
        nginx: secret
    spec:
      containers:
        - name: secret-nginx
          image: nginx:latest
          imagePullPolicy: Always
          envFrom:
            - secretRef:
                name: secret-env
EOF

We can evaluate the secret value by typing:

kubectl get secrets secret-env -o json

Output

{
    "apiVersion": "v1",
    "data": {
        "cred": "c29tZXRoaW5n",
        "env": "ZGV2ZWxvcG1lbnQ="
    },
    "kind": "Secret",
    "metadata": {
        "creationTimestamp": "2022-11-29T07:54:29Z",
        "name": "secret-env",
        "namespace": "default",
        "ownerReferences": [
            {
                "apiVersion": "kubernetes-client.io/v1",
                "controller": true,
                "kind": "ExternalSecret",
                "name": "secret-env",
                "uid": "6987878d-9711-4147-b4ac-788c861ad823"
            }
        ],
        "resourceVersion": "278798378",
        "uid": "12efc64a-d373-4884-99f5-5d236abefc12"
    },
    "type": "Opaque"
}

Else you can also print the env inside the pod using the exec command.

Output

kubectl exec -it pod-name -- env

Conclusion

Kudos to you, you made it to the end. You can finally able to setup up the whole process for managing secrets using External Secret Manager with AWS Parameter Store. Kicked off by GoDaddy to solve the issue of reading the information from a third-party service like AWS Parameter Store and injecting the values as Kubernetes Secrets.

Feel free to provide feedback or reach out to me via Twitter. I look forward to hearing from you!

Are you interested in blog updates? You can also subscribe to my blog newsletter.