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

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는 메타데이터를 특정 컨테이너 이미지와 연결할 수 있도록 설계되었습니다.

예를 들어 하트블리드 취약점을 추적하기 위해 메모를 만들 수 있습니다. 그런 다음 보안 공급업체는 스캐너를 생성하여 컨테이너 이미지에서 취약점을 테스트하고 손상된 각 컨테이너와 관련된 어커런스를 생성합니다.

208aa5ebc53ff2b3.png

컨테이너 분석은 취약점 추적과 함께 일반 메타데이터 API로 설계되었습니다. Binary Authorization은 컨테이너 분석을 활용하여 서명을 검증 중인 컨테이너 이미지와 연결합니다**.** 컨테이너 분석 메모는 단일 증명자를 나타내는 데 사용되며 어커런스는 생성되어 증명자가 승인한 각 컨테이너와 연결됩니다.

Binary Authorization API는 '증명자' 개념을 사용합니다. 및 '증명'을 지원하지만 Container Analysis API에서 해당하는 Notes 및 어커런스를 사용하여 구현됩니다.

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. 정책 변경

텍스트 편집기에서 AssessmentMode를 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. 정책 변경

텍스트 편집기에서 AssessmentMode를 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에 주문형 스캐닝 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 콘솔에서 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일