Secretの扱いと保存時の暗号化
한국어 원문으로 표시합니다.
목표
Secret 을 타입별로 만들고, 주입 방식에 따라 노출 경로가 어떻게 달라지는지 확인하고, RBAC 으로 읽기 범위를 좁힌 뒤 저장 시 암호화 설정 파일을 직접 작성합니다.
왜 중요한가
Secret 매니페스트의 값이 base64 로 되어 있어 암호화된 것처럼 보이지만, base64 는 인코딩이지
암호화가 아닙니다. 키가 없고 되돌리는 데 명령 하나면 됩니다. 기본 설정에서 Secret 은 etcd 에
평문으로 저장되므로 etcd 백업 파일을 얻은 사람은 모든 Secret 을 읽습니다.
EncryptionConfiguration 을 켜야 비로소 저장된 값이 암호화되고, 이때 identity 프로바이더는
반드시 목록의 마지막이어야 합니다. 앞에 오면 평문 저장으로 되돌아갑니다.
주입 방식도 중요합니다. 환경변수는 /proc/PID/environ 으로 읽히고 자식 프로세스에 상속되며,
크래시 리포트나 디버그 페이지에 통째로 실려 나갑니다. 볼륨 마운트가 더 안전하고,
defaultMode 로 파일 권한을 좁히는 것이 관례입니다.
마지막은 RBAC 입니다. 어떤 네임스페이스에 get secrets 권한이 있는 사람은 그 네임스페이스의
모든 Secret 을 읽습니다. resourceNames 로 특정 Secret 만 허용할 수 있다는 사실이 의외로
잘 알려져 있지 않습니다.
단계
- 네임스페이스
cks-secrets를 만들고 그 안에 Opaque Secretdb-cred를 만든다. 키는username(값app)과password(값pr0d-Db-Pass) 두 개다. cks-secrets에kubernetes.io/dockerconfigjson타입 Secretregistry-cred를 만든다. 서버는registry.cks.local, 사용자는ci, 비밀번호는ci-token이다.cks-secrets에kubernetes.io/tls타입 Secretshop-tls를 만든다.tls.crt와tls.key가 모두 비어 있지 않아야 한다.cks-secrets에 파드env-app(컨테이너 이름app, 이미지nginx:1.27-alpine)을 만든다. 컨테이너의envFrom[0].secretRef.name은db-cred다.cks-secrets에 파드vol-app(컨테이너 이름app)을 만든다. 볼륨 이름은db,secret.secretName은db-cred,secret.defaultMode는0400(10진수 256)이고, 컨테이너는 그 볼륨을/etc/db에readOnly: true로 마운트한다.cks-secrets에 Opaque Secretapp-config를 만든다. 키는mode(값strict) 하나이고immutable은true다.cks-secrets에 ServiceAccountapp을 만들고, Roledb-cred-reader를 만든다. apiGroups 는 코어 그룹, resources 는secrets,resourceNames는db-cred하나, verbs 는get하나다. RoleBindingapp-db-cred로 SAapp에 바인딩한다. 그다음 두 질문의 답(yes/no)을 순서대로/root/cks-secrets/can-i.txt에 두 줄로 저장한다. 첫 줄은appSA 가secret/db-cred를 get 할 수 있는지, 두 줄은secret/app-config를 get 할 수 있는지다./root/cks-secrets/encryption-config.yaml에 저장 시 암호화 설정을 쓴다.apiVersion: apiserver.config.k8s.io/v1,kind: EncryptionConfiguration,resources[0].resources는secrets하나,providers는 두 개로 첫 번째가aescbc(키 이름key1, secret 은 32 바이트를 base64 인코딩한 값), 마지막이identity다.
참고
kubectl create secret generic db-cred --from-literal=username=app --from-literal=password=pr0d-Db-Pass -n cks-secrets- 32 바이트 키 생성:
head -c 32 /dev/urandom | base64 kubectl auth can-i get secret/db-cred -n cks-secrets --as=system:serviceaccount:cks-secrets:app- 흔한 실수 1:
defaultMode: 0400을 따옴표로 감싸면 문자열이 되어 거부됩니다. 숫자로 쓰세요. - 흔한 실수 2: EncryptionConfiguration 에서
identity를 첫 번째에 두면 새로 쓰는 Secret 이 평문으로 저장됩니다. - 흔한 실수 3: 설정을 켜도 기존 Secret 은 다시 쓰기 전까지 평문입니다.
kubectl get secrets -A -o json | kubectl replace -f -로 전체를 갱신해야 합니다.
Opaque Secret 만들기
네임스페이스 cks-secrets 를 만들고 그 안에 Opaque Secret db-cred 를 만든다.
키는 username(값 app)과 password(값 pr0d-Db-Pass) 두 개다.
kubectl create secret generic --from-literal= 이 가장 빠릅니다. 타입을 지정하지 않으면 Opaque 입니다.
레지스트리 자격증명 Secret
cks-secrets 에 kubernetes.io/dockerconfigjson 타입 Secret registry-cred 를 만든다.
서버는 registry.cks.local, 사용자는 ci, 비밀번호는 ci-token 이다.
타입이 정해진 Secret 은 데이터 키 이름도 정해져 있습니다. dockerconfigjson 타입은 .dockerconfigjson 키 하나만 씁니다.
TLS Secret
cks-secrets 에 kubernetes.io/tls 타입 Secret shop-tls 를 만든다. tls.crt 와
tls.key 가 모두 비어 있지 않아야 한다.
tls.crt 와 tls.key 두 키가 모두 있어야 API 서버가 받아 줍니다.
환경변수로 주입하기
cks-secrets 에 파드 env-app(컨테이너 이름 app, 이미지 nginx:1.27-alpine)을 만든다.
컨테이너의 envFrom[0].secretRef.name 은 db-cred 다.
envFrom 의 secretRef 는 Secret 의 모든 키를 환경변수로 펼칩니다. 편하지만 프로세스 환경에 그대로 남는다는 점을 기억하세요.
볼륨으로 마운트하고 권한 좁히기
cks-secrets 에 파드 vol-app(컨테이너 이름 app)을 만든다. 볼륨 이름은 db,
secret.secretName 은 db-cred, secret.defaultMode 는 0400(10진수 256)이고,
컨테이너는 그 볼륨을 /etc/db 에 readOnly: true 로 마운트한다.
defaultMode 는 YAML 에서 8진수로 쓰면 10진수로 저장됩니다. 0400 은 소유자 읽기 전용입니다.
변경 불가 Secret
cks-secrets 에 Opaque Secret app-config 를 만든다. 키는 mode(값 strict) 하나이고
immutable 은 true 다.
immutable: true 를 켜면 데이터를 수정할 수 없고 지웠다 다시 만들어야 합니다. kubelet 의 watch 부하도 줄어듭니다.
특정 Secret 만 읽게 제한
cks-secrets 에 ServiceAccount app 을 만들고, Role db-cred-reader 를 만든다.
apiGroups 는 코어 그룹, resources 는 secrets, resourceNames 는 db-cred 하나,
verbs 는 get 하나다. RoleBinding app-db-cred 로 SA app 에 바인딩한다.
그다음 두 질문의 답(yes/no)을 순서대로
/root/cks-secrets/can-i.txt 에 두 줄로 저장한다. 첫 줄은 app SA 가
secret/db-cred 를 get 할 수 있는지, 두 줄은 secret/app-config 를 get 할 수 있는지다.
Role 의 resourceNames 로 이름을 못 박으면 그 Secret 만 읽을 수 있습니다. 결과는 auth can-i 에 TYPE/NAME 형식으로 물어 확인하세요.
저장 시 암호화 설정 파일
/root/cks-secrets/encryption-config.yaml 에 저장 시 암호화 설정을 쓴다.
apiVersion: apiserver.config.k8s.io/v1, kind: EncryptionConfiguration,
resources[0].resources 는 secrets 하나, providers 는 두 개로 첫 번째가 aescbc
(키 이름 key1, secret 은 32 바이트를 base64 인코딩한 값), 마지막이 identity 다.
프로바이더 목록은 순서가 의미를 갖습니다. 쓰기에는 첫 번째가 쓰이고, 읽기에는 순서대로 시도합니다. identity 의 위치를 잘못 두면 평문 저장으로 되돌아갑니다.