Lambda 함수에서 Secrets Manager 보안 암호 사용 - AWS Lambda

Lambda 함수에서 Secrets Manager 보안 암호 사용

AWS Secrets Manager를 사용하면 Lambda 함수에 필요한 자격 증명, API 키 및 기타 보안 암호를 관리할 수 있습니다. Lambda 함수에서 보안 암호 검색에 사용하는 두 가지 접근 방식이 있으며, 둘 다 AWS SDK를 사용하여 직접 보안 암호를 검색하는 것에 비해 성능이 향상되고 비용이 절감됩니다.

  • AWS 파라미터 및 보안 암호 Lambda 확장 - 보안 암호 검색용으로 간단한 HTTP 인터페이스를 제공하는 런타임에 구애받지 않는 솔루션

  • Powertools for AWS Lambda 파라미터 유틸리티 - 내장 변환을 통해 여러 공급자(Secrets Manager, Parameter Store, AppConfig)를 지원하는 코드 통합 솔루션

두 접근 방식 모두 보안 암호의 로컬 캐시를 유지하므로 모든 간접 호출에 대해 함수가 Secrets Manager를 직접 호출하도록 만들 필요가 없습니다. 함수가 보안 암호를 요청하면 먼저 캐시를 확인합니다. 보안 암호가 사용 가능하고 만료되지 않은 경우 즉시 반환됩니다. 캐시에 없으면 Secrets Manager에서 검색하여 캐시되고 반환됩니다. 이 캐싱 메커니즘은 API 직접 호출을 최소화하여 응답 시간을 단축하고 비용을 절감합니다.

접근 방식 선택

다음 요소를 고려하여 확장과 PowerTools 중에서 선택하세요.

AWS 파라미터 및 보안 암호 Lambda 확장 사용이 적합한 상황:
  • Lambda 런타임에서 작동하는 런타임에 구애받지 않는 솔루션을 원하는 경우

  • 함수에 코드 종속성을 추가하지 않으려는 경우

  • Secrets Manager 또는 파라미터 스토어에서만 보안 암호를 검색하는 경우

Powertools for 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 리소스 이름(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의 기본 Java 핸들러 이름(example.Hello::handleRequest)과 일치하도록 /lambda-secrets-demo/src/main/java/example/App.java의 이름을 Hello.java로 바꿉니다.

    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. Lambda 콘솔의 함수 페이지를 엽니다.

  2. 함수 생성을 선택합니다.

  3. 새로 작성을 선택합니다.

  4. [함수 이름]에 secret-retrieval-demo을 입력합니다.

  5. 원하는 런타임을 선택합니다.

  6. 함수 생성을 선택합니다.

배포 패키지를 업로드하려면 다음을 수행하세요.
  1. 함수의 코드 탭에서 에서 업로드를 선택하고 .zip 파일(Python 및 Node.js의 경우) 또는 .jar 파일(Java의 경우)을 선택합니다.

  2. 이전에 생성한 배포 패키지를 업로드합니다.

  3. 저장을 선택합니다.

AWS 파라미터 및 보안 암호 Lambda 확장을 계층으로 추가하려면 다음을 수행하세요.
  1. 함수의 코드 탭에서 계층까지 아래로 스크롤합니다.

  2. [계층 추가]를 선택합니다.

  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_LEVELDEBUG로 설정합니다. 확장은 각 함수 간접 호출이 시작될 때 CloudWatch Logs에 구성 정보를 로깅합니다.

설정 기본값 유효값 환경 변수 세부 사항
HTTP 포트 2773 1~65535 PARAMETERS_SECRETS_EXTENSION_HTTP_PORT 로컬 HTTP 서버의 포트
캐시 활성화됨 TRUE TRUE | FALSE PARAMETERS_SECRETS_EXTENSION_CACHE_ENABLED 캐시 활성화 또는 비활성화
캐시 크기 1000 0~1,000 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인 경우 이 변수는 무시됩니다.
Parameter Store TTL 300초 0~300초 SSM_PARAMETER_STORE_TTL 캐시된 파라미터의 time-to-live. 캐싱을 비활성화하려면 0으로 설정. PARAMETERS_SECRETS_EXTENSION_CACHE_SIZE 값이 0인 경우 이 변수는 무시됩니다.
로그 수준 INFO DEBUG | INFO | WARN | ERROR | NONE 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에 대한 요청 제한 시간(밀리초)
파라미터 스토어 제한 시간 0(제한 시간 없음) 모든 정수 SSM_PARAMETER_STORE_TIMEOUT_MILLIS Parameter Store에 대한 요청 제한 시간(밀리초)

보안 암호 교체 작업

보안 암호를 자주 교체하는 경우 기본 300초 캐시 기간으로 인해 함수가 오래된 보안 암호를 사용할 수 있습니다. 함수가 최신 보안 암호 값을 사용하도록 하는 두 가지 옵션이 있습니다.

  • SECRETS_MANAGER_TTL 환경 변수를 더 낮은 값(초)으로 설정하여 캐시 TTL을 줄입니다. 예를 들어, 60으로 설정하면 함수가 1분 이상 지난 보안 암호를 사용하지 않습니다.

  • 보안 암호 요청에서 AWSCURRENT 또는 AWSPREVIOUS 스테이징 레이블을 사용하여 원하는 특정 버전을 가져오도록 합니다.

    secretsmanager/get?secretId=YOUR_SECRET_NAME&versionStage=AWSCURRENT

성능과 최신 상태에 대한 요구 사항의 균형을 가장 잘 맞출 수 있는 접근 방식을 선택하세요. TTL이 낮을수록 Secrets Manager를 더 자주 직접적으로 호출하지만 가장 최신의 보안 암호 값으로 작업할 수 있습니다.

Powertools for AWS Lambda 파라미터 유틸리티 사용

Powertools for AWS Lambda의 파라미터 유틸리티는 Secrets Manager, 파라미터 스토어 및 AppConfig를 포함한 여러 공급자에서 보안 암호를 검색하기 위한 통합 인터페이스를 제공합니다. 캐싱, 변환을 처리하고 확장 접근 방식에 비해 더 통합된 개발 경험을 제공합니다.

파라미터 유틸리티의 이점

  • 다양한 공급자 - Secrets Manager, Parameter Store 및 AppConfig에서 동일한 인터페이스를 사용하여 파라미터 검색

  • 내장 변환 - 자동 JSON 구문 분석, base64 디코딩 및 기타 데이터 변환

  • 통합 캐싱 - TTL 지원을 통해 구성 가능한 캐싱으로 API 직접 호출을 줄임

  • 유형 안전 - TypeScript 및 기타 지원되는 런타임에서 강력한 유형 지정 지원

  • 오류 처리 - 재시도 로직 및 오류 처리 내장

코드 예제

다음 예제에서는 다양한 런타임에서 파라미터 유틸리티를 사용하여 보안 암호를 검색하는 방법을 보여줍니다.

Python
참고

전체 예제 및 설정 지침은 파라미터 유틸리티 설명서를 참조하세요.

Powertools for AWS 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 AWS 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 AWS 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 AWS 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 AWS Lambda를 설치합니다. 자세한 내용은 AWS Lambda용 Powertools을 참조하세요.

  2. 함수의 실행 역할에 필요한 IAM 권한을 추가합니다. 자세한 내용은 AWS Lambda에서 권한 관리 섹션을 참조하세요.

  3. 환경 변수를 통해 선택적 설정을 구성합니다.

필요한 IAM 권한은 확장 접근 방식과 동일합니다. 유틸리티는 구성에 따라 Secrets Manager에 대한 캐싱 및 API 직접 호출을 자동으로 처리합니다.