쿠버네티스 운영 실무 · Secret 보관 방법 · 실습
etcd 안의 평문을 직접 확인하고 막기
목표
Secret 이 실제로 어떻게 저장되는지 etcd 를 직접 열어 확인하고, 저장 시 암호화·봉투 암호화·접근 통제·외부 저장소라는 네 겹의 대응을 각각 매니페스트로 작성합니다.
왜 중요한가
가장 많이 퍼진 오해가 "Secret 은 base64 라 안전하다"입니다. base64 는 키가 없는 표현 방식이라 되돌리는 데 명령 하나면 되고, 더 중요한 것은 기본 설정에서 apiserver 가 Secret 을 평문 그대로 etcd 에 쓴다는 사실입니다. 앞 모듈에서 배운 etcd 스냅샷은 그래서 클러스터에서 가장 민감한 파일이기도 합니다. 이 실습에서는 그 사실을 남의 말이 아니라 자기 손으로 확인합니다. 대응은 한 겹이 아닙니다. EncryptionConfiguration 은 디스크를 얻은 사람을 막고, KMS 봉투 암호화는 그 키마저 클러스터 밖으로 뺍니다. 그러나 저장을 암호화해도 API 로 읽을 수 있는 사람에게는 소용이 없으므로 RBAC 가 필요하고, resourceNames 로 이름 단위까지 좁힐 수 있지만 그것이 list/watch 에는 통하지 않는다는 한계도 알아야 합니다. 마지막으로 값을 아예 클러스터 밖에 두는 외부 시크릿 방식까지, 네 겹이 각각 무엇을 막고 무엇을 못 막는지 구분하는 것이 이 실습의 핵심입니다.
단계
1. 네임스페이스 secret-lab 을 만들고 그 안에 Secret db-password 를 만드세요. 타입은 Opaque 이고 키 password 의 값은 labhub-Pr0d-2026 입니다. 뒤에서 권한을 비교할 대조군으로 Secret other-secret 도 하나 만드세요.
2. db-password 의 data.password 값을 그대로 /root/ops/secrets/out/encoded.txt 에, 그것을 디코딩한 평문을 /root/ops/secrets/out/decoded.txt 에 저장하세요(디코딩 결과는 labhub-Pr0d-2026 이어야 하고 두 파일은 서로 대응해야 합니다). 그리고 /root/ops/secrets/out/base64-note.txt 에 base64 가 인코딩일 뿐 암호화가 아니라는 결론을 한 줄로 적으세요.
3. /root/ops/secrets/encryption-config.yaml 을 쓰세요. apiVersion 은 apiserver.config.k8s.io/v1, kind 는 EncryptionConfiguration 이고 resources[0].resources 에 secrets 가 들어가야 합니다. providers 의 첫 번째는 실제 암호화 프로바이더(aescbc, secretbox, aesgcm 중 하나)이며 keys[0].name 이 있어야 하고, 마지막은 identity 여야 합니다.
4. /root/ops/secrets/kms-config.yaml 을 쓰세요. kind 는 EncryptionConfiguration 이고 providers[0] 는 kms 여야 합니다. kms.apiVersion 은 v2, kms.endpoint 는 unix:// 로 시작하는 소켓 경로, kms.name 도 있어야 합니다. 그리고 /root/ops/secrets/out/envelope-note.txt 에 봉투 암호화의 두 층 구조(데이터 키와 마스터 키)를 설명해 적으세요.
5. /root/ops/secrets/external-secret.yaml 에 문서 두 개를 쓰세요. 하나는 kind: SecretStore, 다른 하나는 kind: ExternalSecret 입니다. ExternalSecret 에는 spec.refreshInterval, spec.secretStoreRef.name, spec.target.name, spec.target.creationPolicy, spec.data[0].remoteRef.key 가 모두 있어야 합니다. 이 파일에 실제 비밀번호 labhub-Pr0d-2026 이 들어가면 안 됩니다.
6. secret-lab 에 ServiceAccount db-client 를 만들고, Role db-secret-reader 를 만드세요. 규칙은 secrets 리소스에 대해 resourceNames 는 db-password 하나, verbs 는 get 하나뿐이어야 합니다. 이 역할을 db-client 에게 RoleBinding 으로 거세요. 결과적으로 db-client 는 db-password 는 읽고, other-secret 은 읽지 못하며, 시크릿 목록 조회(list)도 못 해야 합니다. 그리고 /root/ops/secrets/out/resourcenames-note.txt 에 resourceNames 가 list/watch 에는 통하지 않는다는 점을 적으세요.
7. secret-lab 에 Secret pinned-config 를 만드세요. immutable: true 이고 키 build 의 값은 2026-08-20 입니다. 그다음 이 값을 바꾸려고 시도하고 거부 메시지를 /root/ops/secrets/out/immutable-error.txt 에 저장하세요. 저장 후에도 build 값은 여전히 2026-08-20 이어야 합니다.
8. etcd 에서 /registry/secrets/secret-lab/db-password 키를 조회한 출력을 /root/ops/secrets/out/etcd-secret.txt 에 저장하세요. 이 파일 안에 평문 labhub-Pr0d-2026 이 보여야 합니다. 그리고 /root/ops/secrets/out/secrets-audit.json 을 만드세요. total_secrets 는 secret-lab 의 실제 Secret 개수와 일치해야 하고, encrypted_at_rest 는 false, plaintext_visible_in_etcd 는 true 입니다. mitigations 배열에는 대응 방안을 3개 이상 적되 저장 시 암호화(EncryptionConfiguration 또는 "암호화")와 접근 권한 통제(RBAC 또는 "권한")가 반드시 포함돼야 합니다.
참고
- 이 랩의 etcd 는 클라이언트 TLS 없이
127.0.0.1:2379에서 듣습니다.etcdctl --endpoints=127.0.0.1:2379 get /registry/secrets/secret-lab/db-password로 조회할 수 있습니다. - 쿠버네티스는 오브젝트를 JSON 이 아니라 protobuf 로 etcd 에 저장합니다. 그래서 출력이 깨진 바이너리처럼 보이는 것이 정상이고, 그 안에 사람이 읽을 수 있는 문자열이 섞여 있습니다. 비밀번호는 그 안에 base64 가 아니라 평문 그대로 들어 있습니다 — 그것이 이 단계의 요점입니다. 읽기 편하게 보려면
| strings나| hexdump -C로 훑고, 파일에는 조회 출력을 그대로 저장하세요(널 바이트가 거슬리면| tr -d '\0'로 걷어 내면 됩니다). - 3번의 암호화 키는 32바이트 무작위 값을 base64 로 인코딩해 씁니다.
head -c 32 /dev/urandom | base64로 만들 수 있습니다. - 7번의 수정 시도는
kubectl patch나kubectl apply어느 쪽이든 됩니다. 오류 메시지는 표준 오류로 나가므로2>&1로 받아야 파일에 남습니다. - 8번의 시크릿 개수는
kubectl get secrets -n secret-lab -o json | jq '.items | length'로 세는 것이 안전합니다. 손으로 세면 어긋나기 쉽습니다. - 흔한 실수 1: 3번에서
identity를 목록 맨 앞에 두는 것. 쓰기에 쓰이는 것은 첫 번째 프로바이더이므로 그 순간 평문 저장으로 되돌아갑니다. - 흔한 실수 2: 6번에서
verbs에list를 함께 넣는 것.resourceNames는 목록 조회를 좁혀 주지 못하므로 모든 시크릿이 노출됩니다. - 흔한 실수 3: 5번 매니페스트에 실제 값을 적어 두는 것. 값이 없다는 점이 외부 시크릿 방식의 존재 이유입니다.
- 실습 파드는 실습마다 새로 뜨므로 앞 실습에서 만든 클러스터 상태는 남아 있지 않습니다. 네임스페이스와 Secret 은 이 실습 안에서 직접 만드세요. 운영 절차를 기억이 아니라 런북과 매니페스트로 남겨야 하는 이유가 바로 이것입니다.
단계 8개
- 실습용 Secret 두 개 만들기
- base64 를 직접 되돌려 보기
- 저장 시 암호화 설정 쓰기
- KMS v2 봉투 암호화 설정 쓰기
- 외부 시크릿 저장소 연동 매니페스트 쓰기
- 시크릿 하나만 여는 역할 만들기
- 불변 Secret 만들고 수정 거부 확인하기
- etcd 에서 평문을 꺼내 증거로 남기기