infra
Platform

모듈 맵

[Cloud] 계정과 IAM — 최소 권한이라는 안전벨트

0 / 22 완료

펼치기
0 / 22 완료0%

클라우드 엔지니어링 · 03 / 22

[Cloud] 계정과 IAM — 최소 권한이라는 안전벨트

클라우드 사고의 최대 원인인 권한 관리를 다룹니다. 루트 계정 보호, IAM 사용자·그룹·역할(Role)·정책, 최소 권한 원칙을 실무 관점으로 정리합니다

🚨INCIDENT ALERT
HIGH

주니어 개발자가 급한 마음에 액세스 키를 코드에 박고 깃허브 공개 저장소에 푸시했습니다. 3시간 뒤, 봇이 키를 긁어가 전 세계 리전에 고성능 인스턴스 수십 대를 띄워 암호화폐를 채굴했습니다. 다음 날 청구서는 수천만 원. 클라우드 사고의 압도적 1위는 해킹이 아니라 '권한을 잘못 다룬 것'입니다. IAM은 멋진 기능이 아니라 안전벨트입니다.

이번 챕터에서 배울 것
  • 1루트 계정을 왜·어떻게 보호하는지 설명할 수 있다
  • 2IAM 사용자·그룹·역할·정책의 관계를 구분할 수 있다
  • 3역할(Role)과 임시 자격증명이 영구 키보다 안전한 이유를 안다
  • 4최소 권한 원칙을 '좁게 주고 넓히는' 방식으로 실천할 수 있다
  • 5액세스 키 하드코딩 같은 대표 사고 패턴을 피할 수 있다

루트 계정 — 금고에 넣고 거의 열지 않는다

💡개념

루트는 권한을 '제한할 수 없다'

계정을 만들면 처음 주어지는 루트(root) 는 결제 수단 변경, 계정 폐쇄까지 무엇이든 할 수 있고, 그 권한을 줄일 수 없습니다. 그래서 루트가 탈취되면 피해가 무한대입니다.

철칙: ① 루트에 MFA(다단계 인증) 를 걸고, ② 루트 액세스 키는 만들지 않거나 삭제하고, ③ 일상 작업은 권한이 제한된 IAM 사용자/역할로 한다. 루트는 결제 정보 변경처럼 정말 루트만 할 수 있는 일에만, 1년에 몇 번 꺼냅니다.


IAM의 네 조각 — 사용자·그룹·역할·정책

💡개념

누가(주체) × 무엇을 할 수 있나(정책)

IAM은 "누가 무엇을 할 수 있는가"를 정의합니다.

  • 사용자(User): 사람이나 앱의 고정된 정체. 영구 자격증명(비밀번호/액세스 키)을 가짐.
  • 그룹(Group): 사용자 묶음. '개발팀' 그룹에 정책을 붙이면 소속 사용자 전체에 적용 — 사람마다 권한을 따로 주지 않아 관리가 쉬움.
  • 역할(Role): 영구 키 없이 잠깐 빌려 쓰는 권한 묶음. 사람·인스턴스·다른 계정이 '맡아서(assume)' 임시 자격증명을 발급받음.
  • 정책(Policy): "어떤 자원에 어떤 액션을 Allow/Deny" 규칙을 적은 JSON. 위 주체들에 붙여 권한을 부여.

IAM 사용자·그룹·역할·정책의 관계 — 정책(JSON으로 권한 정의)을 사용자(개인)·그룹(사용자 묶음)·역할(임시 자격증명, 서비스·교차계정용)에 연결해 권한 부여. 사람은 그룹으로 권한 관리, 애플리케이션·서비스는 역할로 임시 자격증명을 받는 것이 권장 — 장기 키 노출을 피함확대

최소 권한 원칙 — 필요한 것만, 좁게확대 위 그림처럼 과도한 권한(왼쪽)은 실수 한 번으로 운영 DB 삭제가 가능하지만, 최소 권한(오른쪽)은 피해 범위가 해당 역할 범위로 제한됩니다.

MFA + 역할 전환 — 임시 자격증명 흐름확대 위 그림처럼 MFA 인증 후 STS를 통해 임시 자격증명을 발급받으면, 만료 후 자동 무효화되어 영구 키 유출 위험이 없습니다.

💡개념

왜 역할이 키보다 안전한가

EC2 인스턴스가 S3에 파일을 올려야 한다고 합시다. 나쁜 방법은 액세스 키를 코드/환경변수에 박는 것입니다 — 저장소가 털리면 키가 털립니다. 좋은 방법은 인스턴스에 IAM 역할을 붙이는 것입니다. 그러면 SDK가 자동으로 역할의 임시 자격증명(수 시간마다 자동 회전)을 받아 씁니다. 코드는 키를 전혀 모릅니다. 유출·회전·폐기 문제가 통째로 사라집니다.

왜 IAM 역할이 액세스 키보다 안전한가 — 왼쪽 나쁜 방법은 액세스 키를 코드·환경변수에 박는 것으로, 저장소·로그·깃이 털리면 평문 키가 그대로 유출되고 영구 키라 어디서든 그 권한을 행사할 수 있으며 유출·회전·폐기 관리 부담이 남는다. 오른쪽 좋은 방법은 인스턴스에 IAM 역할을 붙이는 것으로, SDK가 역할의 임시 자격증명을 자동 발급받아(수 시간마다 회전) 코드는 키를 전혀 모르고 유출·회전·폐기 문제가 통째로 사라진다. 원칙은 내부 주체(EC2·Lambda)엔 역할이 기본이고 키는 마지막 수단이며, 외부 CI 등 키가 불가피하면 최소 권한+정기 회전+Secrets Manager 보관, 가능하면 OIDC 페더레이션(키리스)으로, 루트 계정은 MFA 걸고 비상용으로만 쓴다확대

요청 하나가 허용·거부되기까지 — IAM 정책 평가 순서

💡개념

AWS가 요청을 허용/거부하는 법 — IAM 정책 평가

aws s3 rm s3://prod-bucket/db.sql 한 줄이 지워지거나 AccessDenied로 막힙니다. 그 차이는 AWS가 요청을 받을 때마다 돌리는 정책 평가에서 갈립니다. 판정은 감(感)이 아니라 정해진 순서입니다 — 요청을 (주체=principal, 액션=action, 자원=resource, 조건=condition)으로 정규화하고, 그 요청에 적용될 수 있는 모든 정책을 모은 뒤, ① 명시적 Deny가 하나라도 있으면 즉시 거부, ② 없으면 Allow가 하나라도 있어야 허용, ③ 그것도 없으면 암묵적 거부입니다. "분명 권한을 줬는데 왜 막히지"의 답이 대부분 이 순서 안에 있습니다.

TEXT
[요청]  aws s3 rm s3://prod-bucket/db.sql
   │
   ① 요청을 정규화        (principal=이 사용자·역할, action=s3:DeleteObject, resource=prod-bucket/db.sql, condition=출발IP·MFA 등)
   │
   ② 적용 가능한 정책을 모두 수집
   │    · 자격증명 기반(identity) 정책 — 사용자·그룹·역할에 붙은 것
   │    · 자원 기반(resource) 정책 — S3 버킷 정책 등
   │    · SCP(조직 서비스 제어 정책) — 계정 상위 상한
   │    · 권한 경계(permission boundary) — 개별 주체의 상한
   │
   ③ 명시적 Deny가 하나라도 있는가?   (어느 정책이든 Deny면)
   │    → 있으면 즉시 거부 — 다른 Allow가 아무리 많아도 진다
   │
   ④ (Deny 없음) Allow가 하나라도 있는가?
   │    → 없으면 암묵적 거부(기본 거부) — 안 준 것은 곧 막힌 것
   │    → SCP·권한경계가 그 액션을 안 열어줬으면 여기서 막힘
   │
   ⑤ Allow 있음 + 조건 충족 → 허용
   ▼
[실행/거부]  허용이면 삭제, 거부면 AccessDenied (어느 정책이 막았는지는 메시지에 안 나옴)

각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:

단계하는 일여기서 막히면
① 정규화요청을 principal·action(s3:DeleteObjectresource(ARN)·condition(IP·MFA·태그)으로 분해액션명 오해(s3:*로 뭉뚱그림)·리소스 ARN 불일치 → 의도한 정책이 안 걸림
② 정책 수집identity·resource·SCP·권한경계를 모두 모음. 여러 출처가 겹쳐 적용됨상위(SCP·경계)를 못 보고 identity 정책만 보면 "줬는데 왜 안 되지"에 빠짐
③ 명시적 Deny모은 정책 중 Effect: Deny가 하나라도 매칭되면 즉시 거부. Allow보다 항상 우선어딘가의 Deny(보안팀 가드레일·SCP)가 이김 → 아무리 Allow를 더해도 안 뚫림
④ Allow 탐색Deny가 없을 때만, 매칭되는 Allow가 하나라도 있는지 확인Allow 0개 → 암묵적 거부. SCP·권한경계가 상한에서 안 열어준 경우도 여기
⑤ 조건 평가매칭된 문장의 Condition(MFA 여부·SourceIp·시간)까지 만족해야 최종 Allow조건 불충족(예: MFA 없이 호출) → 그 Allow가 무효가 되어 거부

핵심은 세 가지입니다. 첫째, 명시적 Deny가 언제나 이깁니다 — 권한은 "많이 주면 되는" 덧셈이 아니라, 한 곳의 Deny로 전부 막을 수 있는 구조입니다. 둘째, 안 준 것은 곧 막힌 것(암묵적 거부) 이라 최소 권한은 "좁게 주고 필요할 때 넓히는" 방향으로만 안전하게 굴러갑니다. 셋째, 막는 주체가 identity 정책만이 아닙니다 — 자원 정책·SCP·권한 경계가 상위에서 자를 수 있어, 감사할 때 이 네 출처를 모두 봐야 합니다.

진단은 판정을 그대로 재현·추적하는 것입니다. 배포 전 검증은 IAM Policy Simulator(또는 aws iam simulate-principal-policy)로 특정 주체·액션·자원 조합이 allowed인지 미리 돌려 봅니다. 이미 벌어진 거부는 CloudTrail의 해당 이벤트에서 errorCode: AccessDenied와 함께 어떤 principal이 어떤 action을 어느 resource에 시도했는지 봅니다 — 여기에 실제로 쓰는 액션만 실측해 주는 IAM Access Analyzer를 더하면 나머지 권한을 안전하게 깎을 수 있습니다. *(전체 액션·자원)는 이 평가에서 "무엇이든 Allow"라 사고의 폭을 최대로 키우므로, action·resource를 좁히는 것이 진단이자 예방입니다.

IAM 권한 부여 판단 — 무엇을 누구에게, 어떤 방식으로
클라우드 내부 주체(EC2·Lambda)가 자원 접근액세스 키가 아니라 IAM 역할(Role) — 임시 자격증명 자동 회전, 코드에 키 없음. '내부 주체엔 역할이 기본, 키는 마지막 수단'
권한 범위를 얼마나 줄까최소 권한 — 작업에 필요한 액션만. AdministratorAccess·*:* 금지. '일단 넓게 주고 나중에 줄이기'는 영영 안 줄어든다
여러 사용자에게 같은 권한사용자마다 정책 붙이지 말고 그룹(Group)에 정책 → 사용자를 그룹에. 권한 변경이 한 곳에서. '개별 부여는 관리 지옥'
루트 계정을 언제 쓰나거의 안 씀 — 계정 설정·결제 등 루트만 가능한 작업 몇 개뿐. MFA 걸고 금고에. 일상은 IAM 사용자/역할로. '루트는 비상용'
외부 CI 등 키가 불가피최소 권한 정책 + 정기 회전·폐기 + 비밀 저장소(Secrets Manager) 보관, 깃 금지. 가능하면 OIDC 페더레이션(키리스)으로
'권한이 없다'는데 분명 줬다명시적 Deny가 Allow를 이김 + 권한 경계(permission boundary)·SCP가 상위에서 차단할 수 있음. 정책 평가 순서를 본다 — 'Deny 우선'
위험 명령어영구 액세스 키는 한 번 만들면 어디서든 그 권한을 그대로 행사할 수 있어, 코드/로그/깃에 새어 나가면 즉시 침해로 이어집니다. 키가 평문으로 출력되며 다시 볼 수 없어 관리도 까다롭습니다.

액세스 키 발급 — 정말 필요한가?

안전한 실행 조건: EC2/Lambda 등 클라우드 내부 주체는 역할(Role)로 대체 가능하면 키를 만들지 않는다. 외부 CI 등 불가피한 경우만, 최소 권한 정책을 붙이고 정기적으로 회전·폐기한다.

실행 전 반드시 확인

  • 이 작업을 역할(Role)로 대체할 수 없는지 먼저 검토했는가
  • 발급할 키에 최소 권한 정책만 붙였는가(AdministratorAccess 금지)
  • 키를 안전한 비밀 저장소(Secrets Manager 등)에 보관하고 깃에 올리지 않는가
  • 사용하지 않는 오래된 키를 주기적으로 비활성화·삭제하는가
aws iam create-access-key --user-name some-user

위 항목을 모두 확인한 후 복사할 수 있습니다

1권한 점검 — 내 신원과 붙은 정책 확인

현재 사용자에게 어떤 관리형 정책이 붙어 있는지 확인합니다. 과한 권한(특히 Administrator)이 붙어 있지 않은지 점검합니다.

로컬 터미널
ME=$(aws iam get-user --query "User.UserName" --output text)
aws iam list-attached-user-policies --user-name "$ME" --output table
OUTPUT
------------------------------------------------------
|              ListAttachedUserPolicies              |
+----------------------+-----------------------------+
|  PolicyName          |  PolicyArn                  |
+----------------------+-----------------------------+
|  ReadOnlyAccess      |  arn:aws:iam::aws:policy/... |
+----------------------+-----------------------------+
aws iam list-attached-user-policies
2루트가 일상에 쓰이고 있지 않은지 점검

계정 차원의 보안 상태를 요약으로 확인합니다. MFA 미설정·과다 키 같은 위험 신호를 봅니다.

로컬 터미널
aws iam get-account-summary \
  --query "SummaryMap.{Users:Users,MFADevices:MFADevices,AccessKeysPerUserQuota:AccessKeysPerUserQuota}"
OUTPUT
{
    "Users": 5,
    "MFADevices": 5,
    "AccessKeysPerUserQuota": 2
}
aws iam get-account-summary
🔍실행 후 확인할 것
  • list-attached-user-policies에 AdministratorAccess가 보이면 — 일상 사용자에 전권은 위험 신호. 역할 분리·정책 축소 검토
  • Users 수 대비 MFADevices 수 — 사용자보다 MFA 기기가 적으면 MFA 미설정 계정 존재. 전원 MFA가 기본
  • 오래되고 마지막 사용 기록이 없는 액세스 키 — 잊혀진 키는 공격 표면. 비활성화 후 삭제
  • 역할로 대체 가능한데 키를 쓰는 앱/인스턴스 — 영구 키를 역할 기반 임시 자격증명으로 전환 대상

상황: 역할로 받은 임시 자격증명으로 호출하다가 토큰 만료·무효 오류.

원인: 역할의 임시 자격증명은 수명이 있습니다(기본 1시간 등). 오래된 세션을 계속 쓰거나, 캐시된 만료 토큰을 재사용하면 발생. 영구 키와 달리 '잠깐 빌린' 것이라 갱신이 필요합니다.

진단: aws sts get-caller-identity로 현재 자격증명이 살아 있는지 확인. assumed-role 세션이면 만료 시각 점검.

해결: SDK/CLI는 보통 자동 갱신하므로, 직접 토큰을 캐싱했다면 그 로직을 제거하고 SDK가 갱신하도록 맡김. 인스턴스·Lambda는 역할을 붙이면 자동 회전되니 수동 토큰 관리를 하지 않는 게 정답.

심화 — 권한의 뒷면, 맡는 권한과 건네는 권한

💡개념

심화: 역할의 반쪽, 신뢰 정책 — 누가 이 역할을 맡을 수 있는가

본문에서 역할(Role)은 '빌려 쓰는 권한 묶음'이라 했지만, 역할에는 정책이 두 개 붙습니다. 권한 정책(맡으면 무엇을 할 수 있나)과 신뢰 정책(trust policy — 누가 맡을 수 있나)입니다. 실전 사고는 대부분 이 뒷면에서 납니다.

  • 신뢰 정책의 함정: Principal을 계정 루트(arn:aws:iam::계정ID:root)로 지정하면 '그 계정의 관리자만'이 아니라 그 계정에서 sts:AssumeRole이 허용된 모든 주체가 맡을 수 있다는 뜻입니다. 의도보다 훨씬 넓게 열립니다.
  • 외부에 역할을 열 때 — ExternalId: SaaS·파트너 계정에 역할을 줄 때는 신뢰 정책에 ExternalId 조건을 겁니다. 제3자가 남의 계정을 사칭해 우리 역할을 맡는 혼동된 대리인(confused deputy) 공격을 막는 장치입니다.
  • iam:PassRole — 권한 상승의 단골 통로: 역할을 서비스에 '건네주는' PassRole 권한은 사실상 그 역할의 권한을 얻는 것과 같습니다(강한 역할을 붙인 인스턴스를 띄우면 끝). PassRole은 Resource로 '건넬 수 있는 역할'을 반드시 좁혀야 합니다.

정리하면 권한 감사는 '누가 무엇을 할 수 있나'만이 아니라 '누가 어떤 역할을 맡거나 건넬 수 있나' 까지 봐야 완성됩니다.

상황: CI용 IAM 사용자에는 배포용 정책 하나만 붙어 있었습니다. 그런데 유출된 그 키로 새 관리자 사용자 생성, 정책 변경 등 계정 전역의 행위가 이뤄진 흔적이 남았습니다.

원인: 편의로 만든 그 정책 안에 iam:*가 섞여 있었습니다. 공격자는 iam:CreatePolicyVersion으로 자기에게 붙은 고객 관리형 정책의 새 버전을 전권(*:*)으로 만들어 기본 버전으로 지정 — 새 정책을 붙이지 않고 기존 정책을 '부풀려' 관리자로 승격했습니다(권한 상승, privilege escalation). 최소 권한이 무너지는 지점은 서비스 권한이 아니라 IAM 자체를 만지는 권한입니다.

진단: CloudTrail에서 CreatePolicyVersion·SetDefaultPolicyVersion·AttachUserPolicy·CreateAccessKey 이벤트를 시간순으로 추적하고, aws iam list-policy-versions로 정책 버전 이력을 확인해 언제 어떤 버전이 기본이 됐는지 봅니다.

해결: 유출 키 즉시 비활성화·삭제, 부풀려진 정책 버전 삭제·원복, 공격자가 만든 뒷문(새 사용자·키·역할) 전수 제거. 재발 방지로 ① 사람·CI 정책에서 iam:* 제거(IAM 변경은 별도 관리 경로로만), ② 권한 경계(permission boundary)로 '정책을 아무리 바꿔도 넘을 수 없는 상한'을 설정, ③ CI는 영구 키 대신 OIDC 페더레이션으로 전환합니다.

💼
실무 맥락
현업 패턴

실무에서 IAM은 '한 번 설정하고 끝'이 아니라 지속적으로 줄여나가는 대상입니다. 처음엔 일이 막히지 않게 넓게 줬다가, Access Analyzer·CloudTrail로 "실제로 쓰는 권한"을 확인해 안 쓰는 권한을 깎아냅니다. 보안 감사에서 가장 먼저 보는 것도 "전권을 가진 사용자가 몇 명인가"입니다.

조직이 커지면 계정을 여러 개로 나누고(운영/개발/결제 분리) 중앙에서 가드레일을 거는 멀티계정 거버넌스로 확장됩니다 — 관측과 거버넌스에서 다룹니다. 권한 관리는 왜 클라우드인가에서 본 '책임 공유 모델'에서 고객 책임의 핵심입니다.

관련 모듈로 더 깊이:

  • 시크릿·키 관리 — IAM으로 통제한 권한 위에서 API 키·인증서·시크릿을 보관·교체하는 법
  • 관측과 거버넌스 — 다계정·조직 단위로 권한과 자원을 통제하는 거버넌스 가드레일
  • 가상 서버(EC2) — IAM 권한으로 실제 자원을 띄우는 첫 단계, 가상 서버

다음 모듈에서는 본격적으로 컴퓨트로 들어가, 가상 서버(EC2)를 띄울 때 만나는 인스턴스 타입·AMI·키페어·보안그룹을 다룹니다.

지식 확인

퀴즈 — 8문제

Q1

루트(root) 계정을 일상 작업에 쓰면 안 되는 이유로 가장 적절한 것은?

Q2

IAM '역할(Role)'이 IAM '사용자(User)'와 근본적으로 다른 점은?

Q3

'최소 권한 원칙(least privilege)'을 가장 잘 실천한 예는?

Q4

애플리케이션이 클라우드 API에 접근할 때 권장되는 방식은?

Q5

루트(root) 계정 보안을 위해 가장 먼저 해야 할 조치는?

Q6

사용자 20명에게 같은 권한을 주고 이후 관리도 쉽게 하려면?

Q7

[심화] 개발자에게 인스턴스 생성 권한과 함께 iam:PassRole을 Resource 제한 없이(*) 허용했다. 이 조합이 위험한 가장 큰 이유는?

Q8

[심화] 배포 권한만 준 CI 사용자의 키가 유출됐는데, 새 정책을 붙인 흔적은 없는데도 계정 전역에 관리자급 변경이 일어났다. 가장 먼저 의심할 경로와 확인 지표는?

0 / 8 답변

🧪 실습으로 확인하기

GCP VPC 방화벽 — 외부 접속 안 됨 진단과 규칙 설계

중급

VM에 웹 서버를 띄웠는데 외부 접속이 안 된다. GCP VPC 방화벽은 기본이 "암묵적 거부"다. 현재 규칙을 진단하고, 네트워크 태그 기반으로 최소 범위의 ingress 규칙을 만들어 80/443만 정확히 연다. SSH는 되는데 HTTP가 안 되는 이유를 규칙 우선순위로 설명한다.

40📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요