SecOps 프로젝트를 하나 맡게 되었다. 팀 근두운의 인프라를 세우는 일이었고, 자연스럽게 팀원들에게 AWS 권한을 나눠 줘야 했다. 그런데 여기서 묘한 딜레마에 부딪혔다. 인프라를 뿌리려면 팀원들이 직접 리소스를 만들 수 있어야 하는데, 하필 우리가 하는 일이 SecOps다. CloudTrail을 켜 두고 계정 전체의 API 호출을 기록하는 마당에, 그 로그에서 팀원의 일탈을 발견하고 싶지는 않았다. 팀원을 믿지 않아서가 아니라, 애초에 실수하거나 오해받을 여지 자체를 줄여 두고 싶었다.
가장 간단한 답은 각자에게 필요한 권한만 정밀하게 재단해 주는 것이다. 그러나 초기 단계의 프로젝트에서 누가 무엇을 필요로 할지 미리 다 알 수는 없다. 권한을 좁게 주면 매번 "이것도 막혔어요"라는 문의가 쏟아지고, 넓게 주면 SecOps의 명분이 무색해진다. 그래서 반대로 접근했다. 하나하나 허용하는 대신, 거의 다 허용하되 넘지 말아야 할 선만 상한으로 정해 두는 방식이다. AWS에서 이 상한 역할을 하는 것이 Permissions Boundary다. 그 경계를 설계하고 운영하며 겪은 일을 계기로, IAM 권한 위임 방법론을 조사해봤다.
리소스 권한과 IAM 권한은 위험의 결이 다르다. 팀원에게 s3:*나 ec2:*를 열어 주는 것은 최악의 경우 그 서비스 범위 안에서의 사고로 끝난다. 그러나 IAM 권한, 특히 iam:CreateRole, iam:PutRolePolicy, iam:AttachRolePolicy, iam:PassRole를 무심코 열어 주면 이야기가 달라진다. 이 조합은 권한의 상한 자체를 스스로 끌어올릴 수 있게 하기 때문이다. 이것을 권한 상승(privilege escalation)이라 부른다.
가장 전형적인 순서는 이렇다.
iam:CreateRole로 새 역할을 하나 만든다.iam:AttachRolePolicy로 그 역할에 AdministratorAccess를 붙인다.iam:PassRole로 그 역할을 내가 통제할 수 있는 자원(예: Lambda, EC2 인스턴스 프로파일)에 넘긴다.또는 더 짧게, 내가 이미 맡을(assume) 수 있는 역할에 iam:PutRolePolicy로 인라인 관리자 정책을 꽂아 넣는 방법도 있다. iam:PassRole은 가장 대표적인 권한 상승 경로 중 하나이며, IAM 위임 시 반드시 통제해야 한다 아무리 강력한 역할을 만들어도, 그 역할을 어떤 실행 주체에게 건네주는 행위가 막혀 있으면 그림의 떡이다.
그래서 IAM 권한 위임에서 실제로 통제해야 하는 것은 역할 생성 가능 여부가 아니라, 만들 수 있는 역할의 최대 권한 상한이다. Permissions Boundary가 이 지점을 담당한다.
Permissions Boundary는 IAM 사용자나 역할에 붙이는 두 번째 정책이다. 권한을 부여하는 정책이 아니라 그 주체가 가질 수 있는 권한의 상한을 정의하는 정책으로, 유효 권한은 아이덴티티 정책과 경계가 모두 허용한 것의 교집합이다. 아이덴티티 정책에서 AdministratorAccess를 줬더라도 경계가 허용하지 않는 행위는 불가능하다.
내가 근두운 계정에 적용한 경계 정책의 뼈대는 다음과 같다.
{
"Sid": "AllowEverythingByDefault",
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
시작은 전부 허용이다. 화이트리스트가 아니라 블랙리스트 방식을 택했다. 초기 프로젝트에서 팀원의 자율성을 최대한 살리되, 넘지 말아야 할 선만 명시적으로 Deny하는 전략이다. IAM에서 명시적 Deny는 어떤 Allow보다도 우선하므로, 이 아래에 쌓는 Deny들이 그대로 권한의 상한이 된다.

그 위에 성격이 다른 Deny를 네 갈래로 얹었다.
먼저, 계정의 소유권과 지갑을 잠갔다.