CKS — 쿠버네티스 보안 전문가 · 공급망 보안 · 실습
이미지 고정과 출처 통제
목표
클러스터에 들어오는 이미지를 다이제스트로 고정하고, 출처를 허용 목록으로 좁히고,
프라이빗 레지스트리 인증을 ServiceAccount 수준에서 처리하는 표준 구성을 만듭니다.
왜 중요한가
태그는 변경 가능한 포인터입니다. 같은 v1.2 태그에 다른 이미지를 푸시할 수 있고, 그러면
어제 검증한 이미지와 오늘 도는 이미지가 다를 수 있습니다. 다이제스트는 내용의 해시이므로
이 문제를 원천적으로 없앱니다. 사고가 났을 때 "지금 도는 게 정확히 무엇인가"에 즉시 답할 수
있는지가 대응 시간을 가릅니다.
출처 통제는 그다음입니다. 허용 레지스트리 목록은 정책 엔진의 파라미터로 그대로 들어가고,imagePullSecrets 는 그 레지스트리에 붙을 자격증명입니다. 파드마다 적기보다 ServiceAccount 에
붙여 두면 새 워크로드가 자동으로 상속하므로 누락이 줄어듭니다.
이 환경에는 인터넷이 없어 실제로 이미지를 받아 오지도, Kyverno 같은 정책 엔진 CRD 를 설치하지도
못합니다. 그래서 정책 매니페스트는 파일로 작성해 채점하고, 나머지는 실제 오브젝트로 채점합니다.
단계
1. 네임스페이스 cks-supply 를 만들고 그 안에 파드 pinned 를 만든다. 이미지는 태그 없이
다이제스트로 고정한다(registry.cks.local/shop/api@sha256: 뒤에 64 자리 16진수).imagePullPolicy 는 IfNotPresent 다.
2. /root/cks-supply-chain/allowed-registries.txt 에 허용 레지스트리 prefix 를 한 줄에 하나씩,
아래 순서 그대로 4 줄 쓴다.registry.cks.local/, harbor.cks.local/library/, registry.k8s.io/, ghcr.io/cks-lab/
3. cks-supply 에 kubernetes.io/dockerconfigjson 타입 Secret harbor-pull 을 만든다.
서버는 harbor.cks.local, 사용자는 robot$cks, 비밀번호는 cks-lab-token 으로 한다.
4. cks-supply 에 ServiceAccount deployer 를 만들고 그 imagePullSecrets 에harbor-pull 을 넣는다.
5. /root/cks-supply-chain/verify-images.yaml 에 서명 검증 정책 매니페스트를 쓴다.apiVersion: kyverno.io/v1, kind: ClusterPolicy, 이름은 verify-image-signature,spec.validationFailureAction 은 Enforce, 규칙 하나(spec.rules[0].name 은check-signature)가 verifyImages[0].imageReferences[0] 로 registry.cks.local/* 를
대상으로 삼는다.
6. cks-supply 에 파드 legacy-web(이미지 nginx:latest)을 만들어 미고정 워크로드를
재현한다. 그다음 /root/cks-supply-chain/find-unpinned.sh 에 탐지 스크립트를 쓰고
실행 결과를 /root/cks-supply-chain/unpinned.txt 에 저장한다. 결과는 cks-supply
네임스페이스에서 이미지가 @sha256: 로 고정되지 않은 컨테이너를 가진 파드를cks-supply/<파드이름> 형식으로, 사전순 정렬해 한 줄에 하나씩 담는다.
7. cks-supply 에 Deployment checkout 을 만든다. replicas 2, 셀렉터와 파드 라벨은app=checkout, serviceAccountName 은 deployer, 컨테이너 이름은 app, 이미지는registry.cks.local/ 로 시작하면서 @sha256: 로 고정된 것, imagePullPolicy 는IfNotPresent 다.
참고
- 64 자리 다이제스트가 필요하면
head -c 32 /dev/urandom | sha256sum대신echo cks | sha256sum처럼 아무 문자열의 해시를 써도 됩니다. 형식만 맞으면 됩니다. kubectl create secret docker-registry harbor-pull --docker-server=... --docker-username=... --docker-password=... -n cks-supplykubectl patch serviceaccount deployer -n cks-supply -p '{"imagePullSecrets":[{"name":"harbor-pull"}]}'- 흔한 실수 1: 이미지에 태그와 다이제스트를 같이 씁니다(
nginx:1.27@sha256:...). 이 실습에서는 다이제스트만 남깁니다. - 흔한 실수 2: 허용 목록 prefix 뒤 슬래시를 빼면
registry.cks.local.evil.com/같은 이름이 통과합니다.
단계 7개
- 태그 대신 다이제스트로 고정
- 허용 레지스트리 목록
- 레지스트리 인증 Secret
- ServiceAccount 에 pull Secret 붙이기
- 서명·SBOM 검증 정책 매니페스트
- 다이제스트 미고정 파드 탐지
- 종합: 고정·인증·출처를 모두 갖춘 배포