Terraform 및 KMS로 AWS 교차 계정 비밀 마스터하기

발행: (2025년 12월 3일 오후 11:05 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

멀티 계정 현실 점검

현대 클라우드 아키텍처에서는 멀티‑계정 전략이 표준입니다. 개발 환경과 프로덕션 환경을 분리하고, 종종 공유 서비스를 자체 허브로 중앙화합니다.

아주 흔한 시나리오는 Security 혹은 Shared Services AWS 계정에 민감한 데이터베이스 자격 증명을 AWS Secrets Manager에 보관하고, 이를 전혀 다른 Workload 계정에서 실행되는 Lambda 함수가 접근해야 하는 경우입니다.

이론적으로는 간단해 보입니다:

  1. 계정 A에서 비밀을 생성합니다.
  2. 비밀에 리소스 정책을 붙여 계정 B가 읽을 수 있도록 허용합니다.
  3. 계정 B의 Lambda에 비밀을 읽을 IAM 권한을 부여합니다.

Terraform을 배포하고 Lambda를 호출했지만… 실패합니다. AccessDeniedException 혹은 모호한 KMS 오류가 발생합니다.

Secrets Manager(또는 S3, SQS)에서 크로스‑계정 접근 오류를 디버깅할 때 개발자들의 90 %는 IAM 정책에만 집중합니다. 하지만 비밀이 관여될 때는 인증(IAM)암호화(KMS) 두 전선에서 싸워야 합니다.

기본적으로 Secrets Manager에서 암호화 설정을 지정하지 않고 비밀을 만들면 AWS는 서비스용 AWS‑관리형 키(alias/aws/secretsmanager)를 사용해 암호화합니다.

함정: AWS‑관리형 키의 키 정책은 수정할 수 없습니다. 이 키는 동일 계정 내 주체만을 신뢰하도록 설계되었습니다. IAM 정책을 아무리 넓게 열어도 외부 계정은 페이로드를 복호화할 수 없습니다. 문은 열려 있지만 상자는 용접돼 있습니다.

크로스‑계정 접근을 허용하려면 암호화를 직접 제어해야 합니다. KMS에 고객 관리형 키(CMK) 를 생성하고 외부 계정을 명시적으로 신뢰하도록 설정합니다.

Terraform 구현

변수와 데이터 소스

variable "external_consumer_account_id" {
  description = "The AWS Account ID that needs read access to the secrets."
  type        = string
  # Example: "123456789012"
}

# Helper to get current account ID for policy definitions
data "aws_caller_identity" "current" {}

KMS 키와 별칭

resource "aws_kms_key" "cross_account_secrets_key" {
  description             = "KMS Key for cross-account RDS credentials"
  deletion_window_in_days = 30
  enable_key_rotation     = true

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "EnableIAMUserPermissionsForCurrentAccount"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
        }
        Action   = "kms:*"
        Resource = "*"
      },
      {
        Sid    = "AllowExternalAccountToDecrypt"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${var.external_consumer_account_id}:root"
        }
        Action = [
          "kms:Decrypt",
          "kms:DescribeKey"
        ]
        Resource = "*"
      }
    ]
  })
}

resource "aws_kms_alias" "secrets_key_alias" {
  name          = "alias/cross-account-secrets-key"
  target_key_id = aws_kms_key.cross_account_secrets_key.key_id
}

Secrets Manager 비밀

resource "aws_secretsmanager_secret" "database_credentials" {
  name        = "prod/rds/read-replica-creds"
  description = "Database credentials accessible by workload accounts"

  # CRITICAL: Force the use of our custom KMS key
  kms_key_id = aws_kms_key.cross_account_secrets_key.id
}

비밀 리소스 정책

resource "aws_secretsmanager_secret_policy" "database_credentials_policy" {
  secret_arn = aws_secretsmanager_secret.database_credentials.arn

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "AllowExternalRead"
        Effect    = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${var.external_consumer_account_id}:root"
        }
        Action   = "secretsmanager:GetSecretValue"
        Resource = "*"
      }
    ]
  })
}

필요한 권한

크로스‑계정 비밀 조회가 성공하려면 세 가지 관문이 동시에 열려야 합니다:

  1. KMS 키 정책(소스 계정)이 대상 계정에게 Decrypt 권한을 부여해야 합니다.
  2. 비밀 리소스 정책(소스 계정)이 대상 계정에게 GetSecretValue 권한을 부여해야 합니다.
  3. IAM 역할(대상 계정, Lambda/EC2에 연결)이 두 ARN에 대해 각각의 작업을 수행할 권한을 가져야 합니다.

멀티‑계정 아키텍처에서 가장 흔한 함정은 기본 AWS 키를 사용하는 것입니다. 고객 관리형 키로 전환하고 정책 정의를 Terraform에서 명시적으로 다룸으로써 안전하고 재현 가능한 크로스‑계정 접근을 보장할 수 있습니다.

Back to Blog

관련 글

더 보기 »

S3에 Terraform 상태 저장

S3를 Terraform 백엔드로 구성하기 Terraform은 상태를 S3 버킷에 저장할 수 있습니다. 아래는 S3 백엔드를 설정하는 최소 구성 예시입니다: hcl terrafor...