LabHub
배우기 러닝패스 코스

CKS — Kubernetes Security Specialist

Pinning Images and Controlling Provenance

LabHub 에서 이어서 보기

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

목표

클러스터에 들어오는 이미지를 다이제스트로 고정하고, 출처를 허용 목록으로 좁히고, 프라이빗 레지스트리 인증을 ServiceAccount 수준에서 처리하는 표준 구성을 만듭니다.

왜 중요한가

태그는 변경 가능한 포인터입니다. 같은 v1.2 태그에 다른 이미지를 푸시할 수 있고, 그러면 어제 검증한 이미지와 오늘 도는 이미지가 다를 수 있습니다. 다이제스트는 내용의 해시이므로 이 문제를 원천적으로 없앱니다. 사고가 났을 때 "지금 도는 게 정확히 무엇인가"에 즉시 답할 수 있는지가 대응 시간을 가릅니다.

출처 통제는 그다음입니다. 허용 레지스트리 목록은 정책 엔진의 파라미터로 그대로 들어가고, imagePullSecrets 는 그 레지스트리에 붙을 자격증명입니다. 파드마다 적기보다 ServiceAccount 에 붙여 두면 새 워크로드가 자동으로 상속하므로 누락이 줄어듭니다.

이 환경에는 인터넷이 없어 실제로 이미지를 받아 오지도, Kyverno 같은 정책 엔진 CRD 를 설치하지도 못합니다. 그래서 정책 매니페스트는 파일로 작성해 채점하고, 나머지는 실제 오브젝트로 채점합니다.

단계

  1. 네임스페이스 cks-supply 를 만들고 그 안에 파드 pinned 를 만든다. 이미지는 태그 없이 다이제스트로 고정한다(registry.cks.local/shop/api@sha256: 뒤에 64 자리 16진수). imagePullPolicyIfNotPresent 다.
  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-supplykubernetes.io/dockerconfigjson 타입 Secret harbor-pull 을 만든다. 서버는 harbor.cks.local, 사용자는 robot$cks, 비밀번호는 cks-lab-token 으로 한다.
  4. cks-supply 에 ServiceAccount deployer 를 만들고 그 imagePullSecretsharbor-pull 을 넣는다.
  5. /root/cks-supply-chain/verify-images.yaml 에 서명 검증 정책 매니페스트를 쓴다. apiVersion: kyverno.io/v1, kind: ClusterPolicy, 이름은 verify-image-signature, spec.validationFailureActionEnforce, 규칙 하나(spec.rules[0].namecheck-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, serviceAccountNamedeployer, 컨테이너 이름은 app, 이미지는 registry.cks.local/ 로 시작하면서 @sha256: 로 고정된 것, imagePullPolicyIfNotPresent 다.

참고

태그 대신 다이제스트로 고정

네임스페이스 cks-supply 를 만들고 그 안에 파드 pinned 를 만든다. 이미지는 태그 없이 다이제스트로 고정한다(registry.cks.local/shop/api@sha256: 뒤에 64 자리 16진수). imagePullPolicyIfNotPresent 다.

이미지 참조는 <레지스트리>/<이름>@sha256:<64자리 16진수> 형태입니다. 태그와 다이제스트를 함께 쓰지 말고 다이제스트만 남기세요.

허용 레지스트리 목록

/root/cks-supply-chain/allowed-registries.txt 에 허용 레지스트리 prefix 를 한 줄에 하나씩, 아래 순서 그대로 4 줄 쓴다. registry.cks.local/, harbor.cks.local/library/, registry.k8s.io/, ghcr.io/cks-lab/

정책 엔진의 파라미터로 그대로 넘어갈 목록입니다. prefix 형태로 적고, 뒤에 슬래시를 붙여야 이름이 비슷한 다른 레지스트리가 통과하지 않습니다.

레지스트리 인증 Secret

cks-supplykubernetes.io/dockerconfigjson 타입 Secret harbor-pull 을 만든다. 서버는 harbor.cks.local, 사용자는 robot$cks, 비밀번호는 cks-lab-token 으로 한다.

kubectl create secret docker-registry 한 줄이면 됩니다. 타입 이름은 kubernetes.io/dockerconfigjson 이고 데이터 키는 .dockerconfigjson 입니다.

ServiceAccount 에 pull Secret 붙이기

cks-supply 에 ServiceAccount deployer 를 만들고 그 imagePullSecretsharbor-pull 을 넣는다.

ServiceAccount 의 imagePullSecrets 에 넣어 두면 그 SA 를 쓰는 모든 파드가 자동으로 인증합니다. 파드마다 적는 것보다 낫습니다.

서명·SBOM 검증 정책 매니페스트

/root/cks-supply-chain/verify-images.yaml 에 서명 검증 정책 매니페스트를 쓴다. apiVersion: kyverno.io/v1, kind: ClusterPolicy, 이름은 verify-image-signature, spec.validationFailureActionEnforce, 규칙 하나(spec.rules[0].namecheck-signature)가 verifyImages[0].imageReferences[0]registry.cks.local/* 를 대상으로 삼는다.

이 클러스터에는 정책 엔진 CRD 가 없으므로 파일로만 작성합니다. imageReferences 로 대상 레지스트리를 고르고 attestors 에 공개키를 넣는 구조입니다.

다이제스트 미고정 파드 탐지

cks-supply 에 파드 legacy-web(이미지 nginx:latest)을 만들어 미고정 워크로드를 재현한다. 그다음 /root/cks-supply-chain/find-unpinned.sh 에 탐지 스크립트를 쓰고 실행 결과를 /root/cks-supply-chain/unpinned.txt 에 저장한다. 결과는 cks-supply 네임스페이스에서 이미지가 @sha256: 로 고정되지 않은 컨테이너를 가진 파드를 cks-supply/<파드이름> 형식으로, 사전순 정렬해 한 줄에 하나씩 담는다.

이미지 문자열에 @sha256: 이 없으면 미고정입니다. jq 의 containstest 로 거를 수 있고, 초기화 컨테이너도 함께 봐야 합니다.

종합: 고정·인증·출처를 모두 갖춘 배포

cks-supply 에 Deployment checkout 을 만든다. replicas 2, 셀렉터와 파드 라벨은 app=checkout, serviceAccountNamedeployer, 컨테이너 이름은 app, 이미지는 registry.cks.local/ 로 시작하면서 @sha256: 로 고정된 것, imagePullPolicyIfNotPresent 다.

앞에서 만든 ServiceAccount 와 허용 레지스트리 목록의 첫 prefix 를 그대로 씁니다. 이미지는 다이제스트로 고정하고 imagePullPolicy 도 함께 지정하세요.