AWS Secret Manager is a well-known service for storing, managing, and accessing sensitive information in a secure way. From database credentials to API keys to application environment variables, they can all be securely stored and managed using Secret Manager. Additional features such as secret rotation, secret encryption, programmatic access, and cross-region replication make this service very reliable for developers and cloud architects.
In July 2024, AWS announced the open-source release of the Secrets Manager Agent. This update introduces a primary feature that significantly enhances the ease of retrieving secrets by reading them from AWS Secrets Manager and caching them in memory. The focus of this release is to simplify the process of accessing secrets, eliminating the need for constant API calls to AWS and reducing the overhead of maintaining separate code for secret management.
Let's test out this new feature and try to retrieve a secret value.
Table of Contents
- Secret Manager Agent Features
- Create a Secret
- Create an EC2 Instance
- Add Secret Manager Access Policy
- Cloning Secret Manager Agent Git Repository
- Build Secret Manager Agent Binary
- Install Secret Manager Agent
- Testing the Secret Manager Agent
Secret Manager Agent Features
Let's quickly go over the features of the Secret Manager Agent:
- Can support environments like: EKS, EC2, ECS, and AWS Lambda.
- Ability to cache secrets in memory.
- Can only make read requests (can't modify secrets)
- Offers protection against Server Side Request Forgery (SSRF) to help improve secret security.
- Periodically refreshes the cached secret value.
- Has the ability to change agent configuration.
- Returns secret in the same format as the response of API
GetSecretValue
.
While the AWS Secrets Manager Agent is a relatively new service, its setup can be somewhat complex and may present a few challenges. However, with the right approach, you can configure it effectively and resolve any errors that arise along the way. Let's walk through the configuration process and address potential issues to ensure a smooth setup.
Create a Secret
Who would've guessed right? The first step is to create a secret. Log in to your AWS account and create a simple secret in Secret Manager.
Create an EC2 Instance
We'll be testing the AWS Secrets Manager Agent on an EC2 instance, as it offers easier configuration and eliminates the need for additional credential setup. Ensure that the EC2 instance is configured with the necessary permissions for connection. You can choose your preferred method of access: AWS Systems Manager Session Manager, EC2 Instance Connect, or SSH with a key pair. Additionally, make sure the instance has secure internet access.
Once connection to your instance has been established, we can move on to configuring and installing Secret Manager Agent.
Add Secret Manager Access Policy
Attach an IAM policy to the Instance Role of the previously created EC2 Instance. The policy should allow access to the previously created Secret
{ "Version": "2012-10-17", "Statement": [ { "Sid": "secretmanager", "Effect": "Allow", "Action": [ "secretsmanager:DescribeSecret", "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:<Region>:<Account_ID>:<secret_name>" ] } ] }
Cloning Secret Manager Agent Git Repository
We will start by installing git and then cloning the git repository containing Secret Manager Agent source code.
(1) Install Git:
$ sudo dnf install git -y
(2) Clone Repository:
$ sudo git clone https://github.com/aws/aws-secretsmanager-agent.git && cd aws-secretsmanager-agent
Build Secret Manager Agent Binary
We need to install standard development tools and Rust to build the agent binary.
Now perform the following operations:
(1) Install development tools:
$ sudo yum -y groupinstall "Development Tools"
(2) Install Rust:
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh . "$HOME/.cargo/env"
(3) Build the agent binary
$ cargo build --release
A possible error during this step is a Permission denied
error.
If you lack permissions to compile rust dependencies, you can provide permission using chmod
or chown
commands. Alternatively, you can also just switch to root user with sudo su
.
This should have created a binary with the name aws_secretmanager_agent
in target/release
.
Install Secret Manager Agent
At this point, if we directly try to execute the agent binary, we face issues since SSRF token variables are not set up for the environment.
$ target/release/aws_secretmanager_agent Could not read SSRF token variable(s) ["AWS_TOKEN", "AWS_SESSION_TOKEN"]: environment variable not found
To resolve this issue, AWS have conveniently provided an install script in the repository.
The install script is located in aws-secretsmanager-agent/aws_secretsmanager_agent/configuration/install
.
The install script creates a SSRF token and stores it in the file /var/run/awssmatoken
. Additonally, it creates a user group awssmatokenreader
. The token is readable for users in this group.
If you directly try to run the script, you will most likely face this error:
$ sudo bash aws_secretsmanager_agent/configuration/install Can not read awssmaseedtoken
Let's open the script and have a look at a few parameters defined in the script:
AGENTDIR=/opt/aws/secretsmanageragent AGENTBIN=aws_secretsmanager_agent TOKENGROUP=awssmatokenreader AGENTUSER=awssmauser TOKENSCRIPT=awssmaseedtoken AGENTSCRIPT=awssmastartup
These are the default configuration for the install script. The AGENTBIN
value should be the path of the binary we created, but by default it is set to current directory (path of the install script).
We can simply update the path, but instead let's copy the binary into the location of install script.
$ cp target/release/aws_secretsmanager_agent aws_secretsmanager_agent/configuration/
These should be the content of the configuration directory before running the install script
$ ls aws_secretsmanager_agent/configuration/ aws_secretsmanager_agent awssmaseedtoken awssmaseedtoken.service awssmastartup.service install uninstall
Let's run the installation script now. The installation script will install and run the secret manager agent.
On startup, the script generates a random SSRF token and saves it in /var/run/awssmatoken
. The token is accessible to members of the awssmatokenreader
group, which is created by the installation script.
$ cd aws_secretsmanager_agent/configuration/ $ bash install
if configuration is not correct, you might get the following error:
Can not read awssmaseedtoken
We face this error if the AGENTBIN
path is not properly set to the path of the binary.
If you lack permissions to execute the installation script, you can provide permission using chmod
or chown
commands. Alternatively, you can also just switch to root user with sudo su
.
If all goes well, your output should look like this:
Created symlink /etc/systemd/system/multi-user.target.wants/awssmaseedtoken.service → /etc/systemd/system/awssmaseedtoken.service. Created symlink /etc/systemd/system/multi-user.target.wants/awssmastartup.service → /etc/systemd/system/awssmastartup.service.
Let's confirm that the SSRF token has been created in /var/run/
$ ls /var/run/ | grep awssmatoken awssmatoken
Testing the Secret Manager Agent
Testing the secret manager agent can be done simply using a curl
command to endpoint of the secret.
I would rather prefer accessing through a Python script, since most of my backend development work involves Python frameworks.
This is a script for retrieving secrets:
import requests import json SECRET_NAME = '<YOUR_SECRET_NAME>' # Function that fetches the secret from Secrets Manager Agent for the provided secret id. def get_secret(): # Construct the URL for the GET request url = f"http://localhost:2773/secretsmanager/get?secretId={SECRET_NAME}" # Get the SSRF token from the token file with open('/var/run/awssmatoken') as fp: token = fp.read() headers = { "X-Aws-Parameters-Secrets-Token": token.strip() } try: # Send the GET request with headers response = requests.get(url, headers=headers) # Check if the request was successful if response.status_code == 200: # Return the secret value return json.loads(response.text) else: # Handle error cases raise Exception(f"Status code {response.status_code} - {response.text}") except Exception as e: # Handle network errors raise Exception(f"Error: {e}") if __name__ == "__main__": my_secrets = get_secret() print(my_secrets['SecretString'])
Let's run it to retrieve the secret values:
python secret_manager_test.py {"secret_key_0":"secret_value_0","secret_key_1":"secret_value_0"}
And there it is! The secret values we set initially can be safely retrieved. The Secrets retrieved will be cached in memory eliminating the need for constant API calls.
I hope this feature enhances your development experience and makes working with AWS more efficient and seamless.
Author:
Rahul Raje
JTP Co., Ltd.