在 Lambda 函數中使用 Secrets Manager 秘密 - AWS Lambda

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

在 Lambda 函數中使用 Secrets Manager 秘密

AWS Secrets Manager 可協助您管理 Lambda 函數所需的登入資料、API 金鑰和其他秘密。與直接使用 AWS SDK 擷取秘密相比,您有兩種主要方法來擷取 Lambda 函數中的秘密,同時提供更好的效能和更低的成本:

  • AWS 參數和秘密 Lambda 延伸模組 - 與執行時間無關的解決方案,提供簡單的 HTTP 介面以擷取秘密

  • 適用於 AWS Lambda 參數公用程式的 Powertools - 程式碼整合解決方案,支援具有內建轉換的多個提供者 (Secrets Manager、參數存放區、AppConfig)

這兩種方法都會維護秘密的本機快取,因此不需要您的函數在每次叫用時呼叫 Secrets Manager。當您的函數請求秘密時,會先檢查快取。如果秘密可用且尚未過期,則會立即傳回。否則,它會從 Secrets Manager 擷取、快取和傳回。此快取機制可縮短回應時間,並將 API 呼叫降至最低,進而降低成本。

選擇方法

在擴充功能和 PowerTools 之間進行選擇時,請考慮下列因素:

在下列情況下使用 AWS 參數和秘密 Lambda 延伸模組:
  • 您想要一個與執行時間無關的解決方案,可與任何 Lambda 執行時間搭配使用

  • 您不想將程式碼相依性新增至函數

  • 您只需要從 Secrets Manager 或 參數存放區擷取秘密

在下列情況下,使用 Powertools 做為 AWS Lambda 參數公用程式:
  • 您需要與應用程式程式碼整合的開發體驗

  • 您需要支援多個提供者 (Secrets Manager、Parameter Store、AppConfig)

  • 您想要內建的資料轉換 (JSON 剖析、Base64 解碼)

  • 您使用 Python、TypeScript、Java 或 .NET 執行時間

何時搭配 Lambda 使用 Secrets Manager

搭配 Lambda 使用 Secrets Manager 的常見案例包括:

  • 存放 函數用來連線至 Amazon RDS 或其他資料庫的資料庫登入資料

  • 管理函數呼叫之外部服務的 API 金鑰

  • 儲存加密金鑰或其他敏感組態資料

  • 自動輪換登入資料,而不需要更新函數程式碼

使用 AWS 參數和秘密 Lambda 延伸模組

這些 AWS 參數和秘密 Lambda 延伸模組使用與任何 Lambda 執行時間相容的簡單 HTTP 介面。根據預設,它會快取秘密 300 秒 (5 分鐘),最多可保留 1,000 個秘密。您可以使用環境變數自訂這些設定

在 Lambda 函數中使用 Secrets Manager

本節假設您已擁有 Secrets Manager 秘密。若要建立秘密,請參閱建立 AWS Secrets Manager 秘密

選擇您偏好的執行時間,並依照步驟建立從 Secrets Manager 擷取秘密的函數。範例函數會從 Secrets Manager 擷取秘密,並可用來存取應用程式中的資料庫登入資料、API 金鑰或其他敏感組態資料。

Python
若要建立 Python 函數
  1. 建立並導覽至新的專案目錄。範例:

    mkdir my_function cd my_function
  2. lambda_function.py 使用下列程式碼建立名為 的檔案。對於 secret_name,請使用秘密的名稱或 Amazon Resource Name (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 Resource Name (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. 建立包含 檔案的 .zip index.mjs 檔案:

    zip -r function.zip index.mjs
Java
若要建立 Lambda 函數
  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.javaHello.java,以符合 Lambda 的預設 Java 處理常式名稱 (example.Hello::handleRequest):

    mv src/main/java/example/App.java src/main/java/example/Hello.java
  5. 開啟 Hello.java 檔案,並以下列內容取代其內容。對於 secretName,請使用秘密的名稱或 Amazon Resource Name (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. 開啟 Lambda 主控台中的 函數頁面

  2. 選擇建立函數

  3. 選取從頭開始撰寫

  4. 針對函數名稱,請輸入 secret-retrieval-demo

  5. 選擇您偏好的執行時間

  6. 選擇 Create function (建立函數)

上傳部署套件
  1. 在函數的程式碼索引標籤中,選擇從 上傳,然後選取 .zip 檔案 (適用於 Python 和 Node.js) 或 .jar 檔案 (適用於 Java)。

  2. 上傳您先前建立的部署套件。

  3. 選擇儲存

新增 AWS Parameters and Secrets Lambda 延伸做為 layer
  1. 在函數的程式碼索引標籤中,向下捲動至圖層

  2. 選擇 Add a layer (新增 layer)

  3. 選取AWS 圖層

  4. 選擇 AWS-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. 選擇測試。您應該會看到下列回應:

    成功的測試結果

環境變數

AWS Parameters and Secrets 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 快取秘密的Time-to-live。設定為 0 以停用快取。如果 PARAMETERS_SECRETS_EXTENSION_CACHE_SIZE 的值為 0,則會忽略此變數。
參數存放區 TTL 300 秒 0 - 300 秒 SSM_PARAMETER_STORE_TTL 快取參數的Time-to-live。設定為 0 以停用快取。如果 PARAMETERS_SECRETS_EXTENSION_CACHE_SIZE 的值為 0,則會忽略此變數。
日誌層級 INFO 除錯 | 資訊 | 警告 | 錯誤 | 無 PARAMETERS_SECRETS_EXTENSION_LOG_LEVEL 擴充功能日誌中報告的詳細資訊層級
最大連線數 3 1 或以上 PARAMETERS_SECRETS_EXTENSION_MAX_CONNECTIONS 參數存放區或 Secrets Manager 請求的 HTTP 連線數目上限
Secrets Manager 逾時 0 (無逾時) 所有整數 SECRETS_MANAGER_TIMEOUT_MILLIS Secrets Manager 請求逾時 (以毫秒為單位)
參數存放區逾時 0 (無逾時) 所有整數 SSM_PARAMETER_STORE_TIMEOUT_MILLIS 參數存放區的請求逾時 (以毫秒為單位)

使用秘密輪換

如果您經常輪換秘密,預設的 300 秒快取持續時間可能會導致函數使用過時的秘密。您有兩個選項可確保函數使用最新的秘密值:

  • SECRETS_MANAGER_TTL環境變數設定為較低的值 (以秒為單位),以減少快取 TTL。例如,將其設定為 60 可確保您的函數永遠不會使用超過一分鐘的秘密。

  • 在秘密請求中使用 AWSPREVIOUS AWSCURRENT或 預備標籤,以確保您取得所需的特定版本:

    secretsmanager/get?secretId=YOUR_SECRET_NAME&versionStage=AWSCURRENT

選擇最能平衡效能和新鮮度需求的方法。較低的 TTL 表示更頻繁地呼叫 Secrets Manager,但確保您使用的是最新的秘密值。

從 Powertools for 使用參數公用程式 AWS Lambda

Powertools for 的參數公用程式 AWS Lambda 提供統一界面,用於從多個提供者擷取秘密,包括 Secrets Manager、參數存放區和 AppConfig。它可處理快取、轉換,並提供比延伸方法更整合的開發體驗。

參數公用程式的優點

  • 多個提供者 - 使用相同的界面從 Secrets Manager、參數存放區和 AppConfig 擷取參數

  • 內建轉換 - 自動 JSON 剖析、Base64 解碼和其他資料轉換

  • 整合式快取 - 具有 TTL 支援的可設定快取,以減少 API 呼叫

  • 類型安全 - TypeScript 和其他支援執行時間的強式輸入支援

  • 錯誤處理 - 內建重試邏輯和錯誤處理

程式碼範例

下列範例示範如何在不同的執行時間中使用 Parameters 公用程式擷取秘密:

Python
注意

如需完整範例和設定說明,請參閱參數公用程式文件

使用 Powertools for AWS Lambda Parameters 公用程式從 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 AWS Lambda Parameters 公用程式從 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 AWS Lambda Parameters 公用程式從 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 AWS Lambda Parameters 公用程式從 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. AWS Lambda 為您的執行時間安裝適用於 的 Powertools。如需詳細資訊,請參閱AWS Lambda 的 Powertools

  2. 將必要的 IAM 許可新增至函數的執行角色。請參閱 在 中管理許可 AWS Lambda 以取得詳細資訊。

  3. 透過環境變數設定任何選用設定。

所需的 IAM 許可與延伸方法相同。公用程式會根據您的組態,自動處理對 Secrets Manager 的快取和 API 呼叫。