바이너리 인증으로 배포 제한

1. 소개

Binary Authorization은 배포 시점의 보안 제어를 제공하여 Google Kubernetes Engine(GKE) 또는 Cloud Run에 신뢰할 수 있는 컨테이너 이미지만 배포되도록 합니다. Binary Authorization을 사용하면 개발 프로세스 중에 신뢰할 수 있는 기관으로부터 이미지에 서명을 받도록 요구하고 이후 배포 시 서명 검증을 실시할 수 있습니다. 검증을 시행하면 확인된 이미지만 빌드 및 배포 프로세스에 통합되어 컨테이너 환경을 보다 확실하게 제어할 수 있습니다.

다음 다이어그램은 Binary Authorization/Cloud Build 설정에 사용되는 구성요소를 보여줍니다.

Cloud Build Binary Authorization 증명 파이프라인**그림 1.**Binary Authorization 증명을 만드는 Cloud Build 파이프라인

이 파이프라인에 대한 설명은 다음과 같습니다.

  1. 컨테이너 이미지를 빌드하는 코드가 Cloud Source Repositories와 같은 소스 저장소로 푸시됩니다.
  2. 지속적 통합(CI) 도구인 Cloud Build가 컨테이너를 빌드하고 테스트합니다.
  3. 이 빌드는 컨테이너 이미지를 Container Registry 또는 빌드된 이미지를 저장하는 다른 레지스트리로 푸시합니다.
  4. 암호화 키 쌍용 키 관리를 제공하는 Cloud Key Management Service가 컨테이너 이미지에 서명합니다. 그러면 결과 서명이 새로 생성된 증명에 저장됩니다.
  5. 배포 시 증명자가 키 쌍의 공개 키를 사용하여 증명을 확인합니다. Binary Authorization에서 서명된 증명에 컨테이너 이미지를 배포하도록 요구하여 정책을 적용합니다.

이 실습에서는 배포된 아티팩트를 보호하는 도구와 기법에 중점을 둡니다. 이 실습에서는 생성되었지만 특정 환경에 배포되지 않은 아티팩트(컨테이너)에 중점을 둡니다.

학습할 내용

  • 이미지 서명
  • 어드미션 컨트롤 정책
  • 스캔된 이미지 서명
  • 서명된 이미지 승인
  • 서명되지 않은 이미지 차단

2. 설정 및 요구사항

자습형 환경 설정

  1. Google Cloud Console에 로그인하여 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 프로젝트 이름은 이 프로젝트 참가자의 표시 이름입니다. 이는 Google API에서 사용하지 않는 문자열이며 언제든지 업데이트할 수 있습니다.
  • 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유하며, 변경할 수 없습니다(설정된 후에는 변경할 수 없음). Cloud 콘솔은 고유한 문자열을 자동으로 생성합니다. 일반적으로는 신경 쓰지 않아도 됩니다. 대부분의 Codelab에서는 프로젝트 ID (일반적으로 PROJECT_ID로 식별됨)를 참조해야 합니다. 생성된 ID가 마음에 들지 않으면 다른 임의 ID를 생성할 수 있습니다. 또는 직접 시도해 보고 사용 가능한지 확인할 수도 있습니다. 이 단계 이후에는 변경할 수 없으며 프로젝트 기간 동안 유지됩니다.
  • 참고로 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참고하세요.
  1. 다음으로 Cloud 리소스/API를 사용하려면 Cloud 콘솔에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼이 끝난 후에 요금이 청구되지 않도록 리소스를 종료하려면 만든 리소스 또는 전체 프로젝트를 삭제하면 됩니다. Google Cloud 새 사용자에게는 미화 $300 상당의 무료 체험판 프로그램에 참여할 수 있는 자격이 부여됩니다.

환경 설정

Cloud Shell에서 프로젝트 ID와 프로젝트의 프로젝트 번호를 설정합니다. 이러한 변수를 PROJECT_IDPROJECT_ID 변수로 저장합니다.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
    --format='value(projectNumber)')

서비스 사용 설정

필요한 모든 서비스를 사용 설정합니다.

gcloud services enable \
  cloudkms.googleapis.com \
  cloudbuild.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  artifactregistry.googleapis.com \
  containerscanning.googleapis.com \
  ondemandscanning.googleapis.com \
  binaryauthorization.googleapis.com 

Artifact Registry 저장소 만들기

이 실습에서는 Artifact Registry를 사용하여 이미지를 저장하고 스캔합니다. 다음 명령어를 사용하여 저장소를 만듭니다.

gcloud artifacts repositories create artifact-scanning-repo \
  --repository-format=docker \
  --location=us-central1 \
  --description="Docker repository"

Artifact Registry에 액세스할 때 gcloud 사용자 인증 정보를 활용하도록 Docker를 구성합니다.

gcloud auth configure-docker us-central1-docker.pkg.dev

작업 디렉터리를 만들고 해당 디렉터리로 변경합니다.

mkdir vuln-scan && cd vuln-scan

샘플 이미지 정의

다음 콘텐츠로 Dockerfile이라는 파일을 만듭니다.

cat > ./Dockerfile << EOF
from python:3.8-slim  

# App
WORKDIR /app
COPY . ./

RUN pip3 install Flask==2.1.0
RUN pip3 install gunicorn==20.1.0

CMD exec gunicorn --bind :\$PORT --workers 1 --threads 8 main:app

EOF

다음 콘텐츠로 main.py라는 파일을 만듭니다.

cat > ./main.py << EOF
import os
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    name = os.environ.get("NAME", "Worlds")
    return "Hello {}!".format(name)

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
EOF

AR에 이미지 빌드 및 푸시

Cloud Build를 사용하여 컨테이너를 빌드하고 Artifact Registry에 자동으로 푸시합니다.

gcloud builds submit . -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image

3. 이미지 서명

증명자란 무엇인가요?

증명자

  • 증명자는 시스템의 신뢰 체인에서 하나의 링크를 담당하는 사람 또는 프로세스로서
  • 암호화 키를 보유하고 승인 절차를 통과한 이미지에 서명합니다.
  • 정책 생성자는 추상적인 방식으로 상위 수준에서 정책을 결정하는 반면 증명자는 정책의 일부 측면을 구체적으로 시행합니다.
  • QA 테스터나 관리자와 같은 실제 사람일 수도 있고 CI 시스템의 봇일 수도 있습니다.
  • 시스템의 보안은 신뢰성에 따라 달라지므로 비공개 키를 안전하게 보관하는 것이 중요합니다.

각 역할은 조직의 개인 또는 팀을 나타낼 수 있습니다. 프로덕션 환경에서는 이러한 역할이 별도의 Google Cloud Platform (GCP) 프로젝트에서 관리되고 리소스에 대한 액세스 권한이 Cloud IAM을 사용하여 제한된 방식으로 공유될 것입니다.

a37eb2ed54b9c2eb.png

Binary Authorization의 증명자는 Cloud Container Analysis API를 기반으로 구현되므로 계속하기 전에 작동 방식을 설명하는 것이 중요합니다. Container Analysis API는 메타데이터를 특정 컨테이너 이미지와 연결할 수 있도록 설계되었습니다.

예를 들어 Heartbleed 취약점을 추적하기 위해 노트를 만들 수 있습니다. 그러면 보안 공급업체는 컨테이너 이미지에서 취약점을 테스트하는 스캐너를 만들고 손상된 각 컨테이너와 연결된 발생 항목을 만듭니다.

208aa5ebc53ff2b3.png

취약점 추적과 함께 컨테이너 분석은 일반 메타데이터 API로 설계되었습니다. Binary Authorization은 컨테이너 분석을 활용하여 확인 중인 컨테이너 이미지와 서명을 연결합니다**.** 컨테이너 분석 노트는 단일 증명자를 대표하는 데 사용되며, 발생 항목은 증명자가 승인한 각 컨테이너에 대해 생성되고 연결됩니다.

Binary Authorization API는 '증명자' 및 '증명' 개념을 사용하지만 이러한 개념은 Container Analysis API에서 해당 노트 및 발생 항목을 사용하여 구현됩니다.

63a701bd0057ea17.png

증명자 노트 만들기

증명자 노트는 적용되는 서명 유형의 라벨 역할을 하는 작은 데이터입니다. 예를 들어 한 노트는 취약점 검사를 나타내고 다른 노트는 QA 승인에 사용될 수 있습니다. 노트는 서명 프로세스 중에 참조됩니다.

919f997db0ffb881.png

메모 만들기

cat > ./vulnz_note.json << EOM
{
  "attestation": {
    "hint": {
      "human_readable_name": "Container Vulnerabilities attestation authority"
    }
  }
}
EOM

노트를 저장합니다.

NOTE_ID=vulnz_note

curl -vvv -X POST \
    -H "Content-Type: application/json"  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
    --data-binary @./vulnz_note.json  \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/?noteId=${NOTE_ID}"

노트를 검증합니다.

curl -vvv  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}"

이제 노트가 Container Analysis API 내에 저장됩니다.

증명자를 만듭니다.

증명자는 실제 이미지 서명 프로세스를 실행하는 데 사용되며 나중에 확인할 수 있도록 노트의 발생 항목을 이미지에 연결합니다. 증명자를 사용하려면 Binary Authorization에 노트도 등록해야 합니다.

ed05d438c79b654d.png

증명자를 만듭니다.

ATTESTOR_ID=vulnz-attestor

gcloud container binauthz attestors create $ATTESTOR_ID \
    --attestation-authority-note=$NOTE_ID \
    --attestation-authority-note-project=${PROJECT_ID}

증명자를 검증합니다.

gcloud container binauthz attestors list

마지막 줄은 이후 단계에서 키를 제공할 NUM_PUBLIC_KEYS: 0를 나타냅니다.

또한 이미지를 생성하는 빌드를 실행할 때 Cloud Build가 프로젝트에 built-by-cloud-build 증명자를 자동으로 만듭니다. 따라서 위의 명령어는 vulnz-attestorbuilt-by-cloud-build라는 두 개의 증명자를 반환합니다. 이미지가 성공적으로 빌드되면 Cloud Build가 자동으로 이미지에 대해 서명하고 증명을 만듭니다.

IAM 역할 추가

Binary Authorization 서비스 계정에는 증명 노트를 볼 권한이 필요합니다. 다음 API 호출을 사용하여 액세스 권한을 제공합니다.

PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}"  --format="value(projectNumber)")

BINAUTHZ_SA_EMAIL="service-${PROJECT_NUMBER}@gcp-sa-binaryauthorization.iam.gserviceaccount.com"


cat > ./iam_request.json << EOM
{
  'resource': 'projects/${PROJECT_ID}/notes/${NOTE_ID}',
  'policy': {
    'bindings': [
      {
        'role': 'roles/containeranalysis.notes.occurrences.viewer',
        'members': [
          'serviceAccount:${BINAUTHZ_SA_EMAIL}'
        ]
      }
    ]
  }
}
EOM

파일을 사용하여 IAM 정책 만들기

curl -X POST  \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    --data-binary @./iam_request.json \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}:setIamPolicy"

KMS 키 추가

1e3af7c177f7a311.png

이 증명자를 사용하려면 기관에서 컨테이너 이미지에 서명하는 데 사용할 수 있는 암호화 키 쌍을 만들어야 합니다. 이는 Google Cloud Key Management Service(KMS)를 통해 수행할 수 있습니다.

먼저 새 키를 설명하는 환경 변수를 추가합니다.

KEY_LOCATION=global
KEYRING=binauthz-keys
KEY_NAME=codelab-key
KEY_VERSION=1

키 집합을 보관할 키링 만들기

gcloud kms keyrings create "${KEYRING}" --location="${KEY_LOCATION}"

증명자를 위한 새 비대칭 서명 키 쌍을 만듭니다.

gcloud kms keys create "${KEY_NAME}" \
    --keyring="${KEYRING}" --location="${KEY_LOCATION}" \
    --purpose asymmetric-signing   \
    --default-algorithm="ec-sign-p256-sha256"

Google Cloud 콘솔의 KMS 페이지에 키가 표시됩니다.

이제 gcloud binauthz 명령어를 통해 키를 증명자와 연결합니다.

gcloud beta container binauthz attestors public-keys add  \
    --attestor="${ATTESTOR_ID}"  \
    --keyversion-project="${PROJECT_ID}"  \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

권한 목록을 다시 출력하면 이제 등록된 키가 표시됩니다.

gcloud container binauthz attestors list

서명된 증명 만들기

이제 이미지를 서명할 수 있는 기능이 구성되었습니다. 이전에 만든 증명자를 사용하여 작업 중인 컨테이너 이미지에 서명합니다.

858d7e6feeb6f159.png

증명에는 증명자가 특정 컨테이너 이미지를 확인했으며 클러스터에서 실행해도 안전하다고 명시하는 암호화 서명이 포함되어야 합니다. 증명할 컨테이너 이미지를 지정하려면 다이제스트를 확인해야 합니다.

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image

DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:latest \
    --format='get(image_summary.digest)')

이제 gcloud를 사용하여 증명을 만들 수 있습니다. 이 명령어는 서명에 사용할 키의 세부정보와 승인할 특정 컨테이너 이미지를 입력받습니다.

gcloud beta container binauthz attestations sign-and-create  \
    --artifact-url="${CONTAINER_PATH}@${DIGEST}" \
    --attestor="${ATTESTOR_ID}" \
    --attestor-project="${PROJECT_ID}" \
    --keyversion-project="${PROJECT_ID}" \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

컨테이너 분석 용어로 말하자면, 새 발생 항목이 생성되어 증명자의 노트에 연결됩니다. 모든 항목이 예상대로 작동하는지 확인하려면 증명을 나열하면 됩니다.

gcloud container binauthz attestations list \
   --attestor=$ATTESTOR_ID --attestor-project=${PROJECT_ID}

4. 어드미션 컨트롤 정책

Binary Authorization은 컨테이너 이미지가 실행되기 전에 규칙을 검증할 수 있는 기능을 제공하는 GKE 및 Cloud Run의 기능입니다. 유효성 검사는 신뢰할 수 있는 CI/CD 파이프라인에서든 사용자가 이미지를 수동으로 배포하려고 시도하든 이미지를 실행하려는 모든 요청에 대해 실행됩니다. 이 기능을 사용하면 CI/CD 파이프라인 검사만으로는 불가능한 런타임 환경을 더 효과적으로 보호할 수 있습니다.

이 기능을 이해하기 위해 엄격한 승인 규칙을 적용하도록 기본 GKE 정책을 수정하겠습니다.

GKE 클러스터 만들기

Binary Authorization이 사용 설정된 GKE 클러스터를 만듭니다.

gcloud beta container clusters create binauthz \
    --zone us-central1-a  \
    --binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE

Cloud Build가 이 클러스터에 배포하도록 허용합니다.

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/container.developer"

모두 허용 정책

먼저 기본 정책 상태와 이미지를 배포할 수 있는지 확인합니다.

  1. 기존 정책 검토
gcloud container binauthz policy export
  1. 시행 정책이 ALWAYS_ALLOW로 설정되어 있습니다.

evaluationMode: ALWAYS_ALLOW

  1. 샘플을 배포하여 모든 항목을 배포할 수 있는지 확인합니다.
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. 배포가 작동하는지 확인
kubectl get pods

다음과 같은 출력이 표시됩니다.

161db370d99ffb13.png

  1. 배포 삭제
kubectl delete pod hello-server

모두 거부 정책

이제 모든 이미지를 허용하지 않도록 정책을 업데이트합니다.

  1. 현재 정책을 수정 가능한 파일로 내보냅니다.
gcloud container binauthz policy export  > policy.yaml
  1. 정책 변경

텍스트 편집기에서 evaluationMode를 ALWAYS_ALLOW에서 ALWAYS_DENY로 변경합니다.

edit policy.yaml

정책 YAML 파일은 다음과 같이 표시됩니다.

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_DENY
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy

이 정책은 비교적 간단합니다. globalPolicyEvaluationMode 줄은 이 정책이 Google에서 정의한 전역 정책을 확장한다고 선언합니다. 이렇게 하면 모든 공식 GKE 컨테이너가 기본적으로 실행될 수 있습니다. 또한 이 정책은 다른 모든 포드가 거부됨을 명시하는 defaultAdmissionRule을 선언합니다. 어드미션 규칙에는 이 규칙을 준수하지 않는 모든 포드가 클러스터에서 실행되지 않도록 차단해야 한다고 명시하는 enforcementMode 줄이 포함되어 있습니다.

더 복잡한 정책을 빌드하는 방법은 Binary Authorization 문서를 참고하세요.

657752497e59378c.png

  1. 터미널을 열고 새 정책을 적용한 후 변경사항이 전파될 때까지 몇 초간 기다립니다.
gcloud container binauthz policy import policy.yaml
  1. 샘플 워크로드 배포 시도
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. 다음 메시지와 함께 배포가 실패합니다.
Error from server (VIOLATES_POLICY): admission webhook "imagepolicywebhook.image-policy.k8s.io" denied the request: Image gcr.io/google-samples/hello-app:1.0 denied by Binary Authorization default admission rule. Denied by always_deny admission rule

모두 허용하도록 정책 되돌리기

다음 섹션으로 넘어가기 전에 정책 변경사항을 되돌리세요.

  1. 정책 변경

텍스트 편집기에서 evaluationMode를 ALWAYS_DENY에서 ALWAYS_ALLOW로 변경합니다.

edit policy.yaml

정책 YAML 파일은 다음과 같이 표시됩니다.

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_ALLOW
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. 되돌린 정책 적용
gcloud container binauthz policy import policy.yaml

5. 스캔된 이미지 서명

이미지 서명을 사용 설정하고 증명자를 수동으로 사용하여 샘플 이미지에 서명했습니다. 실무에서는 CI/CD 파이프라인과 같은 자동화된 프로세스 중에 증명서를 적용하는 것이 좋습니다.

이 섹션에서는 이미지를 자동으로 증명하도록 Cloud Build를 구성합니다.

역할

Cloud Build 서비스 계정에 Binary Authorization 증명자 뷰어 역할을 추가합니다.

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/binaryauthorization.attestorsViewer

Cloud Build 서비스 계정(KMS 기반 서명)에 Cloud KMS CryptoKey 서명자/확인자 역할을 추가합니다.

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/cloudkms.signerVerifier

Cloud Build 서비스 계정에 컨테이너 분석 메모 첨부자 역할을 추가합니다.

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/containeranalysis.notes.attacher

Cloud Build 서비스 계정에 대한 액세스 권한 제공

Cloud Build에는 On-Demand Scanning API에 액세스할 권한이 필요합니다. 다음 명령어를 사용하여 액세스 권한을 제공합니다.

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/iam.serviceAccountUser"
        
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/ondemandscanning.admin"

커스텀 빌드 Cloud Build 단계 준비

Cloud Build의 커스텀 빌드 단계를 사용하여 증명 프로세스를 간소화합니다. Google은 프로세스를 간소화하기 위해 도우미 함수가 포함된 커스텀 빌드 단계를 제공합니다. 사용하려면 먼저 컨테이너에 커스텀 빌드 단계의 코드를 빌드하고 Cloud Build로 푸시해야 합니다. 이를 위해 다음 명령어를 실행하세요.

git clone https://github.com/GoogleCloudPlatform/cloud-builders-community.git
cd cloud-builders-community/binauthz-attestation
gcloud builds submit . --config cloudbuild.yaml
cd ../..
rm -rf cloud-builders-community

cloudbuild.yaml에 서명 단계 추가

이 단계에서는 Cloud Build 파이프라인에 증명 단계를 추가합니다.

  1. 아래 서명 단계를 검토합니다.

검토만 가능합니다. 복사 금지

#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'
  1. 아래의 전체 파이프라인을 사용하여 cloudbuild.yaml 파일을 작성합니다.
cat > ./cloudbuild.yaml << EOF
steps:

# build
- id: "build"
  name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', '.']
  waitFor: ['-']

#Run a vulnerability scan at _SECURITY level
- id: scan
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    (gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --location us \
    --format="value(response.scan)") > /workspace/scan_id.txt

#Analyze the result of the scan
- id: severity check
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
      gcloud artifacts docker images list-vulnerabilities \$(cat /workspace/scan_id.txt) \
      --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq CRITICAL; \
      then echo "Failed vulnerability check for CRITICAL level" && exit 1; else echo "No CRITICAL vulnerability found, congrats !" && exit 0; fi

#Retag
- id: "retag"
  name: 'gcr.io/cloud-builders/docker'
  args: ['tag',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#pushing to artifact registry
- id: "push"
  name: 'gcr.io/cloud-builders/docker'
  args: ['push',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'



images:
  - us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good
EOF

빌드 실행

gcloud builds submit

Cloud Build 기록에서 빌드 검토

Cloud Console에서 Cloud Build 기록 페이지를 열고 최신 빌드와 빌드 단계의 성공적인 실행을 검토합니다.

6. 서명된 이미지 승인

이 섹션에서는 이미지를 실행하기 전에 Binary Authorization을 사용하여 취약점 스캔 후 서명을 받은 이미지인지 검사하도록 GKE를 업데이트합니다.

d5c41bb89e22fd61.png

증명을 요구하도록 GKE 정책 업데이트

GKE BinAuth 정책에 clusterAdmissionRules를 추가하여 증명자가 이미지에 서명하도록 요구

현재 클러스터는 공식 저장소의 컨테이너는 허용하고 다른 모든 컨테이너는 거부하는 규칙이 하나인 정책을 실행하고 있습니다.

아래 명령어를 사용하여 업데이트된 구성으로 정책을 덮어씁니다.

COMPUTE_ZONE=us-central1-a

cat > binauth_policy.yaml << EOM
defaultAdmissionRule:
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
  evaluationMode: ALWAYS_DENY
globalPolicyEvaluationMode: ENABLE
clusterAdmissionRules:
  ${COMPUTE_ZONE}.binauthz:
    evaluationMode: REQUIRE_ATTESTATION
    enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
    requireAttestationsBy:
    - projects/${PROJECT_ID}/attestors/vulnz-attestor
EOM

이제 디스크에 updated_policy.yaml이라는 새 파일이 있어야 합니다. 이제 기본 규칙에서 모든 이미지를 거부하는 대신 먼저 증명자에게 확인을 요청합니다.

822240fc0b02408e.png

새 정책을 Binary Authorization에 업로드합니다.

gcloud beta container binauthz policy import binauth_policy.yaml

서명된 이미지 배포

바람직한 이미지의 이미지 다이제스트 가져오기

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:good \
    --format='get(image_summary.digest)')

Kubernetes 구성에서 다이제스트 사용

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

앱을 GKE에 배포합니다.

kubectl apply -f deploy.yaml

콘솔에서 워크로드를 검토하고 이미지가 성공적으로 배포되었는지 확인합니다.

7. 서명되지 않은 이미지 차단

이미지 빌드

이 단계에서는 로컬 Docker를 사용하여 이미지를 로컬 캐시에 빌드합니다.

docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:bad .

서명되지 않은 이미지를 저장소에 푸시

docker push us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:bad

악성 이미지의 이미지 다이제스트 가져오기

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:bad \
    --format='get(image_summary.digest)')

Kubernetes 구성에서 다이제스트 사용

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

GKE에 앱 배포 시도

kubectl apply -f deploy.yaml

콘솔에서 워크로드를 검토하고 배포가 거부되었다는 오류를 확인합니다.

No attestations found that were valid and signed by a key trusted by the attestor

8. 축하합니다.

축하합니다. Codelab을 완료했습니다.

학습한 내용

  • 이미지 서명
  • 어드미션 컨트롤 정책
  • 스캔된 이미지 서명
  • 서명된 이미지 승인
  • 서명되지 않은 이미지 차단

다음 단계:

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

프로젝트 삭제

비용이 청구되지 않도록 하는 가장 쉬운 방법은 튜토리얼에서 만든 프로젝트를 삭제하는 것입니다.

최종 업데이트: 2023년 3월 21일