AWS OpenSearch Access Policy Principal
정의 (Definition)
AWS OpenSearch의 Access Policy Principal은 도메인에 대한 IAM 리소스 기반 정책(Resource-based Policy) 의 핵심 요소로, 네트워크 도달 가능성(VPC/Public)이나 내부 인증(Basic Auth)과 독립적으로 “누가(Which IAM Entity)” 요청을 보낼 자격이 있는지를 결정하는 1차 보안 경계입니다.
문제가 되는 배경 (Problem Context)
OpenSearch 도메인이 VPC 내부에 존재하거나 Master User/Basic Auth가 설정되어 있다는 이유로, 편의상 Access Policy의 Principal을 AWS: "*" 또는 AWS: "...:root"로 넓게 설정하는 경우가 많습니다. 이는 “네트워크가 격리되었으니 안전하다”는 착각에서 비롯되지만, 실제로는 공격 면적(Attack Surface)을 계정 전체 혹은 전 세계로 확장하는 결과를 초래합니다.
핵심 메커니즘 (How it Works)
OpenSearch 접근 제어는 두 가지 축으로 작동하며, Principal은 그중 첫 번째 관문입니다.
- IAM Access Policy (Authorization): 요청이 도메인에 도달했을 때, “이 주체가 API를 호출할 권한이 있는가?”를 검사합니다.
- Internal Auth (Authentication): “이 사용자가 누구인가?”(Basic Auth, FGAC)를 검사합니다.
설정 패턴 비교
| 패턴 | 설정 예시 (Principal) | 의미 | 위험도 | 비고 |
|---|---|---|---|---|
| Wildcard | { "AWS": "*" } | 모든 AWS 계정의 주체 허용 | 🚨 Critical | Public Access와 동급. 조건(Condition) 필수. |
| Account Root | { "AWS": "arn:aws:iam::111:root" } | 해당 계정 내 모든 IAM 주체 허용 | ⚠️ High | 계정 내 침해 사고 시 방어력 상실. 편의성은 높음. |
| Specific Roles | { "AWS": ["arn:..:role/A", "arn:..:role/B"] } | 명시된 특정 역할만 허용 | ✅ Safe | 최소 권한 원칙 준수. 운영 오버헤드 있음. |
❌ 위험한 설정 (Wildcard Principal)
access_policies = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { AWS = "*" } # 위험: 타 계정 포함 모든 주체 허용
Action = "es:*"
Resource = "arn:aws:es:${region}:${account}:domain/${domain_name}/*"
}]
})✅ 권장 설정 (Specific Roles)
접근이 필요한 Workload IRSA와 Bastion Role만 명시하여 권한 경계를 명확히 합니다.
access_policies = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
AWS = concat(
values(module.workload_irsa_roles)[*].arn, # 워크로드 Role 자동 수집
[aws_iam_role.bastion.arn] # 운영자(Bastion) Role
)
}
Action = "es:*"
Resource = "arn:aws:es:${region}:${account}:domain/${domain_name}/*"
}]
})위험 시나리오: Principal: "*" + Action: "es:*" 상태라면, 공격자가 유효한 AWS 자격 증명(아무 계정)으로 SigV4 서명을 해 요청을 보내면, Basic Auth(비밀번호) 단계를 우회하고 통과될 수 있습니다.
불변 조건과 보장 범위 (Invariants & Guarantees)
- 보장:
Principal에 명시되지 않은 IAM Entity는 (설령 올바른 Basic Auth 정보를 가졌더라도) IAM 레벨에서 거부됩니다(Explicit Deny 우선). - 비보장: “VPC 내부에 있다”는 사실이 “IAM 권한이 있다”는 것을 의미하지 않습니다. VPC는 네트워크 경로일 뿐, IAM 인증 인가가 아닙니다.
- 비보장: “엔드포인트 주소를 모른다”는 보안 경계가 아닙니다(Security through obscurity is not security).
비유 (Analogy)
- VPC: “폐쇄형 아파트 단지” (물리적/네트워크 격리)
- Access Policy (
Principal): “현관문 도어락의 등록된 지문”
*: 아파트 단지에 들어온 아무나(배달원, 이웃, 침입자) 문을 열 수 있음.root: 같은 가족(계정) 이면 문을 열 수 있음. (철없는 동생이나 도둑맞은 가족의 지문도 포함)Specific Role: 가장(Admin)과 허락된 자녀(Workload) 만 문을 열 수 있음.
실무적 함의 (Operational Implications)
- 계정 Root(
:root)의 함정::root를 쓰면 운영은 편하지만(Role 추가 시 정책 수정 불필요), 계정 내 불필요한 Role(CI, 테스트용 등)이나 탈취된 내부 자격증명이 공격 경로가 됩니다. - Role 목록 관리 자동화:
Specific Roles방식은 Role 추가 시마다 정책을 업데이트해야 하는 오버헤드가 있습니다. 이를 줄이기 위해 Terraform 등에서concat(),values()등을 활용해 Role 목록을 동적으로 수집하는 패턴을 권장합니다. - 예외적 허용: 샌드박스 환경 등에서 부득이하게 범위를 넓힐 때는
Principal을 넓히더라도 반드시Condition: { "StringEquals": { "aws:SourceVpce": "..." } }를 병행해야 합니다.
주의사항 / 오해 (Pitfalls & Misconceptions)
- “비밀번호(Basic Auth)가 있으니 괜찮다?”: IAM 정책에서 허용되면 SigV4 서명으로 비밀번호 없이 통과될 수 있는 경로가 존재합니다.
- “Private VPC니까 외부에서 못 들어온다?”: VPN, TGW, VPC Peering, 혹은 내부의 탈취된 컨테이너(SSRF 등)를 통해 네트워크 장벽은 우회될 수 있습니다. 이때 IAM 정책이 최후의 방어선입니다.
References