8편에서 CreateAccount는 새 계정의 루트 자격증명을 통째로 돌려줬다. 편했지만, 그러면서 정직하게 인정한 게 있다 — 이건 AWS가 실제로 하는 안전한 방식이 아니라고. 장기 루트키는 유출되면 영원히, 전권으로 뚫린다. 이번 편은 그 빚을 갚는다. 루트키를 넘기는 대신, 역할을 빌려 임시 자격증명으로 들어간다.
역할은 신분이 아니라 빌리는 모자
지금까지 mincloud의 모든 신원은 영구 자격증명을 가졌다. 루트도, IAM 유저도 각자 키+시크릿이 있고, 안 지우면 영원히 유효했다.
역할(Role)은 다르다. 자기 자격증명이 없다. 역할은 잠깐 쓰고 벗는 모자다. 두 가지를 가진다 — 이 모자를 쓰면 뭘 할 수 있나(권한), 그리고 누가 이 모자를 써도 되나(신뢰 정책). 아무도 역할로 로그인하지 않는다. 자기 자신으로 시작해서, 역할을 빌려서(assume) 잠깐 그것이 된다.
그러니 순서가 정해진다. 역할이 있어야 빌릴 수 있다. CreateRole이 AssumeRole보다 먼저다.
먼저: 역할을 만든다
멤버 계정을 하나 만들고(8편의 그 문), 그 계정 안에 역할을 만든다. 핵심은 --assume-role-policy-document — 신뢰 정책이다.
$ aws iam create-role --role-name OrgAccess \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::000000000001:root" },
"Action": "sts:AssumeRole"
}]
}' --endpoint-url http://localhost:9910
arn:aws:iam::207346699092:role/OrgAccess
이 JSON이 이 시리즈에서 처음 등장하는 진짜 정책 문서다. 8편의 인가는 isManagementRoot라는 하드코딩 한 줄이었다. 여기선 “누가 이 역할을 빌려도 되나"를 데이터로 적는다 — “관리 계정(000000000001)의 루트만.” 서버는 나중에 이 문서를 읽어 판단한다.
| |
하드코딩 게이트가 처음으로 읽어서 평가하는 정책이 된 순간이다.
다음: 역할을 빌린다
이제 관리 루트(마스터)로 그 역할을 빌린다.
$ aws sts assume-role \
--role-arn arn:aws:iam::207346699092:role/OrgAccess \
--role-session-name jeff-session \
--endpoint-url http://localhost:9900
돌아오는 건 영구키가 아니다. 임시 자격증명이다.
AccessKeyId: ASIADARWTRNKAN3U4M7T ← ASIA (임시) 프리픽스
Expiration: 2026-07-06T16:36:01+00:00 ← 1시간 뒤 죽음
AssumedRole: arn:aws:sts::207346699092:assumed-role/OrgAccess/jeff-session
AKIA가 아니라 ASIA로 시작한다 — STS가 발급한 임시키의 표식이다. 그리고 세 조각이 더 있다: SecretAccessKey, SessionToken, 그리고 만료 시각.
이 임시키로 신원을 물으면, 우리는 멤버 계정 안에 들어가 있다.
$ AWS_ACCESS_KEY_ID=ASIA... AWS_SECRET_ACCESS_KEY=... AWS_SESSION_TOKEN=... \
aws sts get-caller-identity --endpoint-url http://localhost:9900
{
"UserId": "AROAVX9IJ90X1PM0DI57:jeff-session",
"Account": "207346699092",
"Arn": "arn:aws:sts::207346699092:assumed-role/OrgAccess/jeff-session"
}
계정 207346699092. 관리 루트는 이 멤버의 루트키를 한 번도 손에 쥐지 않았다. 자기 키로 역할을 잠깐 빌렸을 뿐이고, 1시간 뒤엔 이 임시키도 죽는다. 유출돼도 피해 반경이 “영원"이 아니라 “한 시간"이다. 이게 8편이 못했던, AWS의 안전한 방식이다.
session token과 만료는 서버가 지킨다
임시키는 그냥 키+시크릿이 아니다. 요청마다 SessionToken(X-Amz-Security-Token 헤더)을 같이 실어야 하고, 만료 전이어야 한다. Authenticate가 서명을 검증한 뒤 이 둘을 더 본다.
| |
영구키엔 이 둘이 비어 있어 그냥 지나간다. 임시키만 이 관문을 더 통과한다.
신뢰하지 않으면 거부한다
신뢰 정책은 관리 계정만 적어 뒀다. 그럼 멤버 계정의 루트 자신이 이 역할을 빌리려 하면?
$ ...(멤버 루트 키로)... aws sts assume-role \
--role-arn arn:aws:iam::207346699092:role/OrgAccess --role-session-name intruder
An error occurred (AccessDenied) when calling the AssumeRole
operation: User: arn:aws:iam::207346699092:root is not authorized
to perform: sts:AssumeRole on resource: .../OrgAccess
AccessDenied. 8편에선 이 판단이 하드코딩 한 줄이었지만, 지금은 역할에 붙은 JSON 문서가 내린 결정이다. 신뢰 정책에 없으면 못 빌린다.
루프가 닫혔다
8편에서 멤버 계정을 만들었고, 9편에서 그 계정에 안전하게 들어가는 법을 얻었다. 진짜 AWS의 OrganizationAccountAccessRole이 바로 이거다 — 계정 만들 때 관리 계정을 신뢰하는 역할을 하나 심어두고, 관리 계정이 그걸 assume해서 들어간다. 우리가 손으로 한 create-role(신뢰=관리 계정) + assume-role이 정확히 그 그림이다. 마법이 아니라, 원시 도구 두 개의 조합.
정직하게 남는 것도 있다. 지금 CreateRole엔 권한 검사가 없다 — 인증만 되면 누구나 자기 계정에 역할을 만든다. 그리고 역할은 신뢰 정책(누가 빌리나)은 있지만, 아직 권한 정책(빌린 뒤 뭘 할 수 있나)은 없다. 임시키는 지금 사실상 그 계정의 전권을 갖는다.
그 두 구멍이 다음 편을 가리킨다. 오늘 trust policy로 “누가 빌리나"를 데이터로 판단하기 시작했으니, 같은 엔진을 “누가, 무슨 행동을, 무슨 리소스에” 로 일반화하면 — 그게 IAM 정책, 진짜 인가 엔진이다. 하드코딩 한 줄에서 시작해 신뢰 정책까지 온 이 흐름의 종착지다.