원문: How to Restrict Amazon S3 Bucket Access to a Specific IAM Role
나는 AWS의 클라우드 서포트 엔지니어인데 고객들이 종종 어떻게 아마존 S3 버킷에 대한 접근을 특정 AWS IAM 역할
로 제한하느냐 묻곤 한다. 일반적으로 그들은 IAM 사용자와 같은 방식으로 이것을 시도한다: 접근 승인을 하고 싶지 않은 Principals
(정책에서 자원에 대한 접근이 호용되거나 거부되는 보안 주체를 지정하는 요소)를 모두 거부(Deny
)한다. 이 방식의 문제점은 버킷 정책 유지가 요구된다는 점이다. 만약 모든 작업(Action
)에 대해 "s3:\*"
가 적용된 새로운 IAM 사용자는 버킷에 접근할 수 있을 것이다. 접근을 차단하고 싶은 사용자 목록을 특정시키기 보다 logic을 반전시키고 NotPrincipal
요소를 버킷 정책의 Deny
명령문에 추가하는 방법을 사용할 수 있다. 이 요소는 목록에 들어가 있지 않은 어떤 사용자도 명시적으로 Deny
한다.
하지만 이런 반전 논리 접근법은 IAM 역할에 관한 문제를 갖고 있다. 역할의 Principal
은 역할(role)ARN
(아마존 리소스 네임)과 수행자 역할(assumed-role)ARN
이라는 두 ARN
으로 구성되어있기 때문이다. 역할 ARN
은 IAM 역할
그 자체의 식별자이고 수행자 역할 ARN
은 로그에서 무엇이 역할 세션을 식별하는지를 알려준다. NotPrincipal
요소를 사용할 때 두 ARN
을 포함시켜야만 실패하지 않고 두 번째 ARN
(수행자 역할 ARN
)을 변수 이름에 포함시켜야한다. 보통 변수 문자열이 가는 곳에 와일드카드(\*
)를 명시할텐데 이는 Principal
요소 혹은 NotPrincipal
요소에서도 허용되지 않는다. 이 포스팅에서 나는어떻게 IAM 역할 혹은 사용자의 S3 버킷에 대한 접근을 NotPrincipal
요소 대신 Conditions
를 사용해 제한할 수 있는지 설명할 것이다. Admin 정책
을 소유하는 계정과 같은 계정을 갖는 사용자 혹은 "s3:\*"
정책이 적용된 사용자라 하더라도 목록에 명시되어있지 않는 한 접근이 거부될 것이다. 이러한 방법을 사용해 예를 들어, Auto Scaling group
에 있는 인스턴스의 버킷에 대한 접근을 설정할 수도 있다. 또한, 높은 보안 수준을 요구하는 버킷에 대한 접근도 제한할 수 있다.
솔루션 개요
이 포스트에서 제안하는 해결법은 S3
의 모든 API에 대한 접근 권한을 가지고 있는 경우에도 S3 버킷
에 대한 접근을 통제하기 위해 버킷 정책을 사용한다. 아래 그림은 같은 계정 안에서 어떤 식으로 동작하는지 묘사하고 있다.
1. IAM 사용자의 정책과 역할의 사용자 정책이 "s3:*"로의 접근을 승인한다.
2. S3 버킷 정책은 오직 역할만으로 접근을 제한한다.
3. IAM 사용자와 역할은 계정이 소유하는 버킷에 접근할 수 있다. 역할은 두 버킷에 전부 접근할 수 있지만 사용자는 버킷 정책이 적용되지 않은 버킷에만 접근할 수 있다. 역할과 사용자 둘 다 "s3:*" 허가를 갖고 있다 할지라도 버킷 정책은 역할이 부여되지 않은 모든 사람들의 버킷에 대한 접근을 무효화한다.
앞서 말한 경우와 교차 계정의 경우의 가장 큰 차이점으로 모든 버킷이 반드시 버킷 정책
을 가지고 있어야 한다는 것을 꼽을 수 있다. 아래 그림은 교차 계정의 경우에 어떻게 동작하는지 보여준다.
1. 버킷 계정의 IAM 역할의 사용자 정책과 IAM 사용자의 정책은 "s3:*"로의 접근을 승인한다.
2. 버킷 정책은 만약 user:id가 역할과 일치하지 않는 모두의 접근을 거부하고 정책은 역할이 버킷으로 무엇을 할 수 있는지 정의한다.
3. 버킷 정책은 다른 계정에서의 역할에 대한 접근을 허가한다.
NotPrincipal 요소와 사용법을 이해하기
IAM
혹은 S3 버킷 정책
의 NotPrincipal
요소를 특정 사용자 집단의 자원에 대한 접근을 제한하기 위해 사용할 수 있다. 이 요소는 값 배열에 정의되지 않은 모든 사용자를 차단한다. 그들의 IAM
사용자 정책에서 Allow
가 적혀있다고 해도 말이다. 결과적으로 S3
에 있는 특정 버킷을 제외한 모든 버킷에 접근해야하는 사용자가 있다면 사용자의 IAM 정책 스택
을 수정하지 않고 이를 버킷에 정의할 수 있다.
IAM 역할
에 있어서, Principal
이 두개 ARN
으로 역할이 정의되어 있기 때문에 사실 더 복잡하다: 역할 ARN
과 수행자 역할 ARN
. 역할 ARN(arn:aws:iam::ACCOUNTNUMBER:role/ROLE-NAME)
은 정적이고 역할 세션을 사용하기 시작한 사람과는 별개이다. (이 포스트 전체에 적혀있는 모든 빨간색 대문자 대신 본인의 정보를 기입해야 한다는 사실을 기억하기 바란다). 수행자 역할 ARN(arn:aws:sts::ACCOUNTNUMBER:assumed-role/ROLE-NAME/ROLE-SESSION-NAME)
은 역할 세션 이름이 어떻게 정의되어 있느냐에 따라 달라진다. 역할을 수행한 사용자가 API를 호출하기 위해 만든 아래 AWS CloudTrail
의 Identity
요소를 보면 이를 확인할 수 있다.
{
"type": "AssumedRole",
"principalId": "AROAJI4AVVEXAMPLE:ROLE-SESSION-NAME",
"arn": "arn:aws:sts::ACCOUNTNUMBER:assumed-role/ROLE-NAME/ROLE-SESSION-NAME",
"accountId": "ACCOUNTNUMBER",
"accessKeyId": "ASIAEXAMPLEKEY",
"sessionContext": {
"attributes": {
"mfaAuthenticated": "false",
"creationDate": "XXXX-XX-XXTXX:XX:XXZ"
},
"sessionIssuer": {
"type": "Role",
"principalId": "AROAJI4AVV3EXAMPLEID",
"arn": "arn:aws:iam::ACCOUNTNUMBER:role/ROLE-NAME",
"accountId": "ACCOUNTNUBMER",
"userName": "ROLE-SESSION-NAME"
}
}
}
위의 자격증명(Identity
) 요소 내에 역할 ARN
과 수행자 역할 ARN
이 들어있는 것이 보일 것이다. ROLE-SESSION-NAME 은 잠재적으로 누가 역할을 수행하는지에 따라 변경된다. principalID
값은 이 정보를 포함하지만 버킷 정책의 Principal요소
밖에서 사용되는 형식으로 포맷된다. 나는 버킷 정책을 기술할 때 이 정보를 사용할 것이다.
특정 역할에 같은 계정 버킷 접근을 승인하기
같은 계정에서 버킷에 접근할 때 대부분의 경우에는 버킷 정책을 사용하지 않아도 된다. 사용자의 IAM 정책
에서 이미 버킷 정책이 접근을 정의하고 있기 때문이다. S3 버킷 정책
은 보통 교차 계정이 접근할 때 사용되지만 버킷처럼 같은 계정에 속해있든 다른 계정에 속해있든 모든 Principal
에 적용될 명시적인 Deny
를 통해 접근을 제한할 때에도 사용할 수 있다.
각 IAM 엔티티
(사용자, 그룹 혹은 역할)는 aws:userid
값이 정의되어있다. 버킷 정책에서 조건적으로 역할 혹은 사용자를 특정해 제외하려면 이 값이 필요할 것이다. 수행자 역할의 aws:userId
값은 UNIQUE-ROLE-ID:ROLE-SESSIOM-NAME (예를 들어, AROAEXAMPLEID:userdefinedsessionname
)와 같이 정의된다.
IAM 역할
을 위해 AROAEXAMPLEID
를 얻기 위해 아래를 따라하면 된다.
1. AWS CLI를 설치하고 커맨드 프롬프트 혹은 셸을 연다.
2. aws iam get-role --role-name ROLE-NAME을 실행한다.
3. 출력값으로 RoleID 문자열이 나올 것이다. AROA로 시작한다. 이 역할만이 버킷에 접근하도록 버킷 정책에서 이를 사용면 된다.
앞서 보여준 CloudTrail
코드 예시에서 이 ID는 principalId
요소이다. 이 요소의 값은 중요한데 AWS 정책 값이 IAM 정책
에서도 문자열로 확인되기 때문이다. NotPrincipal
요소에 있는 역할과 수행자 역할 ARN
을 명시하는 대신 와일드카드 문자열과 함께 StringNotLike
조건 속에 있는 aws:userId
값을 사용할 수도 있다. aws:userId
값에 루트 사용자
(처음 계정을 만든 사용자)를 정의된 역할이 삭제되어 아무도 접근하지 못하게 될 때에 대비해 추가해야 한다. 루트 계정의 userId
는 계정 숫자이다.
AWS CLI
를 통해 얻은 AROAEXAMPLEID
를 사용하는 것은 이 역할을 사용하는 사용자들을 위해서만 작동하는 버킷에 접근하는 버킷 정책의 조건 로직을 만들 수 있다. 조건 로직을 사용하는 것은 NotPrincipal
요소가 어떠한 역할 세션 이름이라도 승인되는 와일드카드 문자열을 허용하는 것 보다 낫다.
접근을 허가하기 위한 ID를 이제는 갖고 있으니 같은 계정의 버킷에 다른 사용자들이 접근하지 못하도록 해야 한다. 버킷에의 접근을 차단하는 정책과 IAM 역할
혹은 루트 계정
을 사용하지 않는 사용자들의 객체는 아래와 같은 자격증을 발급한다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::MyExampleBucket",
"arn:aws:s3:::MyExampleBucket/*"
],
"Condition": {
"StringNotLike": {
"aws:userId": [
"AROAEXAMPLEID:*",
"111111111111"
]
}
}
}
]
}
IAM 사용자들을 위한 같은 정책에 사용해도 된다. IAM 사용자는 AIDA
로 시작하는 특이한 ID를 갖고 있는데 이 목적으로 사용할 수 있다. AIDA
로 시작하는 특이한 ID를 얻기 위해서:
1. AWS CLI가 설치된 상태에서 커맨드 프롬프트나 셸을 기동시킨다.
2. aws iam get-user --user-name (USER-NAME)을 실행한다.
3. 출력값으로 userId 문자열을 얻고 이는 AIDAEXAMPLEID와 같은 형식일 것이다.
식별된 userId
를 얻었으면 아래 예시와 같이 "aws:userId"
조건 배열에 대입하면 된다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::MyExampleBucket",
"arn:aws:s3:::MyExampleBucket/*"
],
"Condition": {
"StringNotLike": {
"aws:userId": [
"AROAEXAMPLEID:*",
"AIDAEXAMPLEID",
"111111111111"
]
}
}
}
]
}
특정 IAM 역할을 통한 교차 계정 버킷 접근 승인하기
앞에서 같은 계정 IAM 역할
혹은 사용자의 S3 버킷
에의 접근 제한 방법을 서술했다. 지금부터 교차 계정의 경우를 설명할 것이다. IAM 사용자 혹은 역할을 통한 교차 계정 버킷 접근을 승인할 때 IAM 사용자 혹은 역할이 무엇을 위해 접근이 허용된 것인지 정의해야 한다. 앞서 올라온 AWS 보안 블로그에서 Jim Scharf는 CLI/API와 콘솔을 통한 버킷 접근을 위한 IAM 엔티티를 허용하는 법에 대해 적었다. 이 정보를 활용하면 CLI/API 차원의 접근을 서술한 버킷 정책은 아래와 같다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/ROLENAME"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::MyExampleBucket"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/ROLENAME"
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::MyExampleBucket/*"
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::MyExampleBucket",
"arn:aws:s3:::MyExampleBucket/*"
],
"Condition": {
"StringNotLike": {
"aws:userId": [
"AROAEXAMPLEID:*",
"111111111111"
]
}
}
}
]
}
작업을 수행하기 위해서는 아래와 같은 콘솔에서의 IAM 역할
전환 기능을 동반한 콘솔 차원의 접근이 요구된다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/ROLENAME"
},
"Action": [
"s3:ListAllMyBuckets",
"s3:GetBucketLocation"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/ROLENAME"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::MyExampleBucket"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/ROLENAME"
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::MyExampleBucket/*"
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::MyExampleBucket",
"arn:aws:s3:::MyExampleBucket/*"
],
"Condition": {
"StringNotLike": {
"aws:userId": [
"AROAEXAMPLEID:*",
"111111111111"
]
}
}
}
]
}
다른 계정의 IAM 사용자에게 API/CLI 접근 승인을 하기 위해서는 IAM 사용자를 위해 앞서 했던 것과 같이 "aws:userId"
에 AIDAEXAMPLEID
를 대입해야 한다. "aws:userId"
조건에 IAM 사용자의 이러한 정책의 Principal
요소에 대한 모든 ARN
을 추가해야 한다. IAM 사용자에 교차 계정 콘솔을 승인할 수 없다는 것을 기억해야 한다. 왜냐 하면, 사용자는 목적 계정에서 역할을 수행해야 하기 때문이다. 하지만 API/CLI를 통해 버킷에의 접근을 승인할 수 있다. 아래와 같이.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": [
{
"AWS": [
"arn:aws:iam::222222222222:role/ROLENAME",
"arn:aws:iam::222222222222:user/USERNAME"
]
}
],
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::MyExampleBucket"
},
{
"Effect": "Allow",
"Principal": [
{
"AWS": [
"arn:aws:iam::222222222222:role/ROLENAME",
"arn:aws:iam::222222222222:user/USERNAME"
]
}
],
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::MyExampleBucket/*"
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::MyExampleBucket",
"arn:aws:s3:::MyExampleBucket/*"
],
"Condition": {
"StringNotLike": {
"aws:userId": [
"AROAEXAMPLEID:*",
"AIDAEXAMPLEID",
"111111111111"
]
}
}
}
]
}
게다가 버킷 정책에 역할 허가를 포함하려면 IAM 사용자 혹은 역할 사용자의 정책에 이러한 허가를 정의해야 한다. 허가는 직접 관리하는 정책에 추가될 수 있고 IAM 콘솔의 역할 혹은 사용자에 적용할 수 있다:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets",
"s3:GetBucketLocation"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::MyExampleBucket"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::MyExampleBucket/*"
}
]
}
이 포스팅을 따라하면 Admin
정책을 가진 사용자 혹은 "s3:*"
가 적용된 로컬 계정 혹은 교차 계정의 특정 IAM 역할 혹은 사용자를 S3 버킷에 제한 할 수 있게 된다. (이하생략)
'AWS 노트' 카테고리의 다른 글
DataWarehouse와 DateLake의 차이 (0) | 2020.04.15 |
---|---|
aws-cdk가 Argument of type 'this' is not assignable to parameter of type 'Construct'. 에러를 뿜을 때 (0) | 2020.04.10 |
Lambda 함수 실행기 (4) AWS Cloud Watch 이용해서 주기적으로 Lambda 함수 실행시키는 CDK Stack 구축하기 (0) | 2020.04.09 |
로컬에서 AWS CDK Stack 빌드해서 Lambda 함수 실행시키기 (0) | 2020.04.08 |
AWS Lambda Python 실행기 (3) 특정 Lambda 함수로만 특정 S3 Bucket에 접근 가능하도록 IAM역할 부여하기 (0) | 2020.04.07 |