CloudFront를 사용하여 S3에서 SSE‑KMS 암호화된 콘텐츠 제공
I’m happy to help translate the article into Korean, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have it, I’ll translate it while preserving the original formatting, markdown, and technical terms.
문제
사용자가 다음과 같은 파일을 업로드하는 애플리케이션을 구축하고 있습니다:
- 프로필 사진
- 청구서
- 영수증
- 문서
- 개인 첨부 파일
두 가지 요구 사항을 모두 만족하는 솔루션이 필요합니다:
- 프라이버시 – 파일은 비공개로 유지되어야 합니다.
- 성능 – 파일은 전 세계적으로 빠르게 로드되어야 합니다.
| 옵션 | 장점 | 단점 |
|---|---|---|
| 공개 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 (Key Management Service) – 암호화 키를 관리합니다.
- SSE‑KMS를 사용하면 S3가 객체를 암호화된 상태로 저장합니다.
- 요청 시 S3는 KMS 키를 사용해 복호화한 뒤 반환합니다.
- 장점: 자동 암호화, IAM 기반 접근 제어, 전체 감사 로그(CloudTrail).
서버 측 암호화 (SSE) 종류
| Type | Description |
|---|---|
| SSE‑S3 | S3에서 관리하는 키; 각 객체마다 고유 키가 생성됩니다. |
| SSE‑KMS | AWS KMS에 저장된 고객 마스터 키(CMK) – 더 많은 제어와 가시성 제공. |
| 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 - 설명: “SSE‑KMS 키 for S3 content served via CloudFront”
- 관리자(IAM 사용자/역할)를 할당하고 필요에 따라 키 정책을 편집.
- 키 생성을 완료.
이 키는 저장할 모든 S3 객체의 “마스터 키”가 됩니다.
2단계 – 비공개 S3 버킷 만들기
- S3 → Create bucket 로 이동.
- 버킷 이름: 예시
myapp-dev-private-assets-123456(전 세계에서 고유해야 함). - KMS 키와 같은 리전을 선택.
- Block all public access: 켜서 실수로 노출되는 것을 방지.
- (선택) Enable versioning – 실수로 삭제되는 것을 방어.
- Default encryption:
- SSE‑KMS 선택.
- 만든 KMS 키의 ARN을 붙여넣기 (
myapp-dev-s3-key). - Bucket Key를 활성화해 KMS 요청 비용을 절감.
참고: 버킷 수준 암호화는 활성화 후에 업로드된 객체에만 적용됩니다.
3단계 – CloudFront에 버킷 접근 권한 부여 (OAC)
- CloudFront → Origin access → Create origin access control 열기.
- 이름:
myapp-dev-oac(또는 설명적인 이름). - Signing behavior: SigV4.
- Origin type: S3.
- Access: Read‑only (필요에 따라 조정).
- OAC 저장.
4단계 – CloudFront 배포 생성
- CloudFront에서 Create Distribution → Web 클릭.
- Origin Settings:
- Origin domain: 만든 비공개 S3 버킷 선택.
- Origin Access Control: 방금 만든 OAC 선택.
- Origin request policy: 기본값 사용하거나 필요한 헤더를 전달하도록 새로 생성.
- Cache Behavior Settings:
- Viewer Protocol Policy: Redirect HTTP to HTTPS.
- Allowed HTTP Methods:
GET, HEAD, OPTIONS(필요 시 다른 메서드 추가). - Cache based on selected request headers: 앱에 필요한 헤더만 Whitelist.
- Distribution Settings:
- 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 사용 로그가 표시됩니다.
Benefits recap
- Bucket stays private forever – no public ACLs or “anyone with the link” access.
- Objects encrypted with your KMS key – full control and auditability.
- Full decrypt audit trail via CloudTrail → better compliance.
- CloudFront edge caching → faster downloads worldwide and reduced S3 request costs.
TL;DR
- Private S3 bucket + SSE‑KMS = 직접 제어하는 데이터 정지 시 암호화.
- CloudFront OAC (SigV4) = 해당 버킷에 대한 안전하고 CDN 친화적인 접근.
- 결과: 안전하고 빠르며 전 세계에 분산된 콘텐츠 전달을 버킷을 공개하지 않고도 구현.
단계별 가이드: OAC를 사용하여 CloudFront를 통해 SSE‑KMS 암호화된 S3 객체 제공
단계 1 – Origin Access Control (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→ 배포 ARNkms: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
Expected: HTTP/2 200 OK
- 직접 S3 URL 테스트 (실패해야 함):
curl -I https://myapp-dev-private-assets-123456.s3.eu-west-2.amazonaws.com/test.jpg
Expected: HTTP/1.1 403 Forbidden
✅ Result: CloudFront만이 공개 접근 지점입니다.
일반적인 함정 및 해결책
| 증상 | 가능한 원인 | 해결 방법 |
|---|---|---|
| CloudFront에서 403 AccessDenied | KMS 키 정책에서 잘못된 AWS:SourceArn, 버킷 정책 불일치, CloudFront 권한 누락, 전파 지연 | ARN 값을 확인하고, OAC가 참조되는지 확인하고, 전파될 때까지 기다리기 |
| 파일이 암호화되지 않음 | SSE‑KMS가 활성화되기 전에 업로드된 파일 | SSE‑KMS가 활성화된 상태로 파일을 다시 업로드하거나 객체를 복사하기 |
| 잘못된 S3 오리진 엔드포인트 | 웹사이트 엔드포인트를 사용하고 버킷 엔드포인트 대신 사용 | 버킷 엔드포인트(*.s3.<region>.amazonaws.com) 사용 |
| CloudFront가 오래된 오류를 계속 제공함 | 캐시된 오류 응답 | 캐시 무효화(/*) |
이제 CloudFront를 통해 제공되는 S3 버킷이 SSE‑KMS로 암호화된 객체를 보안적으로 보유하며, CloudFront 배포를 통해서만 접근할 수 있습니다.