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

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

在 Lambda 函數中使用 Secrets Manager 秘密

AWS Secrets Manager 可協助您管理 Lambda 函數所需的登入資料、API 金鑰和其他秘密。我們建議您使用 AWS Parameters and Secrets Lambda 延伸模組來擷取 Lambda 函數中的秘密。相較於直接使用 AWS SDK 擷取秘密, 擴充功能可提供更好的效能並降低成本。

AWS Parameters and Secrets Lambda 延伸模組會維護秘密的本機快取,因此您的函數不需要在每次叫用時呼叫 Secrets Manager。當您的函數請求秘密時,延伸模組會先檢查其快取。如果秘密可用且尚未過期,則會立即傳回。否則,延伸模組會從 Secrets Manager 擷取它、快取它,然後將它傳回給您的函數。此快取機制可減少對 Secrets Manager 的 API 呼叫,進而加快回應時間並降低成本。

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

何時搭配 Lambda 使用 Secrets Manager

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

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

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

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

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

在 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,但確保您使用的是最新的秘密值。