使用 AWS Secrets Manager 代理
Secrets Manager 代理的工作原理
AWS Secrets Manager 代理是一项客户端 HTTP 服务,可帮助您标准化在计算环境中使用来自 Secrets Manager 的密钥的方式。您可以将以下服务与该密钥一起使用:
-
AWS Lambda
-
Amazon Elastic Container Service
-
Amazon Elastic Kubernetes Service
-
Amazon Elastic Compute Cloud
Secrets Manager 代理检索密钥并将其缓存在内存中,从而可让您的应用程序从本地主机获取密钥,而不必直接调用 Secrets Manager。Secrets Manager 代理只能读取密钥,而无法对其进行修改。
Secrets Manager 代理使用来自您环境的 AWS 凭证来调用 Secrets Manager。它包括针对服务器端请求伪造(SSRF)的保护,以协助改进密钥安全性。默认情况下,Secrets Manager 代理使用后量子 ML-KEM 密钥交换作为优先级最高的密钥交换方式。
了解 Secrets Manager 代理缓存
Secrets Manager 代理使用内存缓存,该缓存会在 Secrets Manager 代理重启时重置。它会根据以下条件定期刷新缓存的密钥值:
-
默认刷新频率(TTL)为 300 秒
-
您可以使用配置文件修改 TTL
-
在 TTL 过期后请求密钥时,就会发生刷新
Secrets Manager 代理不包括缓存失效。如果密钥在缓存条目过期之前轮换,则 Secrets Manager 代理可能会返回过时的密钥值。
Secrets Manager 代理返回的密钥值与 GetSecretValue 的响应格式相同。密钥值在缓存中未进行加密。
构建 Secrets Manager 代理
在开始之前,请确保您已为自己的平台安装标准开发工具和 Rust 工具。
目前,在 macOS 上构建启用 fips 功能的代理需要以下解决方法:
- RPM-based systems
-
在基于 RPM 的系统上构建
-
使用存储库中提供的 install 脚本。
该脚本在启动时生成一个随机的 SSRF 令牌并将其存储在文件 /var/run/awssmatoken 中。安装脚本创建的 awssmatokenreader 组可以读取该令牌。
-
要允许您的应用程序读取令牌文件,您需要将应用程序在其下运行的用户账户添加到 awssmatokenreader 组。例如,您可以使用以下 usermod 命令授予应用程序读取令牌文件的权限,其中 <APP_USER> 是应用程序在其下运行的用户 ID。
sudo usermod -aG awssmatokenreader <APP_USER>
安装开发工具
在基于 RPM 的系统(如 AL2023)上,安装“开发工具”组:
sudo yum -y groupinstall "Development Tools"
-
安装 Rust
按照 Rust 文档中安装 Rust 的说明进行操作:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Follow the on-screen instructions
. "$HOME/.cargo/env"
-
构建代理
使用 cargo build 命令构建 Secrets Manager 代理:
cargo build --release
您将在 target/release/aws_secretsmanager_agent 下找到可执行文件。
- Debian-based systems
-
在基于 Debian 的系统上构建
-
安装开发工具
在基于 Debian 的系统(例如 Ubuntu)上,安装 build-essential 包:
sudo apt install build-essential
-
安装 Rust
按照 Rust 文档中安装 Rust 的说明进行操作:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Follow the on-screen instructions
. "$HOME/.cargo/env"
-
构建代理
使用 cargo build 命令构建 Secrets Manager 代理:
cargo build --release
您将在 target/release/aws_secretsmanager_agent 下找到可执行文件。
- Windows
-
在 Windows 上构建
-
设置开发环境
按照 Microsoft Windows 文档中的 在 Windows 上针对 Rust 设置开发环境中的说明进行操作。
-
构建代理
使用 cargo build 命令构建 Secrets Manager 代理:
cargo build --release
您将在 target/release/aws_secretsmanager_agent.exe 下找到可执行文件。
- Cross-compile natively
-
本机交叉编译
-
安装交叉编译工具
在 mingw-w64 包可用的发行版(例如 Ubuntu)上,安装交叉编译工具链:
# Install the cross compile tool chain
sudo add-apt-repository universe
sudo apt install -y mingw-w64
-
添加 Rust 构建目标
安装 Windows GNU 编译目标:
rustup target add x86_64-pc-windows-gnu
-
针对 Windows 构建
交叉编译适用于 Windows 的代理:
cargo build --release --target x86_64-pc-windows-gnu
您将在 target/x86_64-pc-windows-gnu/release/aws_secretsmanager_agent.exe 处找到可执行文件。
- Cross compile with Rust cross
-
使用 Rust 交叉进行交叉编译
如果系统本机没有交叉编译工具,则可以使用 Rust 交叉项目。有关更多信息,请参阅 https://github.com/cross-rs/cross。
-
设置 Docker
安装和配置 Docker:
# Install and start docker
sudo yum -y install docker
sudo systemctl start docker
sudo systemctl enable docker # Make docker start after reboot
-
配置 Docker 权限
将用户添加到 Docker 组:
# Give ourselves permission to run the docker images without sudo
sudo usermod -aG docker $USER
newgrp docker
-
针对 Windows 构建
安装交叉并构建可执行文件:
# Install cross and cross compile the executable
cargo install cross
cross build --release --target x86_64-pc-windows-gnu
安装 Secrets Manager 代理
从以下安装选项中选择您的计算环境。
- Amazon EC2
-
在 Amazon EC2 上安装 Secrets Manager 代理
-
导航到配置目录
更改到配置目录:
cd aws_secretsmanager_agent/configuration
-
运行安装脚本
运行存储库中提供的 install 脚本。
该脚本在启动时生成一个随机的 SSRF 令牌并将其存储在文件 /var/run/awssmatoken 中。安装脚本创建的 awssmatokenreader 组可以读取该令牌。
-
配置应用程序权限
将运行应用程序的用户账户添加到 awssmatokenreader 组中:
sudo usermod -aG awssmatokenreader APP_USER
将 APP_USER 替换为运行应用程序时使用的用户 ID。
- Container Sidecar
-
您可以使用 Docker 将 Secrets Manager 代理作为附加容器与应用程序一起运行。然后,您的应用程序可以从 Secrets Manager 代理提供的本地 HTTP 服务器检索密钥。有关 Docker 的信息,请参阅 Docker 文档。
创建用于 Secrets Manager 代理的附加容器
-
创建代理 Dockerfile
为 Secrets Manager 代理附加容器创建 Dockerfile:
# Use the latest Debian image as the base
FROM debian:latest
# Set the working directory inside the container
WORKDIR /app
# Copy the Secrets Manager Agent binary to the container
COPY secrets-manager-agent .
# Install any necessary dependencies
RUN apt-get update && apt-get install -y ca-certificates
# Set the entry point to run the Secrets Manager Agent binary
ENTRYPOINT ["./secrets-manager-agent"]
-
创建应用程序 Dockerfile
为您的客户端应用程序创建一个 Dockerfile。
-
创建 Docker Compose 文件
创建 Docker Compose 文件来运行具有共享网络接口的两个容器:
您必须加载 AWS 凭证和 SSRF 令牌,应用程序才能使用 Secrets Manager 代理。对于 Amazon EKS 和 Amazon ECS,请参阅以下内容:
version: '3'
services:
client-application:
container_name: client-application
build:
context: .
dockerfile: Dockerfile.client
command: tail -f /dev/null # Keep the container running
secrets-manager-agent:
container_name: secrets-manager-agent
build:
context: .
dockerfile: Dockerfile.agent
network_mode: "container:client-application" # Attach to the client-application container's network
depends_on:
- client-application
-
复制代理二进制文件
将 secrets-manager-agent 二进制文件复制到包含您的 Dockerfile 和 Docker Compose 文件的同一个目录中。
-
构建并运行容器
使用 Docker Compose 构建并运行容器:
docker-compose up --build
-
后续步骤
在您的客户端容器中,您现在可使用 Secrets Manager 代理来检索密钥。有关更多信息,请参阅 使用 Secrets Manager 代理检索密钥。
- Lambda
-
您可以将 Secrets Manager 代理打包为 Lambda 扩展。然后,您可以将其作为层添加到 Lambda 函数中,并从 Lambda 函数调用 Secrets Manager 代理来获取密钥。
以下说明介绍了如何使用 https://github.com/aws/aws-secretsmanager-agent 中的示例脚本 secrets-manager-agent-extension.sh 将 Secrets Manager 代理安装为 Lambda 扩展来获取名为 MyTest 的密钥。
创建用于 Secrets Manager 代理的 Lambda 扩展
-
打包代理层
从 Secrets Manager 代理代码包的根目录运行以下命令:
AWS_ACCOUNT_ID=AWS_ACCOUNT_ID
LAMBDA_ARN=LAMBDA_ARN
# Build the release binary
cargo build --release --target=x86_64-unknown-linux-gnu
# Copy the release binary into the `bin` folder
mkdir -p ./bin
cp ./target/x86_64-unknown-linux-gnu/release/aws_secretsmanager_agent ./bin/secrets-manager-agent
# Copy the `secrets-manager-agent-extension.sh` example script into the `extensions` folder.
mkdir -p ./extensions
cp aws_secretsmanager_agent/examples/example-lambda-extension/secrets-manager-agent-extension.sh ./extensions
# Zip the extension shell script and the binary
zip secrets-manager-agent-extension.zip bin/* extensions/*
# Publish the layer version
LAYER_VERSION_ARN=$(aws lambda publish-layer-version \
--layer-name secrets-manager-agent-extension \
--zip-file "fileb://secrets-manager-agent-extension.zip" | jq -r '.LayerVersionArn')
-
配置 SSRF 令牌
代理的默认配置会自动将 SSRF 令牌设置为在预设 AWS_SESSION_TOKEN 或 AWS_CONTAINER_AUTHORIZATION_TOKEN 环境变量中设置的值(后一个变量适用于启用 SnapStart 的 Lambda 函数)。或者,您可以改用自己的 Lambda 函数的任意值定义 AWS_TOKEN 环境变量,因为该变量优先于其他两个变量。如果您选择使用 AWS_TOKEN 环境变量,则必须通过 lambda:UpdateFunctionConfiguration 调用来设置该环境变量。
-
将层附加到函数
将层版本附加到 Lambda 函数:
# Attach the layer version to the Lambda function
aws lambda update-function-configuration \
--function-name $LAMBDA_ARN \
--layers "$LAYER_VERSION_ARN"
-
更新函数代码
更新您的 Lambda 函数以使用 X-Aws-codes-Secrets-Token 标头值(设置为来自上述环境变量之一的 SSRF 令牌值)查询 http://localhost:2773/secretsmanager/get?secretId=MyTest,从而检索密钥。务必在应用程序代码中实现重试逻辑,以适应 Lambda 扩展初始化和注册中的延迟。
-
测试此函数
调用 Lambda 函数以验证是否已正确获取密钥。
使用 Secrets Manager 代理检索密钥
要检索密钥,请调用本地 Secrets Manager 代理端点,并将密钥的名称或 ARN 作为查询参数包括在内。默认情况下,Secrets Manager 代理会检索密钥的 AWSCURRENT 版本。要检索其他版本,请使用 versionStage 或 versionId 参数。
为了帮助保护 Secrets Manager 代理,您必须在每个请求中包含 SSRF 令牌标头:X-Aws-Parameters-Secrets-Token。Secrets Manager 代理会拒绝没有此标头或具有无效 SSRF 令牌的请求。您可以在 配置 Secrets Manager 代理 中自定义 SSRF 标头名称。
所需的权限
Secrets Manager 代理使用适用于 Rust 的 AWS SDK,该 SDK 使用 AWS 凭证提供程序链。这些 IAM 凭证的身份决定了 Secrets Manager 代理检索密钥的权限。
有关权限的更多信息,请参阅 AWS Secrets Manager 的权限参考。
将密钥值拉入 Secrets Manager 代理后,任何有权访问计算环境和 SSRF 令牌的用户都可以从 Secrets Manager 代理缓存中访问密钥。有关更多信息,请参阅 安全性注意事项。
示例请求
- curl
-
例 示例 — 使用 curl 获取密钥
以下 curl 示例展示了如何从 Secrets Manager 代理获取密钥。该示例依赖于文件中存在的 SSRF,该文件是安装脚本存储示例的位置。
curl -v -H \\
"X-Aws-Parameters-Secrets-Token: $(/var/run/awssmatoken)" \\
'http://localhost:2773/secretsmanager/get?secretId=YOUR_SECRET_ID' \\
echo
- Python
-
例 示例 — 使用 Python 获取密钥
以下 Python 示例展示了如何从 Secrets Manager 代理获取密钥。该示例依赖于文件中存在的 SSRF,该文件是安装脚本存储示例的位置。
import requests
import json
# 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=YOUR_SECRET_ID"
# 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 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}")
了解 refreshNow 参数
Secrets Manager 代理使用内存缓存来存储密钥值,该值会定期刷新。默认情况下,当您在生存时间 (TTL) 到期后请求密钥时,就会发生此刷新,通常每 300 秒刷新一次。但是,这种方法有时会导致密钥值过时,特别是如果密钥在缓存条目过期之前轮换。
为解决此限制,Secrets Manager 代理支持在 URL 中使用称为 refreshNow 的参数。您可以使用此参数强制立即刷新密钥的值,从而绕过缓存并确保您拥有最新的信息。
- 默认行为(没有
refreshNow)
-
-
在 TTL 过期之前使用缓存值
-
仅在 TTL 之后刷新密钥(默认为 300 秒)
-
如果密钥在缓存过期之前轮换,则可能会返回旧值
- 使用
refreshNow=true 的行为
-
强制刷新密钥值
refreshNow 的默认值为 false。设置为 true 时,它将覆盖 Secrets Manager 代理配置文件中指定的 TTL,并对 Secrets Manager 进行 API 调用。
- curl
-
例 示例 – 使用 curl 强制刷新密钥
以下 curl 示例展示了如何强制 Secrets Manager 代理刷新密钥。该示例依赖于文件中存在的 SSRF,该文件是安装脚本存储示例的位置。
curl -v -H \\
"X-Aws-Parameters-Secrets-Token: $(/var/run/awssmatoken)" \\
'http://localhost:2773/secretsmanager/get?secretId=YOUR_SECRET_ID&refreshNow=true' \\
echo
- Python
-
例 示例 – 使用 Python 强制刷新密钥
以下 Python 示例展示了如何从 Secrets Manager 代理获取密钥。该示例依赖于文件中存在的 SSRF,该文件是安装脚本存储示例的位置。
import requests
import json
# 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=YOUR_SECRET_ID&refreshNow=true"
# 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 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}")
配置 Secrets Manager 代理
要更改 Secrets Manager 代理的配置,请创建一个 TOML 配置文件,然后调用 ./aws_secretsmanager_agent --config
config.toml。
配置选项
log_level
-
Secrets Manager 代理日志中报告的详细程度:DEBUG、INFO、WARN、ERROR 或 NONE。默认值为 INFO。
log_to_file
-
是否记录到文件或 stdout/stderr:true 或 false。默认值为 true。
http_port
-
本地 HTTP 服务器的端口,范围在 1024 到 65535 之间。默认值为 2773。
region
-
用于请求的 AWS 区域。如果未指定区域,则 Secrets Manager 代理会根据 SDK 确定区域。有关更多信息,请参阅《AWS SDK for Rust 开发人员指南》中的 Specify your credentials and default Region。
ttl_seconds
-
缓存项目的 TTL(以秒为单位),范围在 1 到 3600 之间。默认值为 300。0 表示没有缓存。
cache_size
-
缓存中可以存储的最大密钥数,范围为 1 至 1000。默认值为 1000。
ssrf_headers
-
Secrets Manager 代理检查 SSRF 令牌的标头名称列表。默认值为“X-Aws-Parameters-Secrets-Token, X-Vault-Token”。
ssrf_env_variables
-
Secrets Manager 代理按顺序检查 SSRF 令牌的环境变量名称列表。环境变量可以包含令牌或对令牌文件的引用,如下所示:AWS_TOKEN=file:///var/run/awssmatoken。默认值为“AWS_TOKEN, AWS_SESSION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN”。
path_prefix
-
用于确定请求是否为基于路径的请求的 URI 前缀。默认值为“/v1/”。
max_conn
-
Secrets Manager 代理允许的来自 HTTP 客户端的最大连接数,范围在 1 到 1000 之间。默认值为 800。
可选功能
通过将 --features 标志传递给 cargo build,可以使用可选功能构建 Secrets Manager 代理。可用功能如下:
生成功能
prefer-post-quantum
-
使 X25519MLKEM768 成为最高优先级的密钥交换算法。否则,该算法可用,但不是最高优先级。X25519MLKEM768 是一种混合的、后量子安全的密钥交换算法。
fips
-
将代理使用的密码套件限制为仅 FIPS 批准的密码。
日志记录
- 本地日志记录
-
Secrets Manager 代理会根据 log_to_file 配置变量在本地将错误记录到文件 logs/secrets_manager_agent.log 或 stdout/stderr 中。当应用程序调用 Secrets Manager 代理来获取密钥时,这些调用会显示在本地日志中。它们不会出现在 CloudTrail 日志中。
- 日志轮换
-
当文件达到 10 MB 时,Secrets Manager 代理会创建一个新的日志文件,并且总共最多存储五个日志文件。
- AWS 服务日志记录
-
日志不会转到 Secrets Manager、CloudTrail 或 CloudWatch。从 Secrets Manager 代理获取密钥的请求不会出现在这些日志中。当 Secrets Manager 代理调用 Secrets Manager 来获取密钥时,该调用会记录在 CloudTrail 中,并且有一个用户代理字符串包含 aws-secrets-manager-agent。
您可以在 配置 Secrets Manager 代理 中配置日志记录选项。
安全性注意事项
- 信任域
-
对于代理架构,信任域是代理终端节点和 SSRF 令牌可访问的位置,通常是整个主机。为了保持相同的安全状况,Secrets Manager 代理的信任域应与 Secrets Manager 凭证可用的域相匹配。例如,在 Amazon EC2 上,使用 Amazon EC2 角色时,Secrets Manager 代理的信任域将与凭证的域相同。
如果应用程序具有安全意识并且尚未使用将 Secrets Manager 凭证锁定到应用程序的代理解决方案,则应考虑使用特定于语言的 AWS SDK 或缓存解决方案。有关更多信息,请参阅获取密钥。