LabHub
배우기 러닝패스 코스

CKS — Kubernetesセキュリティスペシャリスト

イメージの固定と出所の統制

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 도 함께 지정하세요.