LabHub
배우기 러닝패스 코스

Kubernetes運用実務

etcdの中の平文を自分で確認して塞ぐ

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

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-passworddata.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 을 쓰세요. apiVersionapiserver.config.k8s.io/v1, kindEncryptionConfiguration 이고 resources[0].resourcessecrets 가 들어가야 합니다. providers첫 번째는 실제 암호화 프로바이더(aescbc, secretbox, aesgcm 중 하나)이며 keys[0].name 이 있어야 하고, 마지막identity 여야 합니다.
  4. /root/ops/secrets/kms-config.yaml 을 쓰세요. kindEncryptionConfiguration 이고 providers[0]kms 여야 합니다. kms.apiVersionv2, kms.endpointunix:// 로 시작하는 소켓 경로, 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 리소스에 대해 resourceNamesdb-password 하나, verbsget 하나뿐이어야 합니다. 이 역할을 db-client 에게 RoleBinding 으로 거세요. 결과적으로 db-clientdb-password 는 읽고, other-secret 은 읽지 못하며, 시크릿 목록 조회(list)도 못 해야 합니다. 그리고 /root/ops/secrets/out/resourcenames-note.txtresourceNameslist/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_secretssecret-lab실제 Secret 개수와 일치해야 하고, encrypted_at_restfalse, plaintext_visible_in_etcdtrue 입니다. mitigations 배열에는 대응 방안을 3개 이상 적되 저장 시 암호화(EncryptionConfiguration 또는 "암호화")와 접근 권한 통제(RBAC 또는 "권한")가 반드시 포함돼야 합니다.

참고

실습용 Secret 두 개 만들기

네임스페이스 secret-lab 을 만들고 그 안에 Secret db-password 를 만드세요. 타입은 Opaque 이고 키 password 의 값은 labhub-Pr0d-2026 입니다. 뒤에서 권한을 비교할 대조군으로 Secret other-secret 도 하나 만드세요.

기본 타입이 무엇인지 확인하세요. 뒤에서 권한 범위를 비교할 대조군이 필요하므로 두 개를 만듭니다.

base64 를 직접 되돌려 보기

db-passworddata.password 값을 그대로 /root/ops/secrets/out/encoded.txt 에, 그것을 디코딩한 평문을 /root/ops/secrets/out/decoded.txt 에 저장하세요(디코딩 결과는 labhub-Pr0d-2026 이어야 하고 두 파일은 서로 대응해야 합니다). 그리고 /root/ops/secrets/out/base64-note.txt 에 base64 가 인코딩일 뿐 암호화가 아니라는 결론을 한 줄로 적으세요.

인코딩과 암호화의 차이를 손으로 확인하는 단계입니다. 되돌린 값과 인코딩한 값이 서로 대응하는지도 확인하세요.

저장 시 암호화 설정 쓰기

/root/ops/secrets/encryption-config.yaml 을 쓰세요. apiVersionapiserver.config.k8s.io/v1, kindEncryptionConfiguration 이고 resources[0].resourcessecrets 가 들어가야 합니다. providers첫 번째는 실제 암호화 프로바이더(aescbc, secretbox, aesgcm 중 하나)이며 keys[0].name 이 있어야 하고, 마지막identity 여야 합니다.

쓰기에 쓰이는 것은 목록의 첫 번째입니다. 기존 평문을 계속 읽으려면 마지막에 무엇이 남아 있어야 하는지 생각해 보세요. 키에는 이름이 필요합니다.

KMS v2 봉투 암호화 설정 쓰기

/root/ops/secrets/kms-config.yaml 을 쓰세요. kindEncryptionConfiguration 이고 providers[0]kms 여야 합니다. kms.apiVersionv2, kms.endpointunix:// 로 시작하는 소켓 경로, kms.name 도 있어야 합니다. 그리고 /root/ops/secrets/out/envelope-note.txt 에 봉투 암호화의 두 층 구조(데이터 키와 마스터 키)를 설명해 적으세요.

플러그인은 네트워크가 아니라 소켓으로 붙습니다. 두 층 구조가 무엇인지 메모로 남기세요.

외부 시크릿 저장소 연동 매니페스트 쓰기

/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 이 들어가면 안 됩니다.

문서 두 개가 필요합니다. 하나는 어디서 가져올지, 다른 하나는 무엇을 어떤 이름으로 만들지 정의합니다. 매니페스트에 실제 비밀번호가 들어가면 쓰는 이유가 사라집니다.

시크릿 하나만 여는 역할 만들기

secret-lab 에 ServiceAccount db-client 를 만들고, Role db-secret-reader 를 만드세요. 규칙은 secrets 리소스에 대해 resourceNamesdb-password 하나, verbsget 하나뿐이어야 합니다. 이 역할을 db-client 에게 RoleBinding 으로 거세요. 결과적으로 db-clientdb-password 는 읽고, other-secret 은 읽지 못하며, 시크릿 목록 조회(list)도 못 해야 합니다. 그리고 /root/ops/secrets/out/resourcenames-note.txtresourceNameslist/watch 에는 통하지 않는다는 점을 적으세요.

이름으로 범위를 좁히는 필드가 있습니다. 다만 그 필드가 통하지 않는 동사가 있다는 점을 함께 정리하세요.

불변 Secret 만들고 수정 거부 확인하기

secret-lab 에 Secret pinned-config 를 만드세요. immutable: true 이고 키 build 의 값은 2026-08-20 입니다. 그다음 이 값을 바꾸려고 시도하고 거부 메시지를 /root/ops/secrets/out/immutable-error.txt 에 저장하세요. 저장 후에도 build 값은 여전히 2026-08-20 이어야 합니다.

잠근 뒤 실제로 바꾸려고 시도해 보고 거부 메시지를 남기세요. 오류는 표준 오류로 나갑니다.

etcd 에서 평문을 꺼내 증거로 남기기

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_secretssecret-lab실제 Secret 개수와 일치해야 하고, encrypted_at_restfalse, plaintext_visible_in_etcdtrue 입니다. mitigations 배열에는 대응 방안을 3개 이상 적되 저장 시 암호화(EncryptionConfiguration 또는 "암호화")와 접근 권한 통제(RBAC 또는 "권한")가 반드시 포함돼야 합니다.

쿠버네티스 오브젝트의 키 경로 규칙을 떠올리세요. 값은 protobuf 라 깨져 보이지만 문자열은 그대로 읽힙니다.