aws-secrets-manager
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAWS Secrets Manager
AWS Secrets Manager
Securely store, manage, and rotate secrets in AWS.
在AWS中安全存储、管理和轮换密钥。
When to Use This Skill
何时使用此技能
Use this skill when:
- Storing database credentials, API keys, or tokens in AWS
- Implementing automatic credential rotation for RDS or other services
- Replacing hardcoded secrets in application code or config files
- Integrating secrets into ECS, EKS, or Lambda workloads
- Meeting compliance requirements for secret management and rotation
在以下场景中使用此技能:
- 在AWS中存储数据库凭证、API密钥或令牌
- 为RDS或其他服务实现自动凭证轮换
- 替换应用代码或配置文件中的硬编码密钥
- 将密钥集成到ECS、EKS或Lambda工作负载中
- 满足密钥管理与轮换的合规要求
Prerequisites
前提条件
- AWS account with appropriate IAM permissions
- AWS CLI v2 installed and configured
- IAM policy allowing actions (or scoped permissions)
secretsmanager:* - For rotation: Lambda execution role and VPC access to target services
- Python 3.9+ with for SDK examples
boto3
- 拥有适当IAM权限的AWS账户
- 已安装并配置AWS CLI v2
- 允许操作(或限定范围权限)的IAM策略
secretsmanager:* - 若要启用轮换:Lambda执行角色以及对目标服务的VPC访问权限
- 安装了的Python 3.9+环境(用于SDK示例)
boto3
Secret Creation and Management
密钥创建与管理
bash
undefinedbash
undefinedCreate a secret with JSON structure
创建带JSON结构的密钥
aws secretsmanager create-secret
--name myapp/production/database
--description "Production database credentials"
--secret-string '{"username":"dbadmin","password":"S3cur3P@ssw0rd!","engine":"postgres","host":"db.internal.example.com","port":5432,"dbname":"myapp"}'
--tags '[{"Key":"Environment","Value":"production"},{"Key":"Team","Value":"platform"}]'
--name myapp/production/database
--description "Production database credentials"
--secret-string '{"username":"dbadmin","password":"S3cur3P@ssw0rd!","engine":"postgres","host":"db.internal.example.com","port":5432,"dbname":"myapp"}'
--tags '[{"Key":"Environment","Value":"production"},{"Key":"Team","Value":"platform"}]'
aws secretsmanager create-secret
--name myapp/production/database
--description "Production database credentials"
--secret-string '{"username":"dbadmin","password":"S3cur3P@ssw0rd!","engine":"postgres","host":"db.internal.example.com","port":5432,"dbname":"myapp"}'
--tags '[{"Key":"Environment","Value":"production"},{"Key":"Team","Value":"platform"}]'
--name myapp/production/database
--description "Production database credentials"
--secret-string '{"username":"dbadmin","password":"S3cur3P@ssw0rd!","engine":"postgres","host":"db.internal.example.com","port":5432,"dbname":"myapp"}'
--tags '[{"Key":"Environment","Value":"production"},{"Key":"Team","Value":"platform"}]'
Create a secret with KMS encryption (custom key)
使用KMS加密创建密钥(自定义密钥)
aws secretsmanager create-secret
--name myapp/production/api-key
--description "Third-party API key"
--secret-string "ak_live_xxxxxxxxxxxx"
--kms-key-id alias/secrets-key
--name myapp/production/api-key
--description "Third-party API key"
--secret-string "ak_live_xxxxxxxxxxxx"
--kms-key-id alias/secrets-key
aws secretsmanager create-secret
--name myapp/production/api-key
--description "Third-party API key"
--secret-string "ak_live_xxxxxxxxxxxx"
--kms-key-id alias/secrets-key
--name myapp/production/api-key
--description "Third-party API key"
--secret-string "ak_live_xxxxxxxxxxxx"
--kms-key-id alias/secrets-key
Create a binary secret (certificates, keys)
创建二进制密钥(证书、密钥)
aws secretsmanager create-secret
--name myapp/production/tls-cert
--secret-binary fileb://server.pfx
--name myapp/production/tls-cert
--secret-binary fileb://server.pfx
aws secretsmanager create-secret
--name myapp/production/tls-cert
--secret-binary fileb://server.pfx
--name myapp/production/tls-cert
--secret-binary fileb://server.pfx
Get secret value
获取密钥值
aws secretsmanager get-secret-value
--secret-id myapp/production/database
--query 'SecretString' --output text | jq .
--secret-id myapp/production/database
--query 'SecretString' --output text | jq .
aws secretsmanager get-secret-value
--secret-id myapp/production/database
--query 'SecretString' --output text | jq .
--secret-id myapp/production/database
--query 'SecretString' --output text | jq .
Get a specific version
获取特定版本的密钥
aws secretsmanager get-secret-value
--secret-id myapp/production/database
--version-stage AWSPREVIOUS
--secret-id myapp/production/database
--version-stage AWSPREVIOUS
aws secretsmanager get-secret-value
--secret-id myapp/production/database
--version-stage AWSPREVIOUS
--secret-id myapp/production/database
--version-stage AWSPREVIOUS
Update secret value
更新密钥值
aws secretsmanager put-secret-value
--secret-id myapp/production/database
--secret-string '{"username":"dbadmin","password":"N3wS3cur3P@ss!","engine":"postgres","host":"db.internal.example.com","port":5432,"dbname":"myapp"}'
--secret-id myapp/production/database
--secret-string '{"username":"dbadmin","password":"N3wS3cur3P@ss!","engine":"postgres","host":"db.internal.example.com","port":5432,"dbname":"myapp"}'
aws secretsmanager put-secret-value
--secret-id myapp/production/database
--secret-string '{"username":"dbadmin","password":"N3wS3cur3P@ss!","engine":"postgres","host":"db.internal.example.com","port":5432,"dbname":"myapp"}'
--secret-id myapp/production/database
--secret-string '{"username":"dbadmin","password":"N3wS3cur3P@ss!","engine":"postgres","host":"db.internal.example.com","port":5432,"dbname":"myapp"}'
List all secrets
列出所有密钥
aws secretsmanager list-secrets
--filters Key=name,Values=myapp/production
--filters Key=name,Values=myapp/production
aws secretsmanager list-secrets
--filters Key=name,Values=myapp/production
--filters Key=name,Values=myapp/production
Delete secret (with recovery window)
删除密钥(带恢复窗口期)
aws secretsmanager delete-secret
--secret-id myapp/production/old-key
--recovery-window-in-days 7
--secret-id myapp/production/old-key
--recovery-window-in-days 7
aws secretsmanager delete-secret
--secret-id myapp/production/old-key
--recovery-window-in-days 7
--secret-id myapp/production/old-key
--recovery-window-in-days 7
Restore a deleted secret
恢复已删除的密钥
aws secretsmanager restore-secret
--secret-id myapp/production/old-key
--secret-id myapp/production/old-key
aws secretsmanager restore-secret
--secret-id myapp/production/old-key
--secret-id myapp/production/old-key
Tag a secret
为密钥添加标签
aws secretsmanager tag-resource
--secret-id myapp/production/database
--tags '[{"Key":"RotationEnabled","Value":"true"}]'
--secret-id myapp/production/database
--tags '[{"Key":"RotationEnabled","Value":"true"}]'
undefinedaws secretsmanager tag-resource
--secret-id myapp/production/database
--tags '[{"Key":"RotationEnabled","Value":"true"}]'
--secret-id myapp/production/database
--tags '[{"Key":"RotationEnabled","Value":"true"}]'
undefinedAutomatic Rotation
自动轮换
Enable Rotation
启用轮换
bash
undefinedbash
undefinedEnable rotation with an existing Lambda function
使用现有Lambda函数启用轮换
aws secretsmanager rotate-secret
--secret-id myapp/production/database
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSPostgreSQLRotation
--rotation-rules '{"AutomaticallyAfterDays":30,"ScheduleExpression":"rate(30 days)"}'
--secret-id myapp/production/database
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSPostgreSQLRotation
--rotation-rules '{"AutomaticallyAfterDays":30,"ScheduleExpression":"rate(30 days)"}'
aws secretsmanager rotate-secret
--secret-id myapp/production/database
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSPostgreSQLRotation
--rotation-rules '{"AutomaticallyAfterDays":30,"ScheduleExpression":"rate(30 days)"}'
--secret-id myapp/production/database
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSPostgreSQLRotation
--rotation-rules '{"AutomaticallyAfterDays":30,"ScheduleExpression":"rate(30 days)"}'
Trigger immediate rotation
触发立即轮换
aws secretsmanager rotate-secret
--secret-id myapp/production/database
--secret-id myapp/production/database
aws secretsmanager rotate-secret
--secret-id myapp/production/database
--secret-id myapp/production/database
Check rotation status
检查轮换状态
aws secretsmanager describe-secret
--secret-id myapp/production/database
--query '{RotationEnabled:RotationEnabled,RotationLambdaARN:RotationLambdaARN,RotationRules:RotationRules,LastRotatedDate:LastRotatedDate}'
--secret-id myapp/production/database
--query '{RotationEnabled:RotationEnabled,RotationLambdaARN:RotationLambdaARN,RotationRules:RotationRules,LastRotatedDate:LastRotatedDate}'
undefinedaws secretsmanager describe-secret
--secret-id myapp/production/database
--query '{RotationEnabled:RotationEnabled,RotationLambdaARN:RotationLambdaARN,RotationRules:RotationRules,LastRotatedDate:LastRotatedDate}'
--secret-id myapp/production/database
--query '{RotationEnabled:RotationEnabled,RotationLambdaARN:RotationLambdaARN,RotationRules:RotationRules,LastRotatedDate:LastRotatedDate}'
undefinedLambda Rotation Function
Lambda轮换函数
python
"""rotation_function.py - Custom rotation Lambda for database credentials."""
import boto3
import json
import logging
import psycopg2
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
"""Secrets Manager rotation handler.
The rotation process has four steps:
1. createSecret - Generate new secret value
2. setSecret - Apply the new secret to the target service
3. testSecret - Verify the new secret works
4. finishSecret - Mark rotation complete
"""
secret_arn = event['SecretId']
token = event['ClientRequestToken']
step = event['Step']
client = boto3.client('secretsmanager')
metadata = client.describe_secret(SecretId=secret_arn)
if not metadata.get('RotationEnabled'):
raise ValueError(f"Secret {secret_arn} does not have rotation enabled")
versions = metadata.get('VersionIdsToStages', {})
if token not in versions:
raise ValueError(f"Secret version {token} has no stage for rotation")
if step == "createSecret":
create_secret(client, secret_arn, token)
elif step == "setSecret":
set_secret(client, secret_arn, token)
elif step == "testSecret":
test_secret(client, secret_arn, token)
elif step == "finishSecret":
finish_secret(client, secret_arn, token)
else:
raise ValueError(f"Invalid step: {step}")
def create_secret(client, secret_arn, token):
"""Generate a new secret value."""
current = client.get_secret_value(
SecretId=secret_arn, VersionStage="AWSCURRENT"
)
current_dict = json.loads(current['SecretString'])
new_password = client.get_random_password(
PasswordLength=32,
ExcludeCharacters='/@"\\',
RequireEachIncludedType=True,
)['RandomPassword']
current_dict['password'] = new_password
client.put_secret_value(
SecretId=secret_arn,
ClientRequestToken=token,
SecretString=json.dumps(current_dict),
VersionStages=['AWSPENDING'],
)
logger.info(f"createSecret: New secret version created for {secret_arn}")
def set_secret(client, secret_arn, token):
"""Apply the new secret to the target database."""
pending = client.get_secret_value(
SecretId=secret_arn, VersionId=token, VersionStage="AWSPENDING"
)
pending_dict = json.loads(pending['SecretString'])
current = client.get_secret_value(
SecretId=secret_arn, VersionStage="AWSCURRENT"
)
current_dict = json.loads(current['SecretString'])
conn = psycopg2.connect(
host=current_dict['host'],
port=current_dict.get('port', 5432),
user=current_dict['username'],
password=current_dict['password'],
dbname=current_dict.get('dbname', 'postgres'),
)
conn.autocommit = True
with conn.cursor() as cur:
cur.execute(
"ALTER USER %s WITH PASSWORD %s",
(pending_dict['username'], pending_dict['password']),
)
conn.close()
logger.info(f"setSecret: Password updated in database for {secret_arn}")
def test_secret(client, secret_arn, token):
"""Verify the new secret works."""
pending = client.get_secret_value(
SecretId=secret_arn, VersionId=token, VersionStage="AWSPENDING"
)
pending_dict = json.loads(pending['SecretString'])
conn = psycopg2.connect(
host=pending_dict['host'],
port=pending_dict.get('port', 5432),
user=pending_dict['username'],
password=pending_dict['password'],
dbname=pending_dict.get('dbname', 'postgres'),
)
conn.close()
logger.info(f"testSecret: New credentials verified for {secret_arn}")
def finish_secret(client, secret_arn, token):
"""Finalize the rotation by updating version stages."""
metadata = client.describe_secret(SecretId=secret_arn)
versions = metadata.get('VersionIdsToStages', {})
current_version = None
for version_id, stages in versions.items():
if "AWSCURRENT" in stages:
if version_id == token:
logger.info("finishSecret: Version already marked AWSCURRENT")
return
current_version = version_id
break
client.update_secret_version_stage(
SecretId=secret_arn,
VersionStage="AWSCURRENT",
MoveToVersionId=token,
RemoveFromVersionId=current_version,
)
logger.info(f"finishSecret: Rotation complete for {secret_arn}")python
"""rotation_function.py - Custom rotation Lambda for database credentials."""
import boto3
import json
import logging
import psycopg2
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
"""Secrets Manager rotation handler.
The rotation process has four steps:
1. createSecret - Generate new secret value
2. setSecret - Apply the new secret to the target service
3. testSecret - Verify the new secret works
4. finishSecret - Mark rotation complete
"""
secret_arn = event['SecretId']
token = event['ClientRequestToken']
step = event['Step']
client = boto3.client('secretsmanager')
metadata = client.describe_secret(SecretId=secret_arn)
if not metadata.get('RotationEnabled'):
raise ValueError(f"Secret {secret_arn} does not have rotation enabled")
versions = metadata.get('VersionIdsToStages', {})
if token not in versions:
raise ValueError(f"Secret version {token} has no stage for rotation")
if step == "createSecret":
create_secret(client, secret_arn, token)
elif step == "setSecret":
set_secret(client, secret_arn, token)
elif step == "testSecret":
test_secret(client, secret_arn, token)
elif step == "finishSecret":
finish_secret(client, secret_arn, token)
else:
raise ValueError(f"Invalid step: {step}")
def create_secret(client, secret_arn, token):
"""Generate a new secret value."""
current = client.get_secret_value(
SecretId=secret_arn, VersionStage="AWSCURRENT"
)
current_dict = json.loads(current['SecretString'])
new_password = client.get_random_password(
PasswordLength=32,
ExcludeCharacters='/@"\\',
RequireEachIncludedType=True,
)['RandomPassword']
current_dict['password'] = new_password
client.put_secret_value(
SecretId=secret_arn,
ClientRequestToken=token,
SecretString=json.dumps(current_dict),
VersionStages=['AWSPENDING'],
)
logger.info(f"createSecret: New secret version created for {secret_arn}")
def set_secret(client, secret_arn, token):
"""Apply the new secret to the target database."""
pending = client.get_secret_value(
SecretId=secret_arn, VersionId=token, VersionStage="AWSPENDING"
)
pending_dict = json.loads(pending['SecretString'])
current = client.get_secret_value(
SecretId=secret_arn, VersionStage="AWSCURRENT"
)
current_dict = json.loads(current['SecretString'])
conn = psycopg2.connect(
host=current_dict['host'],
port=current_dict.get('port', 5432),
user=current_dict['username'],
password=current_dict['password'],
dbname=current_dict.get('dbname', 'postgres'),
)
conn.autocommit = True
with conn.cursor() as cur:
cur.execute(
"ALTER USER %s WITH PASSWORD %s",
(pending_dict['username'], pending_dict['password']),
)
conn.close()
logger.info(f"setSecret: Password updated in database for {secret_arn}")
def test_secret(client, secret_arn, token):
"""Verify the new secret works."""
pending = client.get_secret_value(
SecretId=secret_arn, VersionId=token, VersionStage="AWSPENDING"
)
pending_dict = json.loads(pending['SecretString'])
conn = psycopg2.connect(
host=pending_dict['host'],
port=pending_dict.get('port', 5432),
user=pending_dict['username'],
password=pending_dict['password'],
dbname=pending_dict.get('dbname', 'postgres'),
)
conn.close()
logger.info(f"testSecret: New credentials verified for {secret_arn}")
def finish_secret(client, secret_arn, token):
"""Finalize the rotation by updating version stages."""
metadata = client.describe_secret(SecretId=secret_arn)
versions = metadata.get('VersionIdsToStages', {})
current_version = None
for version_id, stages in versions.items():
if "AWSCURRENT" in stages:
if version_id == token:
logger.info("finishSecret: Version already marked AWSCURRENT")
return
current_version = version_id
break
client.update_secret_version_stage(
SecretId=secret_arn,
VersionStage="AWSCURRENT",
MoveToVersionId=token,
RemoveFromVersionId=current_version,
)
logger.info(f"finishSecret: Rotation complete for {secret_arn}")Rotation Lambda Terraform
轮换Lambda的Terraform配置
hcl
resource "aws_lambda_function" "rotation" {
filename = "rotation_function.zip"
function_name = "secrets-rotation-postgresql"
role = aws_iam_role.rotation.arn
handler = "rotation_function.lambda_handler"
runtime = "python3.11"
timeout = 60
vpc_config {
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.rotation.id]
}
environment {
variables = {
SECRETS_MANAGER_ENDPOINT = "https://secretsmanager.${var.region}.amazonaws.com"
}
}
}
resource "aws_lambda_permission" "secrets_manager" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.rotation.function_name
principal = "secretsmanager.amazonaws.com"
statement_id = "AllowSecretsManager"
}
resource "aws_secretsmanager_secret_rotation" "db" {
secret_id = aws_secretsmanager_secret.db.id
rotation_lambda_arn = aws_lambda_function.rotation.arn
rotation_rules {
automatically_after_days = 30
}
}hcl
resource "aws_lambda_function" "rotation" {
filename = "rotation_function.zip"
function_name = "secrets-rotation-postgresql"
role = aws_iam_role.rotation.arn
handler = "rotation_function.lambda_handler"
runtime = "python3.11"
timeout = 60
vpc_config {
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.rotation.id]
}
environment {
variables = {
SECRETS_MANAGER_ENDPOINT = "https://secretsmanager.${var.region}.amazonaws.com"
}
}
}
resource "aws_lambda_permission" "secrets_manager" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.rotation.function_name
principal = "secretsmanager.amazonaws.com"
statement_id = "AllowSecretsManager"
}
resource "aws_secretsmanager_secret_rotation" "db" {
secret_id = aws_secretsmanager_secret.db.id
rotation_lambda_arn = aws_lambda_function.rotation.arn
rotation_rules {
automatically_after_days = 30
}
}Application Integration
应用集成
Python SDK
Python SDK示例
python
import boto3
import json
from functools import lru_cache
def get_secret(secret_name: str, region: str = "us-east-1") -> dict:
"""Retrieve and parse a secret from AWS Secrets Manager."""
client = boto3.client("secretsmanager", region_name=region)
response = client.get_secret_value(SecretId=secret_name)
if "SecretString" in response:
return json.loads(response["SecretString"])
else:
import base64
return base64.b64decode(response["SecretBinary"])
@lru_cache(maxsize=32)
def get_cached_secret(secret_name: str) -> dict:
"""Cached secret retrieval. Clear cache on rotation events."""
return get_secret(secret_name)python
import boto3
import json
from functools import lru_cache
def get_secret(secret_name: str, region: str = "us-east-1") -> dict:
"""Retrieve and parse a secret from AWS Secrets Manager."""
client = boto3.client("secretsmanager", region_name=region)
response = client.get_secret_value(SecretId=secret_name)
if "SecretString" in response:
return json.loads(response["SecretString"])
else:
import base64
return base64.b64decode(response["SecretBinary"])
@lru_cache(maxsize=32)
def get_cached_secret(secret_name: str) -> dict:
"""Cached secret retrieval. Clear cache on rotation events."""
return get_secret(secret_name)Usage
使用示例
creds = get_secret("myapp/production/database")
connection_string = (
f"postgresql://{creds['username']}:{creds['password']}"
f"@{creds['host']}:{creds['port']}/{creds['dbname']}"
)
undefinedcreds = get_secret("myapp/production/database")
connection_string = (
f"postgresql://{creds['username']}:{creds['password']}"
f"@{creds['host']}:{creds['port']}/{creds['dbname']}"
)
undefinedECS Task Definition
ECS任务定义
json
{
"containerDefinitions": [
{
"name": "myapp",
"image": "ghcr.io/acme/myapp:v1.0.0",
"secrets": [
{
"name": "DB_USERNAME",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:myapp/production/database:username::"
},
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:myapp/production/database:password::"
},
{
"name": "API_KEY",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:myapp/production/api-key"
}
]
}
],
"executionRoleArn": "arn:aws:iam::123456789:role/ecsTaskExecutionRole"
}json
{
"containerDefinitions": [
{
"name": "myapp",
"image": "ghcr.io/acme/myapp:v1.0.0",
"secrets": [
{
"name": "DB_USERNAME",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:myapp/production/database:username::"
},
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:myapp/production/database:password::"
},
{
"name": "API_KEY",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:myapp/production/api-key"
}
]
}
],
"executionRoleArn": "arn:aws:iam::123456789:role/ecsTaskExecutionRole"
}EKS with External Secrets Operator
EKS与External Secrets Operator集成
yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-manager
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: myapp/production/database
property: username
- secretKey: password
remoteRef:
key: myapp/production/database
property: passwordyaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-manager
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: myapp/production/database
property: username
- secretKey: password
remoteRef:
key: myapp/production/database
property: passwordResource-Based Policy
基于资源的策略
bash
undefinedbash
undefinedRestrict secret access to specific roles
限制特定角色访问密钥
aws secretsmanager put-resource-policy
--secret-id myapp/production/database
--resource-policy '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::123456789:role/myapp-ecs-task-role", "arn:aws:iam::123456789:role/myapp-lambda-role" ] }, "Action": [ "secretsmanager:GetSecretValue" ], "Resource": "", "Condition": { "StringEquals": { "aws:RequestedRegion": "us-east-1" } } }, { "Effect": "Deny", "Principal": "", "Action": "secretsmanager:GetSecretValue", "Resource": "*", "Condition": { "StringNotEquals": { "aws:PrincipalAccount": "123456789012" } } } ] }'
--secret-id myapp/production/database
--resource-policy '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::123456789:role/myapp-ecs-task-role", "arn:aws:iam::123456789:role/myapp-lambda-role" ] }, "Action": [ "secretsmanager:GetSecretValue" ], "Resource": "", "Condition": { "StringEquals": { "aws:RequestedRegion": "us-east-1" } } }, { "Effect": "Deny", "Principal": "", "Action": "secretsmanager:GetSecretValue", "Resource": "*", "Condition": { "StringNotEquals": { "aws:PrincipalAccount": "123456789012" } } } ] }'
undefinedaws secretsmanager put-resource-policy
--secret-id myapp/production/database
--resource-policy '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::123456789:role/myapp-ecs-task-role", "arn:aws:iam::123456789:role/myapp-lambda-role" ] }, "Action": [ "secretsmanager:GetSecretValue" ], "Resource": "", "Condition": { "StringEquals": { "aws:RequestedRegion": "us-east-1" } } }, { "Effect": "Deny", "Principal": "", "Action": "secretsmanager:GetSecretValue", "Resource": "*", "Condition": { "StringNotEquals": { "aws:PrincipalAccount": "123456789012" } } } ] }'
--secret-id myapp/production/database
--resource-policy '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::123456789:role/myapp-ecs-task-role", "arn:aws:iam::123456789:role/myapp-lambda-role" ] }, "Action": [ "secretsmanager:GetSecretValue" ], "Resource": "", "Condition": { "StringEquals": { "aws:RequestedRegion": "us-east-1" } } }, { "Effect": "Deny", "Principal": "", "Action": "secretsmanager:GetSecretValue", "Resource": "*", "Condition": { "StringNotEquals": { "aws:PrincipalAccount": "123456789012" } } } ] }'
undefinedTroubleshooting
故障排查
| Problem | Cause | Solution |
|---|---|---|
| IAM policy missing permission | Add |
| Rotation fails with Lambda timeout | Lambda cannot reach database | Ensure Lambda is in same VPC with route to DB; check security groups |
| Secret value is empty after rotation | createSecret step failed | Check Lambda CloudWatch logs; verify random password generation works |
| ECS container fails to start | Secret ARN format incorrect | Use full ARN with |
| Application uses old credentials after rotation | Client caching stale values | Implement cache invalidation on rotation; reduce cache TTL |
| Rotation Lambda permission error | Missing | Add |
| KMS decrypt fails | Secret KMS key policy missing role | Add the accessing role to the KMS key policy's |
| 问题 | 原因 | 解决方案 |
|---|---|---|
调用GetSecretValue时出现 | IAM策略缺少权限 | 为角色添加 |
| 轮换因Lambda超时失败 | Lambda无法连接数据库 | 确保Lambda与数据库在同一VPC且有路由;检查安全组配置 |
| 轮换后密钥值为空 | createSecret步骤执行失败 | 查看Lambda CloudWatch日志;验证随机密码生成功能正常 |
| ECS容器启动失败 | 密钥ARN格式错误 | 使用包含 |
| 轮换后应用仍使用旧凭证 | 客户端缓存了过期值 | 实现轮换时的缓存失效机制;缩短缓存TTL |
| 轮换Lambda出现权限错误 | 缺少 | 为secretsmanager.amazonaws.com主体添加 |
| KMS解密失败 | 密钥KMS密钥策略缺少角色 | 将访问角色添加到KMS密钥策略的 |
Best Practices
最佳实践
- Enable automatic rotation with 30-day intervals minimum
- Use resource-based policies in addition to IAM policies (defense in depth)
- Encrypt secrets with customer-managed KMS keys (not default)
- Implement least-privilege access (only the roles that need each secret)
- Use secret versioning for safe rollback during rotation issues
- Monitor secret access with CloudTrail and alert on unusual patterns
- Structure secret names hierarchically:
{app}/{env}/{secret-type} - Never log secret values; log only secret ARNs and access metadata
- Test rotation in staging before enabling in production
- Set up CloudWatch alarms for rotation failures
- 启用自动轮换,最小间隔为30天
- 除IAM策略外,同时使用基于资源的策略(深度防御)
- 使用客户管理的KMS密钥加密密钥(而非默认密钥)
- 实现最小权限访问(仅为需要的角色分配密钥权限)
- 使用密钥版本控制,以便在轮换出现问题时安全回滚
- 通过CloudTrail监控密钥访问,并对异常模式发出警报
- 按层级结构命名密钥:
{应用}/{环境}/{密钥类型} - 切勿记录密钥值;仅记录密钥ARN和访问元数据
- 在生产环境启用轮换前,先在预发布环境测试
- 为轮换失败设置CloudWatch告警
Related Skills
相关技能
- hashicorp-vault - Multi-cloud secrets
- aws-iam - IAM policies
- azure-keyvault - Azure secret management
- gcp-secret-manager - GCP secret management
- hashicorp-vault - 多云密钥管理
- aws-iam - IAM策略配置
- azure-keyvault - Azure密钥管理
- gcp-secret-manager - GCP密钥管理