SAABlog
스토리지중급

S3 버킷 정책 작성법: IAM 정책과 뭐가 다를까?

S3 버킷 정책과 IAM 정책의 차이점, JSON 작성법, 크로스 계정 액세스 설정까지 실전 예제로 완벽 정리합니다.

PHILOLAMB-
S3버킷 정책IAM권한크로스 계정

관련 시험 도메인

  • Domain 1: Design Secure Architectures

핵심 요약

버킷 정책은 "이 버킷에 누가 접근할 수 있는가"를 정의하고, IAM 정책은 "이 사용자가 무엇을 할 수 있는가"를 정의합니다. 크로스 계정 액세스나 퍼블릭 액세스가 필요하면 버킷 정책을, 내부 사용자 권한 관리는 IAM 정책을 사용하세요.

시험 팁

시험 핵심: 버킷 정책은 Principal(누가) 요소가 필수입니다. IAM 정책에는 Principal이 없습니다(연결된 사용자가 주체). 크로스 계정 액세스 시 버킷 정책 AND 상대방 IAM 정책 둘 다 Allow해야 접근 가능합니다.

구분버킷 정책IAM 정책
연결 대상S3 버킷 (리소스)IAM 사용자/역할/그룹
Principal 요소필수 (누가 접근하는가)없음 (연결된 주체가 암묵적)
퍼블릭 액세스가능 ("Principal": "*")불가능
크로스 계정단독 가능역할 위임 필요
적용 범위특정 버킷만사용자의 모든 리소스

버킷 정책 vs IAM 정책: 언제 무엇을 써야 할까?

버킷 정책을 사용해야 할 때

버킷 정책 사용 케이스:
├── 퍼블릭 웹사이트 호스팅 (모든 사용자 읽기 허용)
├── 크로스 계정 액세스 (다른 AWS 계정에 권한 부여)
├── 특정 IP/VPC에서만 접근 허용
├── CloudFront OAC 연동
└── 서비스 Principal 접근 (CloudTrail, ELB 로그 등)

IAM 정책을 사용해야 할 때

IAM 정책 사용 케이스:
├── 동일 계정 내 사용자/역할 권한 관리
├── 여러 S3 버킷에 대한 권한을 한 곳에서 관리
├── EC2 인스턴스 프로파일로 S3 접근
├── Lambda 함수 실행 역할로 S3 접근
└── 세밀한 사용자별 권한 제어

선택 기준 플로우차트

S3 권한 설정이 필요한가?
│
├── 동일 계정 내부 사용자인가?
│   └── YES → IAM 정책 (+ 필요시 버킷 정책 보완)
│
├── 다른 AWS 계정에서 접근해야 하는가?
│   └── YES → 버킷 정책 필수 (+ 상대방 IAM 정책)
│
├── 퍼블릭 액세스가 필요한가?
│   └── YES → 버킷 정책 필수 (+ Block Public Access OFF)
│
└── AWS 서비스가 접근해야 하는가? (CloudTrail, ELB 등)
    └── YES → 버킷 정책 (Service Principal 사용)

버킷 정책 JSON 구조

기본 구조

{
  "Version": "2012-10-17",
  "Id": "ExampleBucketPolicy",
  "Statement": [
    {
      "Sid": "StatementIdentifier",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::bucket-name/*",
      "Condition": {}
    }
  ]
}

각 요소 설명

요소필수설명
Version정책 언어 버전 (항상 "2012-10-17" 사용)
Id정책 식별자 (선택사항)
Statement권한 규칙 배열 (여러 개 가능)
Sid문장 식별자 (가독성용)
EffectAllow 또는 Deny
Principal권한 대상 (누가)
Action허용/거부할 작업 (무엇을)
Resource적용 대상 리소스 (어디에)
Condition조건부 적용 (어떤 조건에서)

Principal 작성법

Principal 유형별 작성 예시

// 1. 모든 사용자 (퍼블릭)
"Principal": "*"

// 2. 특정 AWS 계정
"Principal": {"AWS": "arn:aws:iam::123456789012:root"}

// 3. 특정 IAM 사용자
"Principal": {"AWS": "arn:aws:iam::123456789012:user/username"}

// 4. 특정 IAM 역할
"Principal": {"AWS": "arn:aws:iam::123456789012:role/rolename"}

// 5. AWS 서비스
"Principal": {"Service": "cloudfront.amazonaws.com"}

// 6. 여러 Principal
"Principal": {
  "AWS": [
    "arn:aws:iam::111111111111:root",
    "arn:aws:iam::222222222222:root"
  ]
}

시험 팁

시험 포인트: "Principal": "*"는 **모든 사용자(익명 포함)**를 의미합니다. "Principal": {"AWS": "*"}인증된 모든 AWS 계정을 의미합니다. 둘은 다릅니다!


Action 작성법

자주 사용하는 S3 Action

Action설명적용 대상
s3:GetObject객체 읽기객체
s3:PutObject객체 업로드객체
s3:DeleteObject객체 삭제객체
s3:ListBucket버킷 내 객체 목록 조회버킷
s3:GetBucketLocation버킷 리전 조회버킷
s3:*모든 S3 작업전체

Action과 Resource 매핑

// 객체 레벨 작업 → Resource에 /* 필요
{
  "Action": ["s3:GetObject", "s3:PutObject"],
  "Resource": "arn:aws:s3:::bucket-name/*"
}

// 버킷 레벨 작업 → Resource에 버킷만
{
  "Action": "s3:ListBucket",
  "Resource": "arn:aws:s3:::bucket-name"
}

// 둘 다 필요한 경우 → Resource 배열 사용
{
  "Action": ["s3:ListBucket", "s3:GetObject"],
  "Resource": [
    "arn:aws:s3:::bucket-name",
    "arn:aws:s3:::bucket-name/*"
  ]
}

시험 팁

시험 함정: s3:ListBucket버킷에, s3:GetObject객체에 적용됩니다. Resource ARN을 잘못 지정하면 권한이 작동하지 않습니다!


실전 버킷 정책 예제

1. 정적 웹사이트 퍼블릭 읽기

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-website-bucket/*"
    }
  ]
}

2. 크로스 계정 액세스 허용

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CrossAccountAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:root"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::shared-bucket",
        "arn:aws:s3:::shared-bucket/*"
      ]
    }
  ]
}

3. 특정 IP 범위에서만 접근 허용

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::secure-bucket",
        "arn:aws:s3:::secure-bucket/*"
      ],
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": ["192.168.0.0/24", "10.0.0.0/8"]
        }
      }
    }
  ]
}

4. VPC Endpoint에서만 접근 허용

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VPCEndpointOnly",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::private-bucket",
        "arn:aws:s3:::private-bucket/*"
      ],
      "Condition": {
        "StringNotEquals": {
          "aws:SourceVpce": "vpce-1234567890abcdef0"
        }
      }
    }
  ]
}

5. CloudFront OAC 연동

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontOAC",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::cdn-bucket/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE"
        }
      }
    }
  ]
}

6. HTTPS 강제 (HTTP 차단)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyHTTP",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::secure-bucket",
        "arn:aws:s3:::secure-bucket/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}

Condition 키 활용법

자주 사용하는 Condition 키

Condition 키용도예시 값
aws:SourceIpIP 주소 제한"192.168.1.0/24"
aws:SourceVpceVPC Endpoint 제한"vpce-xxx"
aws:SourceVpcVPC 제한"vpc-xxx"
aws:SecureTransportHTTPS 강제"true" / "false"
s3:x-amz-aclACL 조건"bucket-owner-full-control"
s3:prefix접두사 제한"folder/"

Condition 연산자

연산자설명
StringEquals문자열 정확히 일치
StringNotEquals문자열 불일치
StringLike와일드카드 일치 (*, ?)
IpAddressIP 범위 내
NotIpAddressIP 범위 외
BoolBoolean 조건

크로스 계정 액세스 설정

양쪽 모두 설정 필요

크로스 계정 액세스는 버킷 소유 계정접근 계정 양쪽에서 설정이 필요합니다.

크로스 계정 액세스 설정:

[계정 A: 버킷 소유자]
├── S3 버킷 정책
│   └── Principal: 계정 B 허용
│   └── Action: 필요한 작업 허용
│
[계정 B: 접근자]
├── IAM 정책 (사용자/역할에 연결)
│   └── Resource: 계정 A의 버킷 ARN
│   └── Action: 필요한 작업 허용

예시: 계정 B가 계정 A의 버킷 접근

계정 A (버킷 소유자) - 버킷 정책:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::222222222222:role/DataAccessRole"},
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": [
        "arn:aws:s3:::account-a-bucket",
        "arn:aws:s3:::account-a-bucket/*"
      ]
    }
  ]
}

계정 B (접근자) - IAM 정책:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": [
        "arn:aws:s3:::account-a-bucket",
        "arn:aws:s3:::account-a-bucket/*"
      ]
    }
  ]
}

시험 팁

시험 핵심: 크로스 계정 시 버킷 정책 Allow + IAM 정책 Allow = 접근 가능. 둘 중 하나라도 없으면 접근 불가! 단, 동일 계정 내에서는 버킷 정책만으로도 접근 가능합니다.


정책 평가 로직

Allow와 Deny 우선순위

정책 평가 순서:
1. 명시적 Deny가 있으면? → 무조건 거부 (최우선)
2. 명시적 Allow가 있으면? → 허용
3. 둘 다 없으면? → 암묵적 거부 (기본값)

핵심: Deny는 Allow보다 항상 우선!

여러 정책이 있을 때

동일 계정:
├── IAM 정책 Allow + 버킷 정책 없음 → 허용
├── IAM 정책 없음 + 버킷 정책 Allow → 허용
├── IAM 정책 Allow + 버킷 정책 Allow → 허용
└── 어디서든 Deny → 거부

크로스 계정:
├── 버킷 정책 Allow + IAM 정책 Allow → 허용
├── 버킷 정책 Allow + IAM 정책 없음 → 거부
├── 버킷 정책 없음 + IAM 정책 Allow → 거부
└── 둘 다 Allow 필수!

시험 출제 포인트

자주 나오는 문제 유형

유형핵심 포인트
Principal 차이버킷 정책에만 Principal 있음, IAM 정책엔 없음
크로스 계정양쪽 모두 Allow 필요 (버킷 정책 + IAM 정책)
Resource ARNListBucket은 버킷, GetObject는 객체(/*)
HTTPS 강제aws:SecureTransport 조건 사용
VPC 제한aws:SourceVpce 또는 aws:SourceVpc 사용
Deny 우선명시적 Deny는 모든 Allow보다 우선

오답 함정

❌ IAM 정책에 Principal을 지정한다
   → IAM 정책에는 Principal 요소가 없음

❌ s3:GetObject는 버킷 ARN에 적용한다
   → 객체 ARN(버킷/*) 에 적용해야 함

❌ 크로스 계정은 버킷 정책만 있으면 된다
   → 상대방 계정의 IAM 정책도 필요

❌ Allow가 있으면 Deny를 무시한다
   → 명시적 Deny는 항상 최우선

❌ 퍼블릭 액세스는 IAM 정책으로 설정한다
   → 버킷 정책 + Block Public Access OFF 필요

FAQ

Q1: 버킷 정책과 IAM 정책 중 어떤 것을 먼저 확인해야 하나요?

둘 다 확인해야 합니다. 동일 계정에서는 둘 중 하나만 Allow해도 접근 가능하지만, 크로스 계정에서는 양쪽 모두 Allow가 필요합니다. Deny는 어디에 있든 최우선으로 적용됩니다.

Q2: 버킷 정책 크기 제한이 있나요?

20KB까지 작성 가능합니다. 복잡한 정책이 필요하면 IAM 정책과 분리하거나, S3 Access Points를 활용하세요.

Q3: 버킷 정책과 ACL 중 무엇을 써야 하나요?

버킷 정책을 사용하세요. ACL은 레거시이며, 2023년부터 새 버킷은 기본적으로 ACL이 비활성화됩니다. 버킷 정책이 더 세밀하고 관리하기 쉽습니다.

Q4: "Access Denied" 에러가 나는데 원인을 어떻게 찾나요?

  1. Block Public Access 설정 확인 (퍼블릭 접근 시)
  2. 버킷 정책의 Principal, Action, Resource 확인
  3. IAM 정책 확인 (크로스 계정 시 양쪽)
  4. 명시적 Deny 존재 여부 확인
  5. VPC Endpoint 정책 확인 (VPC 내부 접근 시)

Q5: 여러 Statement를 하나의 정책에 넣어도 되나요?

, Statement는 배열이므로 여러 규칙을 하나의 정책에 포함할 수 있습니다. 하지만 가독성과 관리를 위해 Sid로 각 Statement를 명확히 구분하세요.


마무리

S3 버킷 정책은 리소스 기반 권한 관리의 핵심입니다. 핵심 포인트:

  1. 버킷 정책: "누가 이 버킷에 접근할 수 있는가" (Principal 필수)
  2. IAM 정책: "이 사용자가 어디에 접근할 수 있는가" (Principal 없음)
  3. 크로스 계정: 양쪽 모두 Allow 필요
  4. Deny 우선: 명시적 Deny는 모든 Allow를 무시
  5. Resource ARN: 버킷 vs 객체 구분 필수

다음으로 S3 암호화 옵션IAM 정책 평가 로직을 학습하면 S3 권한 관리를 완성할 수 있습니다.