AWS KMS 완전 이해: S3 버킷 암호화에 AWS 관리형 키 vs CMK, 어떤 걸 써야 할까?
S3 버킷에 암호화를 적용하려고 콘솔을 열었을 때, 'AWS 관리형 키(aws/s3)'와 '고객 관리형 키(CMK)' 두 가지 선택지 앞에서 멈춰본 경험이 있을 것이다. 단순히 '암호화만 되면 되는 거 아닌가?'라고 생각했다가, 나중에 KMS API 호출 비용 청구서를 받고 당황하거나, 감사 요건을 충족하지 못해 다시 설계를 뒤집는 상황이 실제로 발생한다. AWS KMS 키 유형 선택은 보안 정책, 운영 복잡도, 비용 모두에 직접적인 영향을 미친다.
TL;DR: AWS 관리형 키 vs 고객 관리형 키 (CMK) 핵심 비교
| 항목 | AWS 관리형 키 (aws/s3) | 고객 관리형 키 (CMK) |
|---|---|---|
| 키 생성 주체 | AWS가 자동 생성 | 사용자가 직접 생성 |
| 키 정책 제어 | 불가 (AWS 관리) | 완전 제어 가능 |
| 키 로테이션 제어 | AWS 자동 관리 (1년) | 사용자 설정 가능 |
| 교차 계정 접근 | 불가 | 키 정책으로 허용 가능 |
| 월 키 관리 비용 | 무료 | 키당 월 $1 (대칭 키 기준) |
| API 호출 비용 | 발생 (요청당 과금) | 발생 (요청당 과금) |
| CloudTrail 감사 | 제한적 | 모든 키 사용 이벤트 기록 |
| Bucket Key 지원 | 지원 | 지원 |
AWS KMS가 S3 암호화에서 동작하는 방식
S3의 SSE-KMS 암호화는 봉투 암호화(envelope encryption) 구조로 동작한다. S3가 객체를 저장할 때 KMS에 데이터 키(data key) 생성을 요청하고, 반환된 평문 데이터 키로 객체를 암호화한 뒤, 암호화된 데이터 키만 객체 메타데이터에 함께 저장한다. 복호화 시에는 KMS를 다시 호출해 암호화된 데이터 키를 복호화한 후 객체를 읽는다. 즉, S3 객체 PUT/GET마다 KMS API 호출이 발생하며, 이것이 비용과 직결된다.
- PUT 요청: S3가 KMS에 GenerateDataKey 호출 → KMS가 평문 키 + 암호화된 키 반환
- 암호화 저장: S3가 평문 키로 객체 암호화 후, 암호화된 데이터 키를 메타데이터에 저장. 평문 키는 즉시 폐기.
- GET 요청: S3가 KMS에 Decrypt 호출 → 복호화된 데이터 키로 객체 복호화 후 반환
- Bucket Key 활성화 시: 버킷 수준 데이터 키를 캐싱해 KMS 호출 횟수를 대폭 줄임
AWS 관리형 키 (aws/s3): 언제 충분한가
AWS 관리형 키는 AWS가 해당 서비스 전용으로 자동 생성하고 관리하는 키다. S3의 경우 aws/s3라는 별칭으로 계정당 리전별로 하나씩 존재한다. 키 정책을 수정하거나 삭제할 수 없고, 로테이션 주기도 AWS가 결정한다.
규정 준수 요건이 없고, 교차 계정 접근이 필요 없으며, 키 사용에 대한 세밀한 감사 로그가 필요하지 않은 경우라면 AWS 관리형 키로 충분하다. 별도의 키 관리 오버헤드 없이 저장 데이터 암호화 요건을 빠르게 충족할 수 있다.
단, AWS 관리형 키도 KMS API 호출 비용은 발생한다. '무료'라는 인식은 키 관리 비용(월 $1)이 없다는 의미이지, API 호출 비용까지 면제되는 게 아니다.
고객 관리형 키 (CMK): 언제 반드시 필요한가
다음 조건 중 하나라도 해당된다면 CMK를 써야 한다.
- 교차 계정 접근: 다른 AWS 계정의 IAM 엔티티가 이 버킷의 객체를 읽어야 하는 경우. AWS 관리형 키는 키 정책 수정이 불가능해 교차 계정 접근을 허용할 수 없다.
- 세밀한 접근 제어: 특정 IAM 역할만 복호화를 허용하거나, 특정 조건(예: VPC 엔드포인트 경유)에서만 키 사용을 허용해야 하는 경우.
- 감사 및 규정 준수: CloudTrail에서 누가 언제 어떤 키로 어떤 작업을 했는지 추적해야 하는 규정(예: PCI-DSS, HIPAA) 환경.
- 키 비활성화 또는 삭제 제어: 특정 사고 발생 시 즉시 키를 비활성화해 데이터 접근을 차단해야 하는 경우.
- 가져온 키 자료(Imported Key Material): 조직 내부 HSM에서 생성한 키 자료를 사용해야 하는 경우.
CMK는 '더 강한 암호화'가 아니라 '더 많은 제어권'을 의미한다. 암호화 강도 자체는 AWS 관리형 키와 동일하게 AES-256이다.
AWS KMS 비용 구조 이해
KMS 비용은 크게 두 가지로 나뉜다. 정확한 수치는 리전과 시점에 따라 변동되므로, 반드시 AWS KMS 공식 요금 페이지에서 확인해야 한다.
1. 키 관리 비용
- AWS 관리형 키: 무료
- 고객 관리형 키 (대칭): 키당 월 $1 (AWS 공식 문서 기준, 리전별 상이할 수 있음)
- 고객 관리형 키 (비대칭): 키 유형에 따라 상이 — 공식 요금 페이지 확인 필수
2. API 호출 비용
S3 SSE-KMS에서 발생하는 주요 KMS API 호출은 GenerateDataKey(PUT 시)와 Decrypt(GET 시)다. 두 API 모두 요청 건수 기준으로 과금된다. 트래픽이 높은 S3 버킷에서 Bucket Key를 활성화하지 않으면 KMS 호출 비용이 예상보다 크게 발생할 수 있다.
월 $0 (무료)"] B --> E["CMK (대칭 키)
키당 월 $1"] C --> F["GenerateDataKey
(PUT 요청마다)"] C --> G["Decrypt
(GET 요청마다)"] F --> H["Bucket Key 비활성화
객체 수 = KMS 호출 수"] F --> I["Bucket Key 활성화
KMS 호출 수 대폭 감소"] G --> H G --> I
S3 Bucket Key 활성화 확인 및 비용 최적화
S3 Bucket Key는 버킷 수준에서 단기 데이터 키를 캐싱해 KMS API 호출 횟수를 줄이는 기능이다. 객체마다 KMS를 호출하는 대신, 버킷 키를 통해 로컬에서 데이터 키를 생성하므로 KMS 요청 수가 대폭 감소한다. AWS 관리형 키와 CMK 모두에서 사용할 수 있다.
현재 버킷의 Bucket Key 활성화 여부를 확인하려면:
aws s3api get-bucket-encryption \
--bucket my-example-bucket
CMK를 사용해 Bucket Key를 활성화하며 버킷 암호화를 설정하는 예시:
🔽 CMK + Bucket Key 활성화 CLI 예시 (클릭하여 펼치기)
aws s3api put-bucket-encryption \
--bucket my-example-bucket \
--server-side-encryption-configuration '{
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789012:key/your-cmk-key-id"
},
"BucketKeyEnabled": true
}
]
}'
AWS 관리형 키(aws/s3)에 Bucket Key를 적용하려면, 먼저 아래 명령으로 해당 키의 ARN을 조회한 후, 그 ARN을 KMSMasterKeyID 값으로 명시해야 한다.
aws kms describe-key \
--key-id alias/aws/s3 \
--query 'KeyMetadata.Arn' \
--output text
반환된 ARN을 put-bucket-encryption의 KMSMasterKeyID에 지정하고 BucketKeyEnabled: true를 함께 설정하면 된다.
CMK 생성 및 S3 버킷 암호화 적용 실전 절차
CMK를 직접 생성하고 S3 버킷에 적용하는 전체 흐름이다. 각 단계는 독립적으로 검증 가능하도록 구성했다.
Step 1: CMK 생성
키 정책 없이 기본값으로 생성하면 키 생성자(루트 계정)만 키를 관리할 수 있다. 실제 운영 환경에서는 반드시 키 정책을 명시적으로 정의해야 한다.
aws kms create-key \
--description "S3 encryption key for my-example-bucket" \
--key-usage ENCRYPT_DECRYPT \
--origin AWS_KMS
반환된 KeyMetadata.KeyId 값을 메모해 둔다.
Step 2: 키 별칭 생성 (선택 사항이지만 강력 권장)
ARN 대신 별칭으로 키를 참조하면 키 로테이션이나 교체 시 애플리케이션 코드 변경 없이 별칭만 업데이트할 수 있다.
aws kms create-alias \
--alias-name alias/my-s3-bucket-key \
--target-key-id YOUR_KEY_ID
Step 3: S3 버킷에 CMK 암호화 적용
aws s3api put-bucket-encryption \
--bucket my-example-bucket \
--server-side-encryption-configuration '{
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789012:key/YOUR_KEY_ID"
},
"BucketKeyEnabled": true
}
]
}'
Step 4: IAM 정책 — 최소 권한 원칙 적용
S3 버킷에 객체를 쓰는 IAM 역할에는 KMS GenerateDataKey 권한이, 읽는 역할에는 Decrypt 권한이 필요하다. 두 권한을 모든 역할에 부여하지 말고 역할별로 분리하는 것이 원칙이다.
🔽 S3 쓰기 역할용 IAM 정책 예시 (클릭하여 펼치기)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3KMSEncrypt",
"Effect": "Allow",
"Action": [
"kms:GenerateDataKey",
"kms:DescribeKey"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/YOUR_KEY_ID"
}
]
}
🔽 S3 읽기 역할용 IAM 정책 예시 (클릭하여 펼치기)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3KMSDecrypt",
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:DescribeKey"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/YOUR_KEY_ID"
}
]
}
실제 운영에서 마주치는 함정: 'AccessDenied' 오류의 진짜 원인
CMK로 암호화된 S3 버킷에서 AccessDenied가 발생했을 때, 대부분의 엔지니어는 S3 버킷 정책부터 확인한다. 버킷 정책에 해당 역할의 s3:GetObject가 명시되어 있으면 '왜 안 되지?'라는 생각이 든다.
실제 원인은 KMS 키 정책에 있는 경우가 많다. S3 버킷 정책과 IAM 정책 모두 올바르게 설정되어 있어도, KMS 키 정책에 해당 IAM 역할의 kms:Decrypt가 허용되어 있지 않으면 S3는 복호화 단계에서 실패하고 AccessDenied를 반환한다. 오류 메시지만 봐서는 KMS 문제인지 S3 문제인지 구분이 안 된다.
CloudTrail에서 eventSource: kms.amazonaws.com, eventName: Decrypt, errorCode: AccessDenied 로그를 확인하는 것이 가장 빠른 진단 방법이다.
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=Decrypt \
--start-time 2024-01-01T00:00:00Z \
--end-time 2024-01-02T00:00:00Z \
--query 'Events[?contains(CloudTrailEvent, `AccessDenied`)].CloudTrailEvent'
KMS 키 정책은 IAM 정책과 독립적으로 평가된다. IAM 정책에서 허용하더라도 키 정책에서 명시적으로 허용하지 않으면 접근이 거부된다 — 이것이 KMS 키 정책의 기본 동작 방식이다.
키 유형 선택 의사결정 가이드
필요?"} Q1 -->|예| CMK["CMK 필수"] Q1 -->|아니오| Q2{"키 정책
커스터마이징 필요?"} Q2 -->|예| CMK Q2 -->|아니오| Q3{"규정 준수 감사
(PCI-DSS, HIPAA 등)?"} Q3 -->|예| CMK Q3 -->|아니오| Q4{"키 비활성화/삭제
즉시 제어 필요?"} Q4 -->|예| CMK Q4 -->|아니오| AWS_Managed["AWS 관리형 키 (aws/s3)
충분"] CMK --> BucketKey1["Bucket Key 활성화 권장"] AWS_Managed --> BucketKey2["Bucket Key 활성화 권장"]
AWS KMS와 S3 암호화: 마무리 및 다음 단계
AWS KMS를 활용한 S3 버킷 암호화에서 키 유형 선택의 핵심은 '제어 필요성'이다. 교차 계정 접근, 세밀한 감사, 키 정책 커스터마이징이 필요하다면 CMK를 선택하고, 그렇지 않다면 AWS 관리형 키로 충분하다. 어떤 키 유형을 선택하든 Bucket Key 활성화는 비용 최적화를 위해 기본으로 적용하는 것이 좋다.
다음 단계로 고려할 사항:
- 기존 S3 버킷의 암호화 설정 현황을 AWS S3 암호화 공식 문서를 참고해 점검한다.
- KMS 키 로테이션 정책을 조직의 보안 정책에 맞게 설정한다.
- CloudTrail과 AWS Config를 연동해 키 사용 패턴과 규정 준수 여부를 지속적으로 모니터링한다.
- 비용 최적화를 위해 AWS Cost Explorer에서 KMS API 호출 비용을 주기적으로 검토한다.
핵심 용어 정리
| 용어 | 설명 |
|---|---|
| SSE-KMS | AWS KMS 키를 사용하는 S3 서버 측 암호화 방식. SSE-S3(S3 관리 키)와 구분된다. |
| 봉투 암호화 (Envelope Encryption) | 데이터를 데이터 키로 암호화하고, 데이터 키 자체를 마스터 키로 암호화하는 이중 구조. |
| Bucket Key | S3 버킷 수준에서 단기 데이터 키를 캐싱해 KMS API 호출 횟수를 줄이는 S3 기능. |
| 키 정책 (Key Policy) | KMS CMK에 대한 접근을 제어하는 리소스 기반 정책. IAM 정책과 독립적으로 평가된다. |
| GenerateDataKey | KMS가 평문 데이터 키와 암호화된 데이터 키 쌍을 생성해 반환하는 API. S3 PUT 시 호출된다. |
댓글
댓글 쓰기