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 상당의 무료 체험판 프로그램에 참여할 수 있는 자격이 부여됩니다.
Cloudshell 편집기 시작
이 실습은 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 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
출력 파일 검토
scan_id.txt 파일에 저장된 이전 단계의 출력을 잠시 검토해 보세요. 메타데이터 서버에서 스캔 결과의 보고서 위치를 확인합니다.
cat scan_id.txt
자세한 스캔 결과 검토
스캔의 실제 결과를 보려면 출력 파일에 표시된 보고서 위치에서 list-vulnerabilities
명령어를 사용하세요.
gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt)
출력에는 이미지의 모든 취약점에 관한 상당한 양의 데이터가 포함됩니다.
심각한 문제 신고
인간은 보고서에 저장된 데이터를 직접 사용하는 경우가 거의 없습니다. 일반적으로 결과는 자동화된 프로세스에서 사용합니다. 아래 명령어를 사용하여 보고서 세부정보를 읽고 CRITICAL 취약점이 발견된 경우 로깅합니다.
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. 빌드 파이프라인 스캔
이 섹션에서는 컨테이너 이미지를 빌드하고 스캔한 후 결과를 평가하는 자동화된 빌드 파이프라인을 만듭니다. CRITICAL 취약점이 발견되지 않으면 이미지가 저장소에 푸시됩니다. CRITICAL 취약점이 발견되면 빌드가 실패하고 종료됩니다.
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 파이프라인 실행
처리를 위해 빌드를 제출하여 CRITICAL 심각도 취약점이 발견될 때 빌드가 중단되는지 확인합니다.
gcloud builds submit
빌드 실패 검토
이미지에 심각한 취약점이 포함되어 있으므로 방금 제출한 빌드가 실패합니다.
Cloud Build 기록 페이지에서 빌드 실패를 검토합니다.
취약점 수정
CRITICAL 취약점이 포함되지 않은 기본 이미지를 사용하도록 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 프로세스 실행
처리를 위해 빌드를 제출하여 CRITICAL 심각도 취약점이 발견되지 않으면 빌드가 성공하는지 확인합니다.
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 역할 추가
바이너리 승인 서비스 계정에는 증명 메모를 볼 권한이 필요합니다. 다음 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 Console에서 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
- 정책 변경
텍스트 편집기에서 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
- 터미널을 열고 새 정책을 적용한 후 변경사항이 적용될 때까지 몇 초 동안 기다립니다.
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
모두 허용하도록 정책 되돌리기
다음 섹션으로 넘어가기 전에 정책 변경사항을 되돌리세요.
- 정책 변경
텍스트 편집기에서 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
- 되돌린 정책 적용
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 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.
프로젝트 삭제
비용이 청구되지 않도록 하는 가장 쉬운 방법은 튜토리얼에서 만든 프로젝트를 삭제하는 것입니다.