Containers
Leverage AWS secrets stores from EKS Fargate with External Secrets Operator
Secrets management is a challenging but critical aspect of running secure and dynamic containerized applications at scale. To support this need to securely distribute secrets to running applications, Kubernetes provides native functionality to manage secrets in the form of Kubernetes Secrets. However, many customers choose to centralize the management of secrets outside of their Kubernetes clusters by using external secret stores such as AWS Secrets Manager to improve the security, management, and auditability of their secret usage.
Consuming secrets from external secret stores often requires modifications to your application code to support API-based calls to the external store to retrieve a secret at application run time. When running applications on Amazon EKS, you can use Kubernetes’s flexibility to expose secrets directly to pods without requiring any application code changes. One example of how to accomplish this is by using the AWS Secrets and Configuration Provider (ASCP) for the Kubernetes Secrets Store CSI Driver. ASCP uses the Secrets Store CSI driver to expose secrets from AWS Secrets Manager to your pods as a mounted storage volume.
Customers using Amazon EKS to orchestrate their applications often will use AWS Fargate as their compute layer to reduce the management complexity of operating their containerized workloads. For customers who have EKS clusters with AWS Fargate nodes, a different method of consuming external secrets will be required since ASCP with the Secrets Store CSI is deployed to your EKS cluster as a daemonset. As of today, daemonsets are not supported on Fargate.
One such option is the open-source External Secrets Operator (External Secrets) project. External Secrets manages your secrets in a different manner from the Secrets Store CSI driver. Instead of mounting secrets as volumes, External Secrets reads secrets from your external secret store and automatically stores the values as native Kubernetes Secrets in the Kubernetes control plane. External Secrets is installed as a deployment on your cluster and works with Amazon EKS Fargate-only clusters as well as those with Amazon EC2-based nodes.
In this post, I walk through using the External Secrets Operator on an EKS Fargate cluster to consume secrets stored in AWS Secrets Manager. This same method could apply to secrets stored in an AWS Systems Manager Parameter Store but will not be covered directly in this post.
Solution overview
External Secrets will be deployed to your EKS cluster as a standard Kubernetes deployment, allowing Fargate to be used as your container compute provider. Once configured, External Secrets will sync defined secrets from AWS Secrets Manager back to your EKS cluster. The secrets will be automatically synced on a recurring basis to capture any updates or changes, such as periodic credential rotations.
The following diagram outlines how the sync process will occur.
- External Secrets will make API calls to AWS Secrets Manager on a recurring basis to copy specified secrets values.
- External Secrets will take the copied values and create native Kubernetes Secrets based on the external secret values.
- Kubernetes Secrets will be available for consumption by specified applications running on your EKS cluster. Kubernetes RBAC will define what applications can consume the secrets.
- Pods will consume the secrets as either volume mounts or environment variables as defined in the pod specifications.
Figure 1. Solution overview
Walkthrough
This walkthrough will guide you through the steps of using the External Secrets Operator to sync a secret from AWS Secrets Manager to your EKS Fargate cluster, then consuming that secret in an application pod running on Fargate.
The steps to be outlined in the following sections include:
- Deploying External Secrets Operator on a EKS Fargate cluster
- Configuring External Secrets Operator resources
- Consuming synced Kubernetes Secret in an application pod
Prerequisites
For this walkthrough, you should have the following prerequisites:
- An AWS account
- An IAM policy with permissions to retrieve a secret from Secrets Manager
- Your secret stored in Secrets Manager
- An existing Amazon EKS cluster
- AWS CLI installed
- kubectl, Helm, and eksctl installed
Step 1. Create an EKS Fargate profile
With Amazon EKS, Fargate profiles allow administrators to specify which pods in your cluster should be designated to run on Fargate. Profile selectors declare namespaces and optionally labels that will be evaluated against to determine if a pod should be scheduled on Fargate. Fargate profiles can be created via numerous CLI and Infrastructure as Code (IaC) methods. The following example will showcase the eksctl method. To create your Fargate profile using the AWS console, see creating a Fargate profile in the Amazon EKS user guide.
External Secrets will, by default, deploy its resources in a namespace called “external-secrets.” To ensure our External Secrets pods run on Fargate, we will specify this namespace in our Fargate profile selector.
From the terminal where you have eksctl installed, run the following command to create your Fargate profile. Replace “CLUSTERNAME” with the name of your EKS cluster.
Once the profile creation is complete, run the following command to verify.
The expected output should look similar to the following with your “externalsecrets” profile listed.
- name: externalsecrets podExecutionRoleARN: arn:aws:iam::<ACCOUNTNUM>:role/eksctl-blogdemo-cluster-FargatePodExecutionRole-1GY5JZHKR1993 selectors: - namespace: external-secrets status: ACTIVE subnets: - subnet-072cad201ce783be1 - subnet-086d0835e05be3d89
Step 2. Deploy External Secrets Operator
The External Secrets Operator provides Helm Charts for ease of deployment. The Helm Charts can be found in the project’s Github repository. The following commands will perform a default installation and are further outlined in the External Secrets Getting Started guide.
From the terminal where you have Helm installed, run the following commands.
Once the deployment is complete, run the following command to verify.
The expected output should look like the following with the External Secrets pod running.
NAMESPACE NAME READY STATUS RESTARTS AGE external-secrets external-secrets-8cdbf85cd-k4hvs 1/1 Running 0 87s external-secrets external-secrets-cert-controller-655b7b7d45-bxh24 1/1 Running 0 87s external-secrets external-secrets-webhook-75db54d748-85l8p 1/1 Running 0 87s
Step 3. Set up IAM roles for service accounts
IAM roles for service accounts (IRSA) is a feature of EKS that allows you to map AWS IAM roles to Kubernetes Service Accounts. This feature provides a strategy for managing AWS credentials for your applications running on EKS without having to directly manage static credentials. Reference this AWS blog post, Diving into IAM Roles for Service Accounts, for more information on IRSA.
For our use case with External Secrets, we will be using IRSA to provide AWS credentials to the External Secrets pods for fine-grained access control to our AWS Secrets Manager secret.
The first step in enabling IRSA is to create an IAM OIDC provider for your cluster, if one is not already created. You can follow the steps outlined in create an IAM OIDC provider for your cluster in the Amazon EKS user guide to create an IAM OIDC provider for your cluster or use the following eksctl command. Once the OIDC provider is associated with your EKS cluster, we can create an IRSA service account for External Secrets to use.
From the terminal where you have eksctl installed, run the following command.
From the terminal where you have eksctl installed, run the following commands. IAMPOLICYARN will be replaced with the Amazon Resource Name (ARN) of the IAM policy that has permissions to access your AWS Secrets Manager secret.
The IAM policy associated with the IRSA service account will be used to provide granular access to the secrets stored in AWS Secrets Manager. Limiting the scope of your IAM policy will be important for following security best practices. The following is an example IAM policy used for this blog post. This example policy limits actions to describing and retrieving the single specified secret.
To verify successful creation of the Kubernetes Service Account, run the following kubectl command.
You should see the specified service account name listed.
NAME SECRETS AGE blogdemosa 1 2m49s default 1 18h
You can further inspect the service account by running the following kubectl command.
The output will provide extended service account details. You should see the EKS annotation that references the IAM role created by the eksctl command.
Name: blogdemosa Namespace: default Labels: app.kubernetes.io/managed-by=eksctl Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNTNUM>:role/blogdemosa Image pull secrets: <none> Mountable secrets: blogdemosa-token-8d46d Tokens: blogdemosa-token-8d46d Events: <none>
With our IRSA service account created, we can now configure External Secrets resources.
Step 4. Configuring External Secrets to sync AWS Secrets Manager secrets
External Secrets provides custom resources (CRDs) for configuring the required operator functionality to sync external secrets to your cluster.
SecretStore is used to define the external secrets store and the authentication mechanisms to access the declared store. ExternalSecret defines what data to fetch from the secret store defined in the SecretStore resource. Reference the External Secret resource model documentation for additional details.
For this example, we are going to create a SecretStore object that references our existing AWS Secrets Manager store. We will specify the IRSA-based service account we previously created to define the AWS credentials that will be used to access the secret store.
Use the following object definition YAML to create the resource using kubectl.
From the terminal where you have kubectl installed and the YAML file created, run the following commands.
You can validate your SecretStore was created by running the following kubectl command.
You should see the created SecretStore referenced with STATUS of Valid.
NAME AGE STATUS blogdemo 82s Valid
We will now create our ExternalSecret resource, specifying the secret we want to access and referencing the previously created SecretStore object. You will specify your existing AWS Secrets Manager secret name and keys where highlighted. For details on creating an AWS Secrets Manager secret, see Create and manage secrets with AWS Secrets Manager in the AWS Secrets Manager user guide. For additional External Secrets configuration options, see the AWS Secrets Manager provider in the External Secrets documentation.
Use the following object definition YAML to create the resource using kubectl. Replace the highlight areas with your specific Secrets Manager values
Here is the example secret used for this blog as it is stored in Secrets Manager. Only the username and password are being referenced in the previous ExternalSecret definition.
Figure 2. AWS Secrets Manager Secret value example
From the terminal where you have kubectl installed and the YAML file created, run the following commands.
Once you have created the ExternalSecret resource, you will be able to view the newly created Kubernetes Secret that is being synced with the Secrets Manager store. Run the following kubectl command.
The output will describe the secret as in the following:
Name: blogdemosecret Namespace: default Labels: <none> Annotations: reconcile.external-secrets.io/data-hash: a12b24f34338b71e8d118c6a04107b0e Type: Opaque Data ==== blogdemo-mysql-password: 8 bytes blogdemo-mysql-username: 8 bytes
You now have a synced Kubernetes Secret that can be used within your pod specification for consumption by your applications.
One thing to consider with Kubernetes Secrets is that by default, they are stored in etcd unencrypted in base64 encoded form. With EKS, you are able to leverage AWS Key Management Service (AWS KMS) keys to provide envelope encryption of Kubernetes secrets stored in EKS. This feature, along with the fact that we operate the etcd volumes encrypted at disk-level using AWS-managed encryption keys, provides a defense in-depth strategy for protection of your Kubernetes secrets stored in etcd. Reference this AWS blog post for more information on EKS envelope encryption of Kubernetes Secrets.
Step 4. Consuming secret in pod
Now that External Secrets has synced your AWS Secrets Manager secret to a Kubernetes Secret, you can consume this secret by referencing it in your Pod specification. Refer to Using a Secret in the Kubernetes documentation for additional information on different secret consumption options available.
Use the following pod spec as an example for using your secret via environment variables.
From the terminal where you have kubectl installed and the YAML file created, run the following commands.
Once the pod has been deployed, you can directly access the running container’s shell using the following command.
From there, you can test your environment variables to view your secrets.
By exposing your secrets as environment variables, you are able to consume these secrets in your application as required.
Cleaning up
To avoid incurring continued charges, delete any created resources from your cluster, especially those that are running as Fargate pods. Make sure you run the following commands from the same machine used to originally deploy the resources.
Also delete any IAM or Secrets Manager resources you created specifically for this walkthrough.
Conclusion
This blog post showcased how customers using EKS Fargate can consume secrets stored in AWS external secrets managers using the External Secrets Operator without requiring any code changes to your applications. This same solution can also apply to EC2 and hybrid EC2/Fargate clusters.
For more information on the External Secrets Operator, see the External Secrets Operator documentation or the External Secrets Github.