長期キーなしで権限を渡す
한국어 원문으로 표시합니다.
목표
역할이 장기 키보다 안전한 것은 맞지만, 신뢰 정책을 느슨하게 쓰면 그 안전이 통째로 사라집니다. 이 실습은 클라우드 계정 없이 신뢰 정책을 직접 쓰고, 위험한 형태를 잡아내는 검사기를 손으로 만들어 그 자리를 확인합니다.
신뢰 정책과 권한 정책
두 문서를 헷갈리면 아무것도 안 맞습니다.
- 권한 정책 — 이 역할이 무엇을 할 수 있는가
- 신뢰 정책 — 누가 이 역할을 맡을 수 있는가
이 실습에서 쓰는 것은 신뢰 정책 쪽입니다. 마지막 단계 하나만 권한 정책입니다.
조건
- 애플리케이션은 EC2 인스턴스 위에서 돕니다.
- 협력사 계정
210987654321에 읽기 권한을 열어 줘야 합니다. - CI 는 저장소
acme/platform의refs/heads/main에서만 배포합니다. - 개발자는 ECS 태스크를 만들 때 태스크 역할을 붙일 수 있어야 합니다.
문법 확인
정책은 대개 콘솔이 아니라 코드로 들어갑니다. JSON 이 깨지면 오류 메시지가 정책 내용과 무관하게 나오므로 매번 확인하는 편이 빠릅니다.
python3 -m json.tool /root/roles/trust-ec2.json
만드는 것
| 파일 | 내용 |
|---|---|
/root/roles/trust-ec2.json |
인스턴스가 맡는 역할 |
/root/roles/trust-partner.json |
외부 계정에 여는 역할 |
/root/roles/trust-ci.json |
연합으로 맡는 역할 |
/root/roles/lint_trust.py |
위험한 신뢰 정책 검사기 |
/root/roles/passrole.json |
역할을 넘기는 권한 |
/root/roles/06-session.md |
세션 시간 계산 |
/root/roles/07-notes.md |
정리 |
참고
4단계의 검사기는 숨겨 둔 정책 넷과 당신이 만든 정책 셋에 함께 돌아갑니다. 위험한 것을 놓쳐도 실패이고, 안전한 것을 잡아내도 실패입니다.
인스턴스가 스스로 자격증명을 받게 한다
EC2 인스턴스가 맡을 수 있는 역할의 신뢰 정책을 /root/roles/trust-ec2.json 에 쓰세요. Version 은 2012-10-17 입니다.
신뢰 정책은 '누가 이 역할을 맡을 수 있는가' 만 정합니다. 무엇을 할 수 있는지는 별도의 권한 정책에 있습니다.
주체가 사람이 아니라 서비스라는 점이 핵심입니다. Principal 에 Service 를 적고, 액션은 역할을 맡는 그 동작 하나입니다.
이 역할을 인스턴스에 붙이면 그 안에서 도는 프로그램이 키 파일 없이 자격증명을 얻습니다. 만료 전에 자동으로 갱신되므로 사람이 손댈 일이 없습니다.
외부 계정에 열 때 한 칸을 더 채운다
협력사 계정 210987654321 이 맡을 수 있는 역할의 신뢰 정책을 /root/roles/trust-partner.json 에 쓰세요. 계정 번호만 적어서는 안 됩니다.
계정 번호만 적으면 그 계정 안의 누구든 이 역할을 맡을 수 있습니다. 협력사 직원 한 사람의 자격증명이 새면 우리 역할이 열립니다.
그래서 양쪽만 아는 값을 조건으로 함께 겁니다. sts:ExternalId 를 StringEquals 로 비교하고, 값은 추측하기 어렵게 여덟 자 이상으로 정하세요.
이 조건이 없으면 그 협력사에 일을 맡긴 제3자가 협력사를 거쳐 우리 역할에 닿을 수 있습니다.
CI 에서 키를 없앤다
CI 러너가 OIDC 로 맡는 역할의 신뢰 정책을 /root/roles/trust-ci.json 에 쓰세요. 저장소 acme/platform 의 refs/heads/main 에서만 맡을 수 있어야 합니다.
연합으로 맡을 때는 액션이 sts:AssumeRole 이 아니라 sts:AssumeRoleWithWebIdentity 이고, 주체는 Principal.Federated 에 적는 신원 공급자입니다.
조건을 빠뜨리면 그 CI 서비스를 쓰는 아무 저장소나 우리 역할을 맡습니다. 가장 자주 나오는 실수이고 조건 한 줄로 막힙니다.
:sub 을 StringEquals 로 못 박고 :aud 도 함께 확인하세요. StringLike 에 * 를 써서 저장소만 적으면 그 저장소의 아무 브랜치에서나 맡을 수 있어, 조건이 없는 것과 크게 다르지 않습니다.
위험한 신뢰 정책을 잡는 검사기
/root/roles/lint_trust.py 를 만드세요. python3 /root/roles/lint_trust.py <정책.json> 으로 부르면 위험한 정책은 무엇이 위험한지 출력하고 종료 코드 1, 안전한 정책은 종료 코드 0 으로 끝나야 합니다.
세 가지를 봅니다.
- 주체가 열려 있는가 —
Principal자체가*이거나Principal.AWS가*인 것 - 외부 계정을
Principal.AWS에 두고도sts:ExternalId조건이 없는 것 sts:AssumeRoleWithWebIdentity인데:sub조건이 없거나 값에*가 있는 것
채점기는 앞 세 단계에서 만든 정책도 이 검사기에 넣어 봅니다. 안전한 것을 잡아내도 실패입니다 — 거짓 경보가 나기 시작하면 사람들은 검사기를 끕니다.
역할을 넘기는 권한을 좁힌다
개발자가 ECS 태스크를 만들 때 태스크 역할을 붙일 수 있어야 합니다. 그 권한을 /root/roles/passrole.json 에 쓰되, 붙일 수 있는 역할과 넘길 서비스까지 좁히세요.
자원을 만들 때 그 자원에 역할을 붙이는 동작은 사실상 '내가 못 하는 일을 대신 하게 만들 수 있는' 권한입니다. Resource 를 * 로 두면 낮은 권한의 사용자가 관리자 역할을 붙인 자원을 만들어 권한을 올릴 수 있습니다.
Resource 에는 붙여도 되는 역할의 ARN 을 적습니다. 이름 앞부분만 정하고 뒤를 여는 방식도 됩니다.
iam:PassedToService 조건으로 어느 서비스에 넘길 때만 되는지도 못 박으세요. 그러면 같은 역할을 다른 서비스에 넘기는 길이 막힙니다.
세션 시간이 곧 대응 속도다
자격증명이 발급되자마자 유출됐다고 봅니다. 알아채는 데 3시간, 대응을 결정하는 데 1시간이 걸립니다. 세션이 12시간일 때와 1시간일 때 권한을 회수한 뒤에도 남는 시간을 각각 계산해 /root/roles/06-session.md 에 세 줄 이상 적으세요.
역할의 권한을 줄여도 이미 발급된 세션은 취소되지 않습니다. 남은 유효 시간만큼은 그대로 쓸 수 있습니다.
회수까지 3 + 1 시간이 걸립니다. 12시간 세션이면 그 뒤로 얼마가 남습니까. 1시간 세션이면 어떻게 됩니까.
숫자를 적어 두면 '세션은 짧게' 라는 말이 취향이 아니라 계산이 됩니다.
세 가지를 정리한다
/root/roles/07-notes.md 에 세 줄 이상. 장기 키와 임시 자격증명의 차이, 신뢰 정책이 정하는 것, 외부와 연합에서 빠뜨리면 안 되는 것.
본문에 만료, 신뢰 정책, 조건 이 들어가야 합니다.