이 Codelab 정보
1. 소개
컨테이너 분석은 컨테이너에 대한 취약점 스캔 및 메타데이터 스토리지를 제공합니다. 스캔 서비스는 Artifact Registry와 Container Registry의 이미지에서 취약점 스캔을 수행한 후 결과 메타데이터를 저장하고 API를 통해 사용할 수 있도록 제공합니다. 메타데이터 스토리지를 사용하면 취약점 스캔, Google Cloud 서비스, 서드 파티 제공업체 등 다양한 소스의 정보를 저장할 수 있습니다.
취약점 스캔은 자동으로 발생하거나 요청 시 발생할 수 있습니다.
- 자동 스캔을 사용 설정하면 Artifact Registry 또는 Container Registry에 새 이미지를 푸시할 때마다 스캔이 자동으로 트리거됩니다. 취약점 정보는 새로운 취약점이 발견되면 지속적으로 업데이트됩니다.
- 주문형 스캔이 사용 설정되면 Artifact Registry 또는 Container Registry에서 로컬 이미지 또는 이미지를 스캔하는 명령어를 실행해야 합니다. 주문형 스캔을 사용하면 컨테이너를 유연하게 스캔할 수 있습니다. 예를 들어 로컬에서 빌드된 이미지를 스캔하고 레지스트리에 저장하기 전에 취약점을 해결할 수 있습니다. 스캔 결과는 스캔이 완료된 후 최대 48시간 동안 제공되며, 스캔 후에도 취약점 정보는 업데이트되지 않습니다.
CI/CD 파이프라인에 컨테이너 분석을 통합하면 해당 메타데이터를 토대로 의사 결정을 내릴 수 있습니다. 예를 들어 Binary Authorization을 사용하여 신뢰할 수 있는 레지스트리에서 가져온 규정 준수 이미지에 대해서만 배포를 허용하는 배포 정책을 만들 수 있습니다.
학습할 내용
- 자동 스캔 사용 설정 방법
- 주문형 스캔 수행 방법
- 빌드 파이프라인에서 스캔을 통합하는 방법
- 승인된 이미지에 서명하는 방법
- GKE 허용 컨트롤러를 사용하여 이미지를 차단하는 방법
- 서명된 승인된 이미지만 허용하도록 GKE를 구성하는 방법
2. 설정 및 요구사항
자습형 환경 설정
- Google Cloud Console에 로그인하여 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.
- 프로젝트 이름은 이 프로젝트 참가자의 표시 이름입니다. 이는 Google API에서 사용하지 않는 문자열이며 언제든지 업데이트할 수 있습니다.
- 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유하며, 변경할 수 없습니다(설정된 후에는 변경할 수 없음). Cloud 콘솔이 고유한 문자열을 자동으로 생성합니다. 보통은 그게 뭔지 상관하지 않습니다. 대부분의 Codelab에서는 프로젝트 ID (일반적으로
PROJECT_ID
로 식별됨)를 참조해야 합니다. 생성된 ID가 마음에 들지 않으면 무작위로 다른 ID를 생성할 수 있습니다. 또는 직접 시도해 보고 사용 가능한지 확인할 수도 있습니다. 이 단계 이후에는 변경할 수 없으며 프로젝트 기간 동안 유지됩니다. - 참고로 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참고하세요.
- 다음으로 Cloud 리소스/API를 사용하려면 Cloud 콘솔에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼이 끝난 후에 요금이 청구되지 않도록 리소스를 종료하려면 만든 리소스를 삭제하거나 전체 프로젝트를 삭제하면 됩니다. Google Cloud 새 사용자에게는 미화 $300 상당의 무료 체험판 프로그램에 참여할 수 있는 자격이 부여됩니다.
Cloud Shell 편집기 시작
이 실습은 Google Cloud Shell 편집기에서 사용할 수 있도록 설계 및 테스트되었습니다. 편집기에 액세스하려면
- https://console.cloud.google.com에서 Google 프로젝트에 액세스합니다.
- 오른쪽 상단에서 Cloud Shell 편집기 아이콘을 클릭합니다.
- 창 하단에 새 창이 열립니다.
환경 설정
Cloud Shell에서 프로젝트의 ID와 프로젝트 번호를 설정합니다. PROJECT_ID
및 PROJECT_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
3. 자동 스캔
Artifact Registry 또는 Container Registry에 새 이미지를 푸시할 때마다 Artifact 스캔이 자동으로 트리거됩니다. 취약점 정보는 새로운 취약점이 발견되면 지속적으로 업데이트됩니다. 이 섹션에서는 Artifact Registry로 이미지를 푸시하고 결과를 살펴봅니다.
작업 디렉터리를 만들고 해당 디렉터리로 변경
mkdir vuln-scan && cd vuln-scan
샘플 이미지 정의
다음과 같은 내용으로 Dockerfile이라는 파일을 만듭니다.
cat > ./Dockerfile << EOF
FROM gcr.io/google-appengine/debian9@sha256:ebffcf0df9aa33f342c4e1d4c8428b784fc571cdf6fbab0b31330347ca8af97a
# System
RUN apt update && apt install python3-pip -y
# App
WORKDIR /app
COPY . ./
RUN pip3 install Flask==1.1.4
RUN pip3 install gunicorn==20.1.0
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 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에 자동으로 푸시하세요. 이미지의 bad
태그를 확인합니다. 이렇게 하면 이후 단계에서 식별할 수 있습니다.
gcloud builds submit . -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:bad
이미지 세부정보 검토
빌드 프로세스가 완료되면 Artifact Registry 대시보드에서 이미지와 취약점 결과를 검토하세요.
- Cloud 콘솔에서 Artifact Registry를 엽니다.
- artifact-scanning-repo를 클릭하여 콘텐츠를 확인합니다.
- 이미지 세부정보를 클릭합니다.
- 이미지의 최신 다이제스트를 클릭합니다.
- 스캔이 완료되면 이미지의 취약점 탭을 클릭합니다.
취약점 탭에서 방금 빌드한 이미지에 대한 자동 스캔 결과를 확인할 수 있습니다.
스캔 자동화는 기본적으로 사용 설정되어 있습니다. Artifact Registry 설정에서 자동 스캔을 사용/사용 중지하는 방법을 알아봅니다.
4. On-Demand Scanning
이미지를 저장소로 푸시하기 전에 스캔을 실행해야 할 수 있는 다양한 시나리오가 있습니다. 예를 들어 컨테이너 개발자는 코드를 소스 컨트롤로 푸시하기 전에 이미지를 스캔하여 문제를 해결할 수 있습니다. 아래 예에서는 결과에 따라 조치를 취하기 전에 로컬에서 이미지를 빌드하고 분석합니다.
이미지 빌드
이 단계에서는 로컬 Docker를 사용하여 로컬 캐시에 이미지를 빌드합니다.
docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image .
이미지 스캔
이미지가 빌드되면 이미지 스캔을 요청합니다. 스캔 결과는 메타데이터 서버에 저장됩니다. 작업은 메타데이터 서버의 결과 위치로 완료됩니다.
gcloud artifacts docker images scan \
us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
--format="value(response.scan)" > scan_id.txt
출력 파일 검토
잠시 시간을 내어 스캔_id.txt 파일에 저장된 이전 단계의 출력을 검토합니다. 메타데이터 서버에서 스캔 결과의 보고 위치를 확인합니다.
cat scan_id.txt
자세한 스캔 결과 검토
스캔의 실제 결과를 보려면 출력 파일에 표시된 보고서 위치에서 list-vulnerabilities
명령어를 사용합니다.
gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt)
출력에는 이미지의 모든 취약점에 대한 상당한 양의 데이터가 포함됩니다.
심각한 문제 신고
보고서에 저장된 데이터를 사람이 직접 사용하는 경우는 거의 없습니다. 일반적으로 결과는 자동화된 프로세스에 의해 사용됩니다. 아래 명령어를 사용하여 보고서 세부정보를 읽고 심각한 취약점이 발견되었는지 기록하세요.
export SEVERITY=CRITICAL
gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq ${SEVERITY}; then echo "Failed vulnerability check for ${SEVERITY} level"; else echo "No ${SEVERITY} Vulnerabilities found"; fi
이 명령어의 출력은
Failed vulnerability check for CRITICAL level
5. 빌드 파이프라인 스캔
이 섹션에서는 컨테이너 이미지를 빌드하고 스캔한 후 결과를 평가하는 자동화된 빌드 파이프라인을 만듭니다. 심각한 취약점이 발견되지 않으면 이미지가 저장소로 푸시됩니다. 심각한 취약점이 발견되면 빌드가 실패하고 종료됩니다.
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 파이프라인 만들기
다음 명령어는 자동화된 프로세스에 사용할 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']
images:
- us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image
EOF
CI 파이프라인 실행
처리를 위해 빌드를 제출하여 심각도 심각의 취약점이 발견되었을 때 빌드가 손상되었는지 확인합니다.
gcloud builds submit
빌드 실패 검토
이미지에 심각한 취약점이 포함되어 있으므로 방금 제출한 빌드가 실패합니다.
Cloud Build 기록 페이지에서 빌드 실패 검토
취약점 해결
심각한 취약점이 없는 기본 이미지를 사용하도록 Dockerfile을 업데이트합니다.
다음 명령어로 Debian 10 이미지를 사용하도록 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
올바른 이미지로 CI 프로세스 실행
처리를 위해 빌드를 제출하여 심각한 심각도의 취약점이 발견되지 않은 경우 빌드가 성공하는지 확인합니다.
gcloud builds submit
빌드 성공 검토
업데이트된 이미지에 심각한 취약점이 없으므로 방금 제출한 빌드가 성공적으로 실행됩니다.
Cloud Build 기록 페이지에서 빌드 성공 검토
스캔 결과 검토
Artifact Registry의 양호한 이미지 검토
- Cloud 콘솔에서 Artifact Registry를 엽니다.
- artifact-scanning-repo를 클릭하여 콘텐츠를 확인합니다.
- 이미지 세부정보를 클릭합니다.
- 이미지의 최신 다이제스트를 클릭합니다.
- 이미지의 취약점 탭을 클릭합니다.
6. 이미지 서명
증명자 메모 만들기
증명자 메모는 적용 중인 서명 유형에 대한 라벨 역할을 하는 작은 데이터입니다. 예를 들어 어떤 메모는 취약점 스캔을 나타낼 수 있고 다른 메모는 QA 승인에 사용할 수 있습니다. 이 메모는 서명 프로세스 중에 참조됩니다.
메모 만들기
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}"
증명자 만들기
증명자는 실제 이미지 서명 프로세스를 수행하는 데 사용되며 나중에 확인할 수 있도록 메모의 일치하는 항목을 이미지에 첨부합니다. 나중에 사용할 증명자를 만듭니다.
증명자 만들기
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-attestor
와 built-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 키 추가
증명자는 메모를 첨부하고 검증 가능한 서명을 제공하려면 암호화 키가 필요합니다. 이 단계에서는 Cloud Build에서 나중에 액세스할 수 있도록 키를 만들고 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
서명된 증명 만들기
이제 이미지에 서명할 수 있는 기능이 구성되었습니다. 이전에 만든 증명자를 사용하여 작업 중인 컨테이너 이미지에 서명합니다.
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}
7. Cloud Build로 서명
이미지 서명을 사용 설정하고 수동으로 증명자를 사용하여 샘플 이미지에 서명했습니다. 실무에서는 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에서 커스텀 빌드 단계를 사용합니다. 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 파이프라인에 증명 단계를 추가합니다.
- 추가할 새 단계를 검토합니다.
검토 전용. 복사 안함
#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'
- 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 기록 페이지를 열고 최신 빌드와 빌드 단계의 성공적인 실행을 검토합니다.
8. 허용 제어 정책
Binary Authorization은 GKE 및 Cloud Run의 기능으로, 컨테이너 이미지를 실행하기 전에 규칙을 검증할 수 있는 기능을 제공합니다. 신뢰할 수 있는 CI/CD 파이프라인의 이미지 실행 요청 또는 수동으로 이미지를 배포하려는 사용자의 이미지 실행 요청에 대해 검증이 실행됩니다. 이 기능을 사용하면 CI/CD 파이프라인 검사만 사용할 때보다 더 효과적으로 런타임 환경을 보호할 수 있습니다.
이 기능을 이해하기 위해 엄격한 승인 규칙을 적용하도록 기본 GKE 정책을 수정하겠습니다.
GKE 클러스터 만들기
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"
모든 정책 허용
먼저 기본 정책 상태와 이미지를 배포할 수 있는지 확인하세요.
- 기존 정책 검토
gcloud container binauthz policy export
- 시행 정책이
ALWAYS_ALLOW
로 설정되어 있습니다.
evaluationMode: ALWAYS_ALLOW
- 샘플을 배포하여 무엇이든 배포할 수 있는지 확인합니다.
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
- 배포가 작동하는지 확인
kubectl get pods
다음과 같은 출력이 표시됩니다.
- 배포 삭제
kubectl delete pod hello-server
모든 정책 거부
이제 모든 이미지를 허용하지 않도록 정책을 업데이트하세요.
- 현재 정책을 수정 가능한 파일로 내보내기
gcloud container binauthz policy export > policy.yaml
- 정책 변경
텍스트 편집기에서 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
- 터미널을 열고 새 정책을 적용한 후 변경사항이 적용될 때까지 몇 초 정도 기다립니다.
gcloud container binauthz policy import policy.yaml
- 샘플 워크로드 배포 시도
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
- 다음 메시지와 함께 배포가 실패합니다.
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
정책을 모두 허용으로 되돌리기
다음 섹션으로 넘어가기 전에 정책 변경사항을 되돌려야 합니다.
- 정책 변경
텍스트 편집기에서 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
- 되돌린 정책 적용
gcloud container binauthz policy import policy.yaml
9. GKE에서 취약점 차단
이 섹션에서는 이미지를 스캔한 다음 이미지에 서명하고 배포를 시도하는 Cloud Build와 CI/CD 파이프라인을 구현하여 지금까지 배운 내용을 결합합니다. GKE는 이미지 실행을 허용하기 전에 Binary Authorization을 사용하여 이미지에 취약점 스캔의 서명이 있는지 확인합니다.
증명이 필요하도록 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
정책 적용
gcloud beta container binauthz policy import binauth_policy.yaml
서명되지 않은 이미지 배포 시도
다음 명령어를 사용하여 앞에서 빌드한 애플리케이션의 배포 설명자를 생성합니다. 여기에 사용된 이미지는 이전에 빌드한 이미지로, 심각한 취약점이 있으며 서명된 증명을 포함하지 않습니다.
GKE 허용 컨트롤러는 서명을 일관되게 검증하기 위해 배포할 이미지를 정확히 알아야 합니다. 이렇게 하려면 간단한 태그가 아닌 이미지 다이제스트를 사용해야 합니다.
잘못된 이미지의 이미지 다이제스트 가져오기
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
서명된 이미지 배포
잘못된 이미지의 이미지 다이제스트 가져오기
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
콘솔에서 워크로드를 검토하고 이미지가 성공적으로 배포되었는지 확인합니다.
10. 축하합니다.
축하합니다. Codelab을 완료했습니다.
학습한 내용
- 자동 스캔 사용 설정 방법
- 주문형 스캔 수행 방법
- 빌드 파이프라인에서 스캔을 통합하는 방법
- 승인된 이미지에 서명하는 방법
- GKE 허용 컨트롤러를 사용하여 이미지를 차단하는 방법
- 서명된 승인된 이미지만 허용하도록 GKE를 구성하는 방법
다음 단계:
- Cloud Run과 Google Kubernetes Engine에 이미지를 안전하게 배포 | Cloud Build 문서
- 빠른 시작: GKE로 Binary Authorization 정책 구성 | Google Cloud
삭제
이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.
프로젝트 삭제
비용이 청구되지 않도록 하는 가장 쉬운 방법은 튜토리얼에서 만든 프로젝트를 삭제하는 것입니다.