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

在 Lambda 函数中使用 Secrets Manager 密钥

Amazon Secrets Manager 可帮助您管理 Lambda 函数所需的凭证、API 密钥和其他密钥。您有两种主要方法可以在 Lambda 函数中检索密钥,与直接使用 Amazon SDK 检索密钥相比,这两种方法的性能更佳且成本更低:

  • Amazon 参数和密钥 Lambda 扩展 - 一种与运行时无关的解决方案,提供一个简单的 HTTP 接口来检索密钥

  • Powertools for Amazon Lambda 参数实用程序 - 支持多个提供商(Secrets Manager、Parameter Store、AppConfig)的代码集成解决方案,具有内置转换功能

这两种方法都维护密钥的本地缓存,从而无需您的函数在每次调用时都调用 Secrets Manager。当您的函数请求密钥时,将首先检查缓存。如果密钥可用且尚未过期,则会立即返回该密钥。否则,将从 Secrets Manager 中检索、缓存并返回密钥。这种缓存机制通过最大限度地减少 API 调用,可以缩短响应时间并降低成本。

选择方法

在扩展和 PowerTools 之间进行选择时,请考虑以下因素:

在以下情况下使用 Amazon 参数和密钥 Lambda 扩展:
  • 您需要一个与任何 Lambda 运行时兼容的、与运行时无关的解决方案

  • 您不想在函数中添加代码依赖项

  • 您只需从 Secrets Manager 或 Parameter Store 检索密钥

在以下情况下使用 Powertools for Amazon Lambda 参数实用程序:
  • 您想要获得与应用程序代码集成的开发体验

  • 您需要支持多个提供商(Secrets Manager、Parameter Store、AppConfig)

  • 您需要内置数据转换(JSON 解析、base64 解码)

  • 您正在使用 Python、TypeScript、Java 或 .NET 运行时

何时将 Secrets Manager 与 Lambda 结合使用

将 Secrets Manager 与 Lambda 结合使用的常见场景包括:

  • 存储您的函数用于连接到 Amazon RDS 或其他数据库的数据库凭证

  • 管理您的函数调用的外部服务的 API 密钥

  • 存储加密密钥或其他敏感配置数据

  • 自动轮换凭证,无需更新函数代码

使用 Amazon 参数和密钥 Lambda 扩展

Amazon 参数和密钥 Lambda 扩展使用与任何 Lambda 运行时兼容的简单 HTTP 接口。默认情况下,它会缓存密钥 300 秒(5 分钟),最多可容纳 1,000 个密钥。您可以使用环境变量自定义这些设置

在 Lambda 函数中使用 Secrets Manager

本节假设您已经拥有 Secrets Manager 密钥。要创建密钥,请参阅 Create an Amazon Secrets Manager secret

选择您的首选运行时,并按照步骤创建用于从 Secrets Manager 检索密钥的函数。该示例函数从 Secrets Manager 中检索密钥,并可用于访问数据库凭证、API 密钥或应用程序中的其他敏感配置数据。

Python
创建 Python 函数
  1. 创建并导航到新的项目目录。示例:

    mkdir my_function cd my_function
  2. 使用以下代码创建名为 lambda_function.py 的文件。对于 secret_name,使用您的密钥的名称或 Amazon 资源名称 (ARN)。

    import json import os import requests def lambda_handler(event, context): try: # Replace with the name or ARN of your secret secret_name = "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME" secrets_extension_endpoint = f"http://localhost:2773/secretsmanager/get?secretId={secret_name}" headers = {"X-Aws-Parameters-Secrets-Token": os.environ.get('AWS_SESSION_TOKEN')} response = requests.get(secrets_extension_endpoint, headers=headers) print(f"Response status code: {response.status_code}") secret = json.loads(response.text)["SecretString"] print(f"Retrieved secret: {secret}") return { 'statusCode': response.status_code, 'body': json.dumps({ 'message': 'Successfully retrieved secret', 'secretRetrieved': True }) } except Exception as e: print(f"Error: {str(e)}") return { 'statusCode': 500, 'body': json.dumps({ 'message': 'Error retrieving secret', 'error': str(e) }) }
  3. 使用此内容创建名为 requirements.txt 的文件:

    requests
  4. 安装依赖项:

    pip install -r requirements.txt -t .
  5. 创建包含所有文件的 .zip 文件:

    zip -r function.zip .
Node.js
创建 Node.js 函数
  1. 创建并导航到新的项目目录。示例:

    mkdir my_function cd my_function
  2. 使用以下代码创建名为 index.mjs 的文件。对于 secret_name,使用您的密钥的名称或 Amazon 资源名称 (ARN)。

    import http from 'http'; export const handler = async (event) => { try { // Replace with the name or ARN of your secret const secretName = "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME"; const options = { hostname: 'localhost', port: 2773, path: `/secretsmanager/get?secretId=${secretName}`, headers: { 'X-Aws-Parameters-Secrets-Token': process.env.AWS_SESSION_TOKEN } }; const response = await new Promise((resolve, reject) => { http.get(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { resolve({ statusCode: res.statusCode, body: data }); }); }).on('error', reject); }); const secret = JSON.parse(response.body).SecretString; console.log('Retrieved secret:', secret); return { statusCode: response.statusCode, body: JSON.stringify({ message: 'Successfully retrieved secret', secretRetrieved: true }) }; } catch (error) { console.error('Error:', error); return { statusCode: 500, body: JSON.stringify({ message: 'Error retrieving secret', error: error.message }) }; } };
  3. 创建包含 index.mjs 文件的 .zip 文件:

    zip -r function.zip index.mjs
Java
创建 Java 函数
  1. 创建 Maven 项目:

    mvn archetype:generate \ -DgroupId=example \ -DartifactId=lambda-secrets-demo \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.4 \ -DinteractiveMode=false
  2. 导航到项目目录:

    cd lambda-secrets-demo
  3. 打开 pom.xml 并将内容替换为以下内容:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>example</groupId> <artifactId>lambda-secrets-demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> <version>1.2.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <finalName>function</finalName> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
  4. /lambda-secrets-demo/src/main/java/example/App.java 重命名为 Hello.java,以匹配 Lambda 的默认 Java 处理程序名称 (example.Hello::handleRequest):

    mv src/main/java/example/App.java src/main/java/example/Hello.java
  5. 打开 Hello.java 文件并将其内容替换为以下内容。对于 secretName,使用您的密钥的名称或 Amazon 资源名称 (ARN)。

    package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class Hello implements RequestHandler<Object, String> { private final HttpClient client = HttpClient.newHttpClient(); @Override public String handleRequest(Object input, Context context) { try { // Replace with the name or ARN of your secret String secretName = "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME"; String endpoint = "http://localhost:2773/secretsmanager/get?secretId=" + secretName; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(endpoint)) .header("X-Aws-Parameters-Secrets-Token", System.getenv("AWS_SESSION_TOKEN")) .GET() .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); String secret = response.body(); secret = secret.substring(secret.indexOf("SecretString") + 15); secret = secret.substring(0, secret.indexOf("\"")); System.out.println("Retrieved secret: " + secret); return String.format( "{\"statusCode\": %d, \"body\": \"%s\"}", response.statusCode(), "Successfully retrieved secret" ); } catch (Exception e) { e.printStackTrace(); return String.format( "{\"body\": \"Error retrieving secret: %s\"}", e.getMessage() ); } } }
  6. 删除测试目录。Maven 默认创建此项,但在本例中我们不需要它。

    rm -rf src/test
  7. 构建项目:

    mvn package
  8. 下载 JAR 文件 (target/function.jar) 以供稍后使用。

  1. 打开 Lamba 控制台的 Functions page(函数页面)。

  2. 选择 Create function(创建函数)。

  3. 选择从头开始编写

  4. 对于函数名称,请输入 secret-retrieval-demo

  5. 选择您的首选运行时

  6. 选择创建函数

上传部署包
  1. 在函数的代码选项卡中,选择上传自,然后选择 .zip 文件(对于 Python 和 Node.js)或 .jar 文件(对于 Java)。

  2. 上传您之前创建的部署包。

  3. 选择保存

将 Amazon 参数和密钥 Lambda 扩展添加为层
  1. 在函数的代码选项卡中,向下滚动到

  2. 选择 Add a layer

  3. 选择 Amazon 层

  4. 选择 Amazon-Parameters-and-Secrets-Lambda-Extension

  5. 选择最新版本。

  6. 选择添加

向您的执行角色添加 Secrets Manager 权限
  1. 选择 Configuration(配置)选项卡,然后选择 Permissions(权限)。

  2. 角色名称下,选择至执行角色的链接。此角色将在 IAM 控制台中打开角色。

    至执行角色的链接
  3. 选择添加权限,然后选择创建内联策略

    在 IAM 控制台中附加策略
  4. 选择 JSON 选项卡,然后添加以下策略。对于 Resource,输入您的密钥的 ARN。

    JSON
    { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME" } ] }
  5. 选择下一步

  6. 输入策略的名称。

  7. 选择创建策略

测试此函数
  1. 返回 Lambda 控制台。

  2. 选择测试选项卡。

  3. 选择测试。您应看到以下响应:

    成功测试结果

环境变量

Amazon 参数和密钥 Lambda 扩展使用下面的默认设置。您可以通过创建相应的环境变量来覆盖这些设置。要查看函数的当前设置,请将 PARAMETERS_SECRETS_EXTENSION_LOG_LEVEL 设置为 DEBUG。该扩展将在每次函数调用开始时将其配置信息记录到 CloudWatch Logs 中。

设置 默认值 有效值 环境变量 详细信息
HTTP 端口 2773 1-65535 PARAMETERS_SECRETS_EXTENSION_HTTP_PORT 本地 HTTP 服务器的端口
已启用缓存 TRUE TRUE | FALSE PARAMETERS_SECRETS_EXTENSION_CACHE_ENABLED 启用或禁用缓存
缓存大小 1000 0 - 1000 PARAMETERS_SECRETS_EXTENSION_CACHE_SIZE 设置为 0 以禁用缓存
Secrets Manager TTL 300 秒 0 - 300 秒 SECRETS_MANAGER_TTL 缓存密钥的生存时间。设置为 0 以禁用缓存。如果 PARAMETERS_SECRETS_EXTENSION_CACHE_SIZE 的值为 0,则忽略此变量。
Parameter Store TTL 300 秒 0 - 300 秒 SSM_PARAMETER_STORE_TTL 缓存参数的生存时间。设置为 0 以禁用缓存。如果 PARAMETERS_SECRETS_EXTENSION_CACHE_SIZE 的值为 0,则忽略此变量。
日志级别 INFO 调试 | 信息 | 警告 | 错误 | 无 PARAMETERS_SECRETS_EXTENSION_LOG_LEVEL 扩展日志中报告的详细信息级别
最大连接数 3 1 或更多 PARAMETERS_SECRETS_EXTENSION_MAX_CONNECTIONS 对 Parameter Store 或 Secrets Manager 的请求的最大 HTTP 连接数
Secrets Manager 超时 0(无超时) 所有整数 SECRETS_MANAGER_TIMEOUT_MILLIS Secrets Manager 的请求超时(以毫秒为单位)
Parameter Store 超时 0(无超时) 所有整数 SSM_PARAMETER_STORE_TIMEOUT_MILLIS Parameter Store 的请求超时(以毫秒为单位)

使用密钥轮换

如果频繁轮换密钥,则默认 300 秒缓存持续时间可能会导致您的函数使用过时的密钥。您有两种方法可以确保函数使用最新的密钥值:

  • 通过将 SECRETS_MANAGER_TTL 环境变量设置为较低的值(以秒为单位)来缩短缓存 TTL。例如,将其设置为 60 可确保函数永远不会使用超过一分钟的密钥。

  • 在密钥请求中使用 AWSCURRENTAWSPREVIOUS 暂存标签来确保获得所需的特定版本:

    secretsmanager/get?secretId=YOUR_SECRET_NAME&versionStage=AWSCURRENT

选择最能平衡您对性能和新鲜度需求的方法。较低 TTL 意味着对 Secrets Manager 的调用更频繁,但可确保您使用最新的密钥值。

使用 Powertools for Amazon Lambda 中的参数实用程序

Powertools for Amazon Lambda 中的参数实用程序提供了一个统一的界面,用于从多个提供商(包括 Secrets Manager、Parameter Store 和 AppConfig)检索密钥。它处理缓存、转换,并提供与扩展方法相比更加集成的开发体验。

参数实用程序的好处

  • 多个提供商 - 使用相同的接口从 Secrets Manager、Parameter Store 和 AppConfig 检索参数

  • 内置转换 - 自动 JSON 解析、base64 解码和其他数据转换

  • 集成缓存 - 支持 TTL 的可配置缓存,可减少 API 调用

  • 键入安全 - 在 TypeScript 和其他支持的运行时中提供强大的键入支持

  • 错误处理 - 内置重试逻辑和错误处理

代码示例

以下示例展示了如何在不同的运行时使用参数实用程序检索密钥:

Python
注意

有关完整示例和设置说明,请参阅参数实用程序文档

使用 Powertools for Amazon Lambda 参数实用程序从 Secrets Manager 中检索密钥。

from aws_lambda_powertools import Logger from aws_lambda_powertools.utilities import parameters logger = Logger() def lambda_handler(event, context): try: # Get secret with caching (default TTL: 5 seconds) secret_value = parameters.get_secret("my-secret-name") # Get secret with custom TTL secret_with_ttl = parameters.get_secret("my-secret-name", max_age=300) # Get secret and transform JSON secret_json = parameters.get_secret("my-json-secret", transform="json") logger.info("Successfully retrieved secrets") return { 'statusCode': 200, 'body': 'Successfully retrieved secrets' } except Exception as e: logger.error(f"Error retrieving secret: {str(e)}") return { 'statusCode': 500, 'body': f'Error: {str(e)}' }
TypeScript
注意

有关完整示例和设置说明,请参阅参数实用程序文档

使用 Powertools for Amazon Lambda 参数实用程序从 Secrets Manager 中检索密钥。

import { Logger } from '@aws-lambda-powertools/logger'; import { getSecret } from '@aws-lambda-powertools/parameters/secrets'; import type { Context } from 'aws-lambda'; const logger = new Logger(); export const handler = async (event: any, context: Context) => { try { // Get secret with caching (default TTL: 5 seconds) const secretValue = await getSecret('my-secret-name'); // Get secret with custom TTL const secretWithTtl = await getSecret('my-secret-name', { maxAge: 300 }); // Get secret and transform JSON const secretJson = await getSecret('my-json-secret', { transform: 'json' }); logger.info('Successfully retrieved secrets'); return { statusCode: 200, body: 'Successfully retrieved secrets' }; } catch (error) { logger.error('Error retrieving secret', { error }); return { statusCode: 500, body: `Error: ${error}` }; } };
Java
注意

有关完整示例和设置说明,请参阅参数实用程序文档

使用 Powertools for Amazon Lambda 参数实用程序从 Secrets Manager 中检索密钥。

import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.parameters.SecretsProvider; import software.amazon.lambda.powertools.parameters.ParamManager; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; public class SecretHandler implements RequestHandler<Object, String> { private final SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); @Logging @Override public String handleRequest(Object input, Context context) { try { // Get secret with caching (default TTL: 5 seconds) String secretValue = secretsProvider.get("my-secret-name"); // Get secret with custom TTL (300 seconds) String secretWithTtl = secretsProvider.withMaxAge(300).get("my-secret-name"); // Get secret and transform JSON MySecret secretJson = secretsProvider.get("my-json-secret", MySecret.class); return "Successfully retrieved secrets"; } catch (Exception e) { return "Error retrieving secret: " + e.getMessage(); } } public static class MySecret { // Define your secret structure here } }
.NET
注意

有关完整示例和设置说明,请参阅参数实用程序文档

使用 Powertools for Amazon Lambda 参数实用程序从 Secrets Manager 中检索密钥。

using AWS.Lambda.Powertools.Logging; using AWS.Lambda.Powertools.Parameters; using Amazon.Lambda.Core; [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] public class Function { private readonly ISecretsProvider _secretsProvider; public Function() { _secretsProvider = ParametersManager.SecretsProvider; } [Logging] public async Task<string> FunctionHandler(object input, ILambdaContext context) { try { // Get secret with caching (default TTL: 5 seconds) var secretValue = await _secretsProvider.GetAsync("my-secret-name"); // Get secret with custom TTL var secretWithTtl = await _secretsProvider.WithMaxAge(TimeSpan.FromMinutes(5)) .GetAsync("my-secret-name"); // Get secret and transform JSON var secretJson = await _secretsProvider.GetAsync<MySecret>("my-json-secret"); return "Successfully retrieved secrets"; } catch (Exception e) { return $"Error retrieving secret: {e.Message}"; } } public class MySecret { // Define your secret structure here } }

设置和权限

要使用参数实用程序,您需要:

  1. 为您的运行时安装 Powertools for Amazon Lambda。有关更多信息,请参阅 Powertools for Amazon Lambda

  2. 向您的函数的执行角色添加必要的 IAM 权限。有关详细信息,请参阅在 Amazon Lambda 中管理权限

  3. 通过环境变量配置任何可选设置。

所需的 IAM 权限与扩展方法相同。该实用程序将根据您的配置自动处理缓存和对 Secrets Manager 的 API 调用。