~/infra-dev.md
INFRA_DEV

Terraform secret은 왜 `sensitive`만으로 안전하지 않을까: ephemeral과 write-only로 상태 파일을 줄이기

10분 읽기·2026.07.13·출처 5·00
오늘의 질문

IaC 코드에서 비밀값을 안 보이게 하는 것과 아예 상태 파일에 남기지 않는 것은 어떻게 다를까?

infra-dev.md
Terraform secret state

Terraform secret은 왜 sensitive만으로 안전하지 않을까: ephemeral과 write-only로 상태 파일을 줄이기

1. 왜 지금 봐야 하나

Terraform을 쓰는 팀에서 가장 조용히 쌓이는 위험은 .tf 코드보다 terraform.tfstate에 있다. 코드 리뷰에서는 비밀번호가 보이지 않았는데, 원격 state나 plan artifact에는 DB 초기 비밀번호, API token, provider credential에서 파생된 값이 남아 있을 수 있다.

HashiCorp의 민감 데이터 문서는 이 문제를 직접적으로 설명한다. secret 값을 configuration에 직접 넣으면 Terraform은 그 값을 state와 plan file에 저장할 수 있다. sensitive = true는 CLI 출력에서 값을 가려주는 데 도움이 되지만, “저장하지 않는다”는 뜻은 아니다.

최근 Terraform은 이 빈틈을 줄이기 위해 두 가지 방향을 강화했다.

  • Terraform 1.10: ephemeral resource, ephemeral variable/output처럼 평가 중에만 존재하고 state에 persist하지 않는 값을 도입했다.
  • Terraform 1.11: provider가 지원하는 resource argument에 대해 write-only attribute를 추가했다. 값은 provider로 전달되지만 state에는 저장하지 않는 방식이다.

오늘의 핵심 문장은 이것이다.

인프라 secret 관리의 목표는 값을 예쁘게 가리는 것이 아니라, 값이 지나가는 경로와 저장되는 장소를 줄이는 것이다.

2. 핵심 개념

Terraform에서 민감값을 다루는 방법은 비슷해 보이지만 역할이 다르다.

첫째, sensitive표시 제어에 가깝다. 변수나 output에 sensitive = true를 붙이면 CLI 출력, 일부 로그, plan diff에서 값이 마스킹된다. 하지만 Terraform이 resource 상태를 추적해야 한다면 그 값은 여전히 state에 들어갈 수 있다. 즉 “동료 터미널에 안 보이게 하기”에는 유용하지만 “state backend 관리자도 못 보게 하기”는 아니다.

둘째, ephemeral저장 생략을 목표로 한다. HashiCorp 문서는 ephemeral block이 임시 resource를 정의하며, Terraform이 ephemeral resource 정보를 state나 plan file에 저장하지 않는다고 설명한다. 예를 들어 배포 중에만 필요한 임시 credential, 외부 시스템 연결 정보, 일회성 token처럼 “다음 apply 때 그대로 추적할 필요가 없는 값”에 맞다.

셋째, write-only argument는 resource에 쓰기만 하고 상태로 읽어오지 않는 입력이다. Terraform 1.11 릴리스는 provider가 특정 attribute를 write-only로 지정할 수 있고, 그런 attribute는 state에 persist되지 않는다고 설명한다. 문서상 write-only argument를 쓰려면 Terraform 1.11 이상과 해당 argument를 지원하는 provider/resource가 필요하다.

정리하면 이렇게 구분할 수 있다.

방법주된 효과state 저장 여부적합한 상황
sensitive화면과 출력 마스킹저장될 수 있음출력 노출 줄이기
ephemeral variable/resource평가 중 임시값저장하지 않음일회성 secret, 임시 연결
write-only argumentprovider에 값 전달 후 폐기저장하지 않음초기 비밀번호, token 설정처럼 나중에 읽을 필요 없는 입력
외부 secret manager 참조secret 원문을 Terraform 밖에 둠참조값만 저장되도록 설계 가능운영 secret의 회전과 감사 로그가 필요한 경우

3. 최신 이슈와 연결

Terraform 1.10과 1.11의 변화는 기능 추가라기보다 운영 철학의 변화에 가깝다. 예전 IaC의 기본 모델은 “원하는 최종 상태를 state에 기록하고 다음 실행 때 비교한다”였다. 이 모델은 subnet, instance type, IAM policy처럼 추적 가능한 인프라 속성에는 잘 맞는다.

하지만 secret에는 같은 모델이 위험하다. secret은 “드리프트를 비교해야 하는 값”이 아니라 “노출 면적을 줄여야 하는 값”이다. DB 비밀번호가 바뀌었는지 매번 plan에 보여주는 것보다, secret manager에서 회전되고 애플리케이션이 참조하도록 만드는 편이 안전하다.

HashiCorp가 문서에서 sensitive, ephemeral, write-only를 별도로 설명하는 이유도 여기에 있다. 모든 민감값을 하나의 sensitive = true로 해결할 수 없기 때문이다.

작은 팀에서 특히 위험한 경로는 세 가지다.

  1. CI가 terraform plan 결과를 artifact로 저장한다.
  2. 원격 state backend 접근 권한이 너무 넓다.
  3. incident 대응 중 state 파일을 다운로드해서 Slack, 티켓, 로컬 PC에 복사한다.

이 중 하나만 있어도 “코드에는 secret이 없다”는 말은 충분하지 않다.

4. 개발자 관점 해석

개발자는 Terraform secret을 보안팀 문제로만 보면 안 된다. IaC는 배포 파이프라인, 리뷰 문화, 백업 정책, 장애 대응 runbook과 붙어 있기 때문이다.

첫째, plan file을 로그처럼 취급하면 안 된다. plan은 단순 diff가 아니라 “앞으로 provider에 전달될 구체적 입력”이다. 민감값이 완전히 제거되지 않은 구조라면 plan artifact 보존 기간과 접근 권한도 secret 보관 정책을 따라야 한다.

둘째, state backend는 운영 DB 백업처럼 다뤄야 한다. S3, GCS, Terraform Cloud, HCP Terraform 등 어디에 있든 encryption, access log, 최소 권한, break-glass 절차가 필요하다. state를 읽을 수 있는 권한은 많은 경우 인프라 내부 구조와 secret-like 값을 읽을 수 있는 권한이다.

셋째, provider 지원 여부를 확인해야 한다. write-only argument는 Terraform Core만 올린다고 자동 적용되는 마법이 아니다. 문서가 말하듯 Terraform 1.11 이상과 해당 resource의 write-only 지원이 같이 필요하다. 따라서 “우리 DB provider의 password argument가 write-only인지”, “Kubernetes secret resource가 어떤 값을 state에 남기는지”를 resource별로 확인해야 한다.

넷째, 모든 secret을 Terraform으로 만들 필요가 없다. Terraform은 secret manager, IAM role, service account, network boundary를 연결하는 데 강하지만, 애플리케이션 runtime secret의 생성·회전·사용 이력까지 모두 state로 관리하면 오히려 위험할 수 있다.

5. 내 프로젝트에 적용할 체크포인트

아래 질문에 하나라도 “모름”이 나오면 Terraform secret hygiene 점검이 필요하다.

  1. 원격 state를 읽을 수 있는 사람·CI job·service account 목록이 있는가?
  2. terraform plan artifact가 저장되는 위치와 보존 기간을 알고 있는가?
  3. sensitive = true를 “state에 저장되지 않음”으로 착각한 코드 리뷰 코멘트가 없는가?
  4. DB 초기 password, API token, webhook secret, OAuth client secret이 Terraform state에 남는지 확인했는가?
  5. Terraform 1.10 이상의 ephemeral value를 적용할 수 있는 임시 secret 경로가 있는가?
  6. Terraform 1.11 이상의 write-only argument를 지원하는 provider/resource를 쓰고 있는가?
  7. secret 원문 대신 secret manager의 ARN, resource ID, reference만 state에 남기는 설계인가?
  8. state 다운로드가 incident ticket, 메신저, 로컬 백업으로 퍼지지 않도록 runbook이 있는가?

6. 오늘 10분 액션

오늘은 전체 마이그레이션을 하지 말고, 위험한 값 하나만 추적하자.

  1. 최근 terraform state pull을 실행할 수 있는 안전한 개발/스테이징 workspace 하나를 고른다.
  2. state와 plan artifact에 password, token, secret, client_secret, private_key 같은 문자열이 남는지 검색한다.
  3. 발견한 값마다 아래 표를 만든다.
지금 저장 위치필요한 이유대체 후보
DB 초기 passwordstate최초 생성 시 provider 입력write-only 지원 확인, secret manager 생성 후 참조
webhook secretvariable / plan앱 설정 주입runtime secret manager, CI OIDC로 조회
임시 cloud credentialCI envapply 중 provider 인증OIDC federation, ephemeral variable
  1. 가장 위험한 하나에 대해 “마스킹”, “state 제거”, “secret manager 이전” 중 어떤 단계가 필요한지 이슈를 만든다.

핵심은 sensitive를 지우라는 것이 아니다. sensitive는 계속 필요하다. 다만 sensitive를 마지막 방어선으로 두지 말고, state에 남지 않는 설계를 우선 검토하자는 것이다.

7. 더 볼 자료

  • HashiCorp의 sensitive data 문서는 민감값이 configuration, state, plan에 어떻게 남을 수 있는지와 sensitive, ephemeral, write-only의 차이를 정리한다.
  • Terraform 1.10 릴리스 노트는 ephemeral resource와 ephemeral value가 “state storage에 persist되지 않는다”는 방향을 보여준다.
  • Terraform 1.11 릴리스 노트는 provider가 지원하는 write-only attribute가 state에 저장되지 않는다는 점을 확인할 수 있다.
  • 사용 중인 provider 문서에서 각 resource argument가 write-only를 지원하는지 따로 확인해야 한다.

중복 회피 메모

로컬 content/generated와 Supabase의 최근 infra_dev 글을 확인했다. 최근 글은 GitHub Actions step-level 병렬화, OpenTelemetry semantic convention 카디널리티, RDS delayed replica, GitHub secret scanning triage, Vercel AI Gateway routing, GitHub Actions pull_request_target 보안, OIDC subject/audience, CI 네트워크 failover, Kubernetes Indexed Job 실패 예산, Cloudflare Queues backlog/DLQ를 다뤘다. 이번 글은 Vercel/GitHub OIDC 기반 장기 secret 축소 글과도 겹치지 않도록, Terraform state와 plan file에 secret이 남는 문제, 그리고 Terraform 1.10 ephemeral value와 1.11 write-only argument를 이용해 IaC 저장 면적을 줄이는 관점에 집중했다.

오늘 10분 액션+5%

댓글 0

최신순 ▾
한 줄 남기기

혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)