使用Python Lambda+KMS加密S3视频文件并转存时遇错求排查
问题排查与修正
1. 直接触发的KeyError原因
你的代码尝试从response['ResponseMetadata']['HTTPHeaders']['x-amz-server-side-encryption-aws-kms-key-id']获取KMS密钥ID,但这个字段仅在原S3对象使用SSE-KMS加密存储时才会存在。如果原对象未加密或使用其他加密方式(如SSE-S3),该字段不存在,直接访问会抛出KeyError。
另外,即使原对象用了SSE-KMS,AWS SDK也提供了更可靠的字段获取密钥ID:response['SSEKMSKeyId'],而非从HTTP响应头提取。
2. 核心逻辑错误
你的需求是将视频转存到另一个S3桶并通过KMS加密,但当前代码逻辑存在问题:
- 一次性将原S3对象的明文读入内存(若原对象是SSE-KMS加密,S3会自动解密返回明文),视频文件通常较大,易导致Lambda内存溢出
- 调用KMS
encryptAPI加密整个文件内容,属于客户端加密,后续读取需手动调用KMSdecryptAPI,无法被S3兼容工具直接处理
如果你的真实需求是让目标桶对象通过S3服务端KMS加密存储(即SSE-KMS),无需手动调用KMS加密API,只需在上传时指定加密参数即可,S3会自动完成加密,更高效安全。
3. 修正后的代码(推荐:S3服务端SSE-KMS加密)
采用流式传输避免内存溢出,直接让S3完成服务端加密:
import boto3 from botocore.exceptions import ClientError def lambda_handler(event, context): s3_client = boto3.client('s3') # 解析S3事件 s3_event = event['Records'][0]['s3'] source_bucket = s3_event['bucket']['name'] object_key = s3_event['object']['key'] target_bucket = 'decryptedfiles' # 注:桶名与加密转存的需求不符,建议改为encryptedfiles更合理 try: # 获取原对象流式数据 response = s3_client.get_object(Bucket=source_bucket, Key=object_key) # 上传到目标桶并指定SSE-KMS加密 s3_client.put_object( Bucket=target_bucket, Key=object_key, Body=response['Body'], # 流式传输,避免内存溢出 ServerSideEncryption='aws:kms', # 可指定自定义KMS密钥ARN,不指定则用S3默认KMS密钥 # SSEKMSKeyId='your-kms-key-arn' ) print(f"对象 {object_key} 已成功加密转存到 {target_bucket}") except ClientError as e: print(f"操作失败: {e.response['Error']['Message']}") raise e
4. 若坚持使用客户端加密(不推荐,仅适合小文件)
解决内存和密钥获取问题的示例代码:
import boto3 from botocore.exceptions import ClientError def lambda_handler(event, context): s3_client = boto3.client('s3') kms_client = boto3.client('kms') s3_event = event['Records'][0]['s3'] source_bucket = s3_event['bucket']['name'] object_key = s3_event['object']['key'] target_bucket = 'decryptedfiles' # 建议手动指定KMS密钥ARN,而非依赖原对象密钥 kms_key_id = 'your-kms-key-arn' try: # 读取原对象数据(仅适合小文件) response = s3_client.get_object(Bucket=source_bucket, Key=object_key) plaintext_data = response['Body'].read() # KMS加密数据 encrypted_response = kms_client.encrypt( Plaintext=plaintext_data, KeyId=kms_key_id ) encrypted_data = encrypted_response['CiphertextBlob'] # 上传加密后的数据 s3_client.put_object( Bucket=target_bucket, Key=object_key, Body=encrypted_data ) print(f"对象 {object_key} 已成功客户端加密并上传") except KeyError as e: print(f"密钥ID获取失败: {e}") raise e except ClientError as e: print(f"操作失败: {e.response['Error']['Message']}") raise e
5. 必要的权限配置
Lambda执行角色需添加以下权限:
s3:GetObject(源桶)s3:PutObject(目标桶)- 使用SSE-KMS时:
kms:GenerateDataKey(对应KMS密钥) - 使用客户端加密时:
kms:Encrypt(对应KMS密钥)
内容的提问来源于stack exchange,提问作者Aishwarya Joshi
相关产品推荐
相关产品推荐

