LabHub
배우기 러닝패스 코스

Infrastructure as Code · 상태 관리 · 실습

sensitive 로 가렸는데 상태 파일에 비밀번호가 그대로 있었다

LabHub 에서 이어서 보기

목표

도구가 만든 비밀번호가 상태·JSON 출력·저장한 계획의 어디에 평문으로 남는지 직접 찾고, OpenTofu 의 상태·계획 암호화를 기존 평문 상태에서 이주해 강제한 뒤, 비밀을 상태 밖에 두는 구성을 만듭니다.

왜 중요한가

sensitive 는 사람이 보는 화면에서 값을 가리는 표시일 뿐, 도구가 저장하는 기록에는 값이 그대로 들어갑니다. 상태 파일과 CI 가 올리는 계획 파일이 곧 비밀 저장소가 되는 셈이라, 이것을 모르면 '가렸으니 안전하다' 고 착각한 채 상태를 아무 곳에나 둡니다. 막는 방법은 세 층입니다: 저장 시 암호화(키가 곧 상태의 열쇠가 됨), 평문으로 되돌아가지 못하게 강제, 그리고 애초에 값을 도구에 넘기지 않고 참조만 넘기는 설계입니다.

단계

1. /root/iac-secret/main.tfrandom_password.db(길이 20, 특수문자 없음), 그 값을 out/db.confpassword=<값> 으로 쓰는 local_sensitive_file.dbconf, 그리고 sensitive = true 인 출력 db_password 를 두세요. init·apply 한 뒤 tofu output 에 값이 가려져 보이는지 확인합니다.
2. 상태 파일에서 비밀번호 값이 들어 있는 모든 자리를 찾아 /root/iac-secret/leak.txt 에 JSON 경로를 한 줄에 하나씩 적으세요. 경로는 키와 배열 번호를 점으로 이은 형식입니다(예: resources.0.instances.0.attributes.x). 채점기는 tofu state pull 로 받은 상태에서 같은 계산을 해 비교합니다.
3. tofu output 의 출력을 /root/iac-secret/masked.txt 에 저장하세요. 그다음 tofu plan -out=/root/iac-secret/leak.tfplan 으로 계획을 저장하고, 계획 파일(zip) 안의 tfstate 항목에 비밀번호가 몇 번 나오는지 세어 /root/iac-secret/planleak.txtplanleak=<수> 로 적으세요.
4. /root/iac-secret/encryption.tf 에 민감한 변수 state_passphrase 와 상태·계획 암호화를 선언하세요: pbkdf2 키 제공자(패스프레이즈는 그 변수), aes_gcm 방법, 그리고 기존 평문을 읽기 위한 unencrypted 방법을 state·plan 의 fallback 으로 둡니다. 패스프레이즈는 labhub-state-passphrase-01 이고 환경 변수 TF_VAR_state_passphrase 로 넘깁니다. apply 하면 terraform.tfstate 가 암호화돼야 합니다(파일에 비밀번호가 없고 encrypted_data 가 있음).
5. encryption.tf 에서 unencrypted 방법과 두 fallback 을 지우고 state·plan 에 enforced = true 를 두세요. 올바른 패스프레이즈로는 계획이 깨끗해야 합니다. 틀린 패스프레이즈(TF_VAR_state_passphrase=wrong-passphrase-000000)로 tofu plan 을 돌린 출력을 /root/iac-secret/wrongkey.txt 에 저장하세요. 채점기는 사본에서 틀린 키로 실제로 읽기가 실패하는지 확인합니다.
6. 올바른 패스프레이즈로 tofu plan -out=/root/iac-secret/safe.tfplan 을 저장하세요. 이 파일에는 비밀번호가 평문으로 없어야 하고 zip 도 아니어야 합니다. 키가 있으면 tofu show -json safe.tfplan 이 되고, 키가 없으면 실패하는지 확인하세요.
7. /root/iac-secret/ref/secret/db.keyopenssl rand -hex 16 으로 비밀을 만들고 권한을 600 으로 두세요(도구 밖에서 만든 비밀). /root/iac-secret/ref/main.tf 에는 random_password 없이 out/db.confpassword_file=<그 경로> 한 줄을 쓰는 local_file.dbconf 만 두고 init·apply 합니다. 채점기는 ref 의 상태 어디에도 db.key 의 값이 없고 경로만 있는지 봅니다.

참고

단계 7개

  1. 비밀번호를 만들고 출력은 가린다
  2. 가린 비밀이 상태 파일의 어디에 있는가
  3. 가림은 화면에서만 — JSON 출력과 저장한 계획
  4. 평문 상태를 암호화 상태로 옮긴다
  5. 이주가 끝나면 평문 대체를 빼고 강제한다
  6. 암호화된 계획 파일은 키 없이는 읽을 수 없다
  7. 비밀은 상태 밖에 두고, 상태에는 참조만 넣는다