使用 Amazon Secrets Manager 代理 - Amazon Secrets Manager
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

使用 Amazon Secrets Manager 代理

Secrets Manager 代理的工作原理

Amazon Secrets Manager 代理是一项客户端 HTTP 服务,可帮助您标准化在计算环境中使用来自 Secrets Manager 的密钥的方式。您可以将以下服务与该密钥一起使用:

  • Amazon Lambda

  • Amazon Elastic Container Service

  • Amazon Elastic Kubernetes Service

  • Amazon Elastic Compute Cloud

Secrets Manager 代理检索密钥并将其缓存在内存中,从而可让您的应用程序从本地主机获取密钥,而不必直接调用 Secrets Manager。Secrets Manager 代理只能读取密钥,而无法对其进行修改。

重要

Secrets Manager 代理使用来自您环境的 Amazon 凭证来调用 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 功能的代理需要以下解决方法:

  • 创建名为 SDKROOT 的环境变量,该变量设置为运行 xcrun --show-sdk-path 的结果

RPM-based systems
在基于 RPM 的系统上构建
  1. 使用存储库中提供的 install 脚本。

    该脚本在启动时生成一个随机的 SSRF 令牌并将其存储在文件 /var/run/awssmatoken 中。安装脚本创建的 awssmatokenreader 组可以读取该令牌。

  2. 要允许您的应用程序读取令牌文件,您需要将应用程序在其下运行的用户账户添加到 awssmatokenreader 组。例如,您可以使用以下 usermod 命令授予应用程序读取令牌文件的权限,其中 <APP_USER> 是应用程序在其下运行的用户 ID。

    sudo usermod -aG awssmatokenreader <APP_USER>
    安装开发工具

    在基于 RPM 的系统(如 AL2023)上,安装“开发工具”组:

    sudo yum -y groupinstall "Development Tools"
  3. 安装 Rust

    按照 Rust 文档安装 Rust 的说明进行操作:

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Follow the on-screen instructions . "$HOME/.cargo/env"
  4. 构建代理

    使用 cargo build 命令构建 Secrets Manager 代理:

    cargo build --release

    您将在 target/release/aws_secretsmanager_agent 下找到可执行文件。

Debian-based systems
在基于 Debian 的系统上构建
  1. 安装开发工具

    在基于 Debian 的系统(例如 Ubuntu)上,安装 build-essential 包:

    sudo apt install build-essential
  2. 安装 Rust

    按照 Rust 文档安装 Rust 的说明进行操作:

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Follow the on-screen instructions . "$HOME/.cargo/env"
  3. 构建代理

    使用 cargo build 命令构建 Secrets Manager 代理:

    cargo build --release

    您将在 target/release/aws_secretsmanager_agent 下找到可执行文件。

Windows
在 Windows 上构建
  1. 设置开发环境

    按照 Microsoft Windows 文档中的 在 Windows 上针对 Rust 设置开发环境中的说明进行操作。

  2. 构建代理

    使用 cargo build 命令构建 Secrets Manager 代理:

    cargo build --release

    您将在 target/release/aws_secretsmanager_agent.exe 下找到可执行文件。

Cross-compile natively
本机交叉编译
  1. 安装交叉编译工具

    在 mingw-w64 包可用的发行版(例如 Ubuntu)上,安装交叉编译工具链:

    # Install the cross compile tool chain sudo add-apt-repository universe sudo apt install -y mingw-w64
  2. 添加 Rust 构建目标

    安装 Windows GNU 编译目标:

    rustup target add x86_64-pc-windows-gnu
  3. 针对 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

重要

我们建议为构建环境提供 32GB 的磁盘空间。

  1. 设置 Docker

    安装和配置 Docker:

    # Install and start docker sudo yum -y install docker sudo systemctl start docker sudo systemctl enable docker # Make docker start after reboot
  2. 配置 Docker 权限

    将用户添加到 Docker 组:

    # Give ourselves permission to run the docker images without sudo sudo usermod -aG docker $USER newgrp docker
  3. 针对 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 代理
  1. 导航到配置目录

    更改到配置目录:

    cd aws_secretsmanager_agent/configuration
  2. 运行安装脚本

    运行存储库中提供的 install 脚本。

    该脚本在启动时生成一个随机的 SSRF 令牌并将其存储在文件 /var/run/awssmatoken 中。安装脚本创建的 awssmatokenreader 组可以读取该令牌。

  3. 配置应用程序权限

    将运行应用程序的用户账户添加到 awssmatokenreader 组中:

    sudo usermod -aG awssmatokenreader APP_USER

    APP_USER 替换为运行应用程序时使用的用户 ID。

Container Sidecar

您可以使用 Docker 将 Secrets Manager 代理作为附加容器与应用程序一起运行。然后,您的应用程序可以从 Secrets Manager 代理提供的本地 HTTP 服务器检索密钥。有关 Docker 的信息,请参阅 Docker 文档

创建用于 Secrets Manager 代理的附加容器
  1. 创建代理 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"]
  2. 创建应用程序 Dockerfile

    为您的客户端应用程序创建一个 Dockerfile。

  3. 创建 Docker Compose 文件

    创建 Docker Compose 文件来运行具有共享网络接口的两个容器:

    重要

    您必须加载 Amazon 凭证和 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
  4. 复制代理二进制文件

    secrets-manager-agent 二进制文件复制到包含您的 Dockerfile 和 Docker Compose 文件的同一个目录中。

  5. 构建并运行容器

    使用 Docker Compose 构建并运行容器:

    docker-compose up --build
  6. 后续步骤

    在您的客户端容器中,您现在可使用 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 扩展
  1. 打包代理层

    从 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')
  2. 配置 SSRF 令牌

    代理的默认配置会自动将 SSRF 令牌设置为在预设 AWS_SESSION_TOKENAWS_CONTAINER_AUTHORIZATION_TOKEN 环境变量中设置的值(后一个变量适用于启用 SnapStart 的 Lambda 函数)。或者,您可以改用自己的 Lambda 函数的任意值定义 AWS_TOKEN 环境变量,因为该变量优先于其他两个变量。如果您选择使用 AWS_TOKEN 环境变量,则必须通过 lambda:UpdateFunctionConfiguration 调用来设置该环境变量。

  3. 将层附加到函数

    将层版本附加到 Lambda 函数:

    # Attach the layer version to the Lambda function aws lambda update-function-configuration \ --function-name $LAMBDA_ARN \ --layers "$LAYER_VERSION_ARN"
  4. 更新函数代码

    更新您的 Lambda 函数以使用 X-Aws-codes-Secrets-Token 标头值(设置为来自上述环境变量之一的 SSRF 令牌值)查询 http://localhost:2773/secretsmanager/get?secretId=MyTest,从而检索密钥。务必在应用程序代码中实现重试逻辑,以适应 Lambda 扩展初始化和注册中的延迟。

  5. 测试此函数

    调用 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 的 Amazon SDK,该 SDK 使用 Amazon 凭证提供程序链。这些 IAM 凭证的身份决定了 Secrets Manager 代理检索密钥的权限。

  • secretsmanager:DescribeSecret

  • secretsmanager:GetSecretValue

有关权限的更多信息,请参阅 Amazon 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 的行为
  • 完全绕过缓存

  • 直接从 Secrets Manager 中检索最新的密钥值

  • 使用新值更新缓存并重置 TTL

  • 确保您始终获得最新的密钥值

强制刷新密钥值

重要

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:truefalse。默认值为 true

http_port

本地 HTTP 服务器的端口,范围在 1024 到 65535 之间。默认值为 2773。

region

用于请求的 Amazon 区域。如果未指定区域,则 Secrets Manager 代理会根据 SDK 确定区域。有关更多信息,请参阅《Amazon 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 代理会创建一个新的日志文件,并且总共最多存储五个日志文件。

Amazon 服务日志记录

日志不会转到 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 凭证锁定到应用程序的代理解决方案,则应考虑使用特定于语言的 Amazon SDK 或缓存解决方案。有关更多信息,请参阅获取密钥