使用 CloudFront 从 S3 提供 SSE‑KMS 加密内容
Source: Dev.to
(请提供您希望翻译的正文内容,我将为您翻译成简体中文,并保留原有的格式、代码块和链接。)
问题
您正在构建一个应用,用户会上传:
- 个人头像
- 发票
- 收据
- 文档
- 私人附件
您需要一个同时满足以下两点的解决方案:
- 隐私 – 文件必须保持私密。
- 性能 – 文件必须在全球快速加载。
| 选项 | 优点 | 缺点 |
|---|---|---|
| 公共 S3 存储桶 | 快速(适合 CDN) | 不安全 |
| 私有 S3 存储桶 | 安全 | 可能更慢,且不适合 CDN |
| 私有 + 自己的密钥加密 | 即使存储被泄露仍然安全 | 需要额外设置 |
引入 SSE‑KMS。
理想的设置如下:
- ✅ 私有 S3 存储桶
- ✅ 使用 KMS(SSE‑KMS)进行静态加密
- ✅ 通过 CloudFront 全球分发
- ✅ 存储桶永不公开
架构概览
User ──► CloudFront (edge cache) ──► S3 (private, SSE‑KMS)
每项服务的作用
-
S3 – 大规模云“硬盘”。
- Bucket = 容器(文件夹)
- Object = 文件(图片、PDF、ZIP,…)
- 可以设为公开,但我们将其设为私有,用于存放用户内容。
-
CloudFront – AWS CDN,拥有全球边缘节点。
- 当请求到达(例如
https://d1234abcdef.cloudfront.net/images/photo.jpg)时:- 检查边缘缓存。
- 若已缓存 → 立即返回。
- 若未缓存 → 从 S3 获取,缓存后再返回。
- 当请求到达(例如
-
KMS(密钥管理服务) – 管理加密密钥。
- 使用 SSE‑KMS 时,S3 会对对象进行加密存储。
- 收到请求时,S3 在返回之前使用 KMS 密钥进行解密。
- 好处:自动加密、通过 IAM 控制访问、完整审计日志(CloudTrail)。
服务端加密(SSE)类型
| 类型 | 描述 |
|---|---|
| SSE‑S3 | 由 S3 管理密钥;每个对象拥有唯一密钥。 |
| SSE‑KMS | 客户主密钥(CMK)存放在 AWS KMS 中——提供更高的控制和可见性。 |
| SSE‑C | 客户自行提供密钥;您管理密钥,S3 仅负责加密对象。 |
传输中的数据加密(CloudFront)
- 强制使用 HTTPS(将 HTTP 重定向到 HTTPS)。
- 选择最低的 TLS 版本和密码套件。
- 绑定自定义域名并关联 TLS 证书。
结论: 在生产环境中,SSE‑KMS + CloudFront 是最佳组合。
CloudFront 如何访问受私有、SSE‑KMS 保护的存储桶
两种方法:
-
Origin Access Identity (OAI) – 一个拥有 S3 权限的特殊 CloudFront 用户。
- 限制: OAI 仅支持 SSE‑S3,不支持 SSE‑KMS。
-
Origin Access Control (OAC) – 更新且推荐的方式。
- 使用 SigV4 签名、IAM 风格的访问控制,以及更强的安全模型。
📌 如果您使用 SSE‑KMS + CloudFront,请使用 OAC。
您将构建的内容
在本指南结束时,您将拥有:
- 一个 私有 S3 存储桶,其中的对象使用 SSE‑KMS 加密。
- 一个 CloudFront 分配,可在全球安全地提供这些对象。
步骤指南
步骤 1 – 创建你的第一个 KMS 密钥
- 打开 AWS 控制台 → KMS。
- 点击 Customer managed keys → Create key。
- 密钥类型: 对称
密钥用途: 加密和解密 - 别名:
myapp-dev-s3-key - 描述: “用于通过 CloudFront 提供的 S3 内容的 SSE‑KMS 密钥”
- 分配管理员(你的 IAM 用户/角色),并根据需要编辑密钥策略。
- 完成密钥创建。
该密钥将成为你存储的所有 S3 对象的“主密钥”。
步骤 2 – 创建私有 S3 存储桶
- 前往 S3 → Create bucket。
- 存储桶名称: 例如
myapp-dev-private-assets-123456(必须全局唯一)。 - 选择与你的 KMS 密钥相同的区域。
- 阻止所有公共访问: 开启,以防止意外泄露。
- (可选)启用版本控制 – 可防止意外删除。
- 默认加密:
- 选择 SSE‑KMS。
- 粘贴你创建的 KMS 密钥的 ARN(
myapp-dev-s3-key)。 - 启用 Bucket Key 以降低 KMS 请求费用。
注意: 存储桶级别的加密仅对之后上传的对象生效。
步骤 3 – 为 CloudFront 提供访问存储桶的密钥(OAC)
- 打开 CloudFront → Origin access → Create origin access control。
- 名称:
myapp-dev-oac(或任意描述性名称)。 - 签名行为: SigV4。
- 源类型: S3。
- 访问权限: 只读(或根据需要)。
- 保存 OAC。
步骤 4 – 创建 CloudFront 分配
- 在 CloudFront 中点击 Create Distribution → Web。
- 源设置:
- 源域名: 选择你刚创建的私有 S3 存储桶。
- Origin Access Control: 选择刚才创建的 OAC。
- Origin request policy: 使用默认或创建一个转发必要头部的策略。
- 缓存行为设置:
- Viewer Protocol Policy: Redirect HTTP to HTTPS。
- Allowed HTTP Methods:
GET, HEAD, OPTIONS(如有需要可添加其他方法)。 - Cache based on selected request headers: Whitelist 仅你应用需要的头部。
- 分配设置:
- Price class: 根据目标地区选择。
- Alternate domain names (CNAMEs): 如有自定义域名请添加。
- SSL certificate: 为你的域名选择 Custom SSL(ACM 提供)。
- 点击 Create Distribution 并等待部署完成(通常几分钟)。
步骤 5 – 为 OAC 更新存储桶策略
将占位符替换为 OAC 的 IAM 主体 ARN(在 OAC 控制台可见),并相应修改存储桶名称。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontRead",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <OAC-ARN>"
},
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": "arn:aws:s3:::myapp-dev-private-assets-123456/*"
}
]
}
保存策略。
步骤 6 – 测试端到端流程
-
向 S3 存储桶上传测试文件(例如
test.txt)。 -
验证对象的 Encryption 列显示 SSE‑KMS 且使用了正确的 KMS 密钥。
-
通过 CloudFront 域名访问该文件:
curl -I https://d1234abcdef.cloudfront.net/test.txt- 请求应成功(HTTPS)。
- CloudFront 会在边缘缓存对象,然后即时向后续用户提供。
-
检查 CloudTrail 中的解密事件——你应该能看到每次请求的 KMS 使用日志。
好处回顾
- 存储桶始终保持私有 – 没有公共 ACL 或 “拥有链接的任何人” 访问。
- 对象使用您的 KMS 密钥加密 – 完全控制和可审计性。
- 通过 CloudTrail 完整的解密审计日志 → 更好的合规性。
- CloudFront 边缘缓存 → 全球更快的下载和降低 S3 请求费用。
TL;DR
- Private S3 bucket + SSE‑KMS = 您可控制的数据静止加密。
- CloudFront OAC (SigV4) = 对该存储桶的安全、CDN 友好访问。
- 结果:安全、快速、全球分发的内容交付,且永不公开您的存储桶。
步骤指南:使用 OAC 通过 CloudFront 提供 SSE‑KMS 加密的 S3 对象
步骤 1 – 创建源访问控制 (OAC)
- 导航:
CloudFront → Origin Access Controls → Create OAC - 填写:
| 字段 | 值 |
|---|---|
| Name | myapp-dev-oac |
| Origin type | S3 |
| Signing behavior | Always sign requests |
- 点击 Create。
此时 CloudFront 在从私有存储桶获取对象时即可进行身份验证。
步骤 2 – 创建 CloudFront 分配
-
导航:
CloudFront → Distributions → Create distribution -
配置源:
-
Origin domain: 选择您的 S3 存储桶
-
⚠️ 关键: 使用 存储桶端点,例如
myapp-dev-private-assets-123456.s3.eu-west-2.amazonaws.com不要使用网站端点(例如
s3-website.eu-west-2.amazonaws.com)。 -
Origin access: 选择您刚创建的 OAC(
myapp‑dev-oac)。
-
-
配置缓存行为:
| 设置 | 值 |
|---|---|
| Viewer protocol policy | Redirect HTTP → HTTPS |
| Allowed methods | GET, HEAD, OPTIONS |
| Cache policy | CachingOptimized |
- 点击 Create。
步骤 3 – 更新 S3 存储桶策略
- 在分配页面会看到蓝色横幅:“The S3 bucket policy needs to be updated.”
- 点击 Copy policy。
- 前往
S3 → <your bucket> → Permissions → Bucket policy → Edit。 - 粘贴复制的策略并 Save。
示例存储桶策略(用您的实际值替换占位符):
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::myapp-dev-private-assets-123456/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456785012:distribution/E2ABCDEFG12345"
}
}
}
]
}
注意:
AWS:SourceArn在您保存后会自动填入分配的实际 ARN。
步骤 4 – 为 CloudFront 更新 KMS 密钥策略
由于对象是 SSE‑KMS 加密的,CloudFront 需要解密权限。
- 前往
KMS → Customer managed keys → <your key> → Key policy → Edit。 - 添加以下语句(用您的实际值替换占位符):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableIAMUserPermissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<account-id>:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowCloudFrontDecryptThroughS3Only",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:DescribeKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::<account-id>:distribution/<distribution-id>",
"kms:ViaService": "s3.eu-west-2.amazonaws.com"
}
}
}
]
}
- 替换
AWS:SourceArn→ 您的分配 ARN。kms:ViaService→ 您的 S3 存储桶所在区域(例如s3.eu-west-2.amazonaws.com)。
步骤 5 – 等待部署完成
- CloudFront 传播时间:5–15 分钟。
- 分配状态变化:
Deploying → Enabled。 - 策略更新后,需 2–3 分钟 进行传播。
可选: 创建缓存失效(/*)以立即进行测试。
步骤 6 – 测试全部功能
-
上传测试文件(例如
test.jpg)到 S3 存储桶。确认在 Properties → Server‑side encryption 中显示 SSE‑KMS 加密。 -
通过 CloudFront 进行测试(应成功):
curl -I https://d1234abcdef.cloudfront.net/test.jpg
预期: HTTP/2 200 OK
- 测试直接 S3 URL(应失败):
curl -I https://myapp-dev-private-assets-123456.s3.eu-west-2.amazonaws.com/test.jpg
预期: HTTP/1.1 403 Forbidden
✅ 结果: CloudFront 是唯一的公共访问点。
常见陷阱与解决方案
| 症状 | 可能原因 | 解决办法 |
|---|---|---|
| 403 AccessDenied from CloudFront | KMS 密钥策略中的 AWS:SourceArn 错误,存储桶策略不匹配,缺少 CloudFront 权限,传播延迟 | 核实 ARN 值,确保引用了 OAC,等待策略传播 |
| Files not encrypted | 在启用 SSE‑KMS 之前已上传的文件 | 重新上传或在复制对象时启用 SSE‑KMS |
| Wrong S3 origin endpoint | 使用了网站端点而非存储桶端点 | 使用存储桶端点(*.s3.<region>.amazonaws.com) |
| CloudFront keeps serving old errors | 缓存的错误响应 | 使缓存失效(/*) |
您现在拥有一个安全的、通过 CloudFront 前端的 S3 存储桶,对象已使用 SSE‑KMS 加密,仅可通过 CloudFront 分配访问。