빌드 출처 생성 및 검증

이 페이지에서는 빌드 출처 기록을 생성하고, 출력을 확인하고, 검증하는 방법에 대한 안내를 제공합니다.

빌드 출처 기록은 빌드에 대한 검증 가능한 데이터 모음입니다. 출처 메타데이터에는 빌드된 이미지의 다이제스트, 입력 소스 위치, 빌드 인수, 빌드 기간과 같은 세부정보가 포함됩니다. 이 정보를 통해 현재 사용 중인 빌드 아티팩트가 신뢰할 수 있는 소스 및 빌더에 의해 생성된 정확하고 안정적인 것인지 확인할 수 있습니다.

Cloud Build는 SLSA 버전 0.11.0의 사양을 기반으로 소프트웨어 아티팩트에 대한 공급망 등급(SLSA) 레벨 3 보장을 충족하는 빌드 출처 기록 생성을 지원합니다.

SLSA v1.0 사양에 대한 지원으로 Cloud Build는 빌드 출처 기록에 buildType 세부정보를 제공합니다. buildType 스키마를 사용하여 Cloud Build가 기록하는 값과 이러한 값의 소스를 비롯하여 빌드 프로세스에 사용되는 매개변수화된 템플릿을 파악할 수 있습니다. 자세한 내용은 Cloud Build buildType v1을 참고하세요.

제한사항

  • Cloud Build는 Artifact Registry에 저장된 아티팩트에 대한 빌드 출처 기록만 생성합니다.
  • SLSA v1.0 및 v0.1 출처를 모두 가져오려면 트리거를 사용하여 빌드해야 합니다. 수동으로 빌드를 시작하는 경우 gcloud CLI를 사용하여 Cloud Build는 SLSA v0.1 출처만 제공합니다.

시작하기 전에

  1. Enable the Cloud Build, Container Analysis, and Artifact Registry APIs.

    Enable the APIs

  2. 이 가이드에서 명령줄 예시를 사용하려면 Google Cloud SDK를 설치하고 구성하세요.

  3. 소스 코드를 준비합니다.

  4. Artifact Registry에 저장소가 있습니다.

빌드 출처 기록 생성

다음 안내에서는 Artifact Registry에 저장하는 컨테이너 이미지에 대한 빌드 출처 기록을 생성하는 방법을 설명합니다.

  1. 빌드 구성 파일에 images 필드를 추가하여 빌드가 완료된 후 빌드된 이미지를 Artifact Registry에 저장하도록 Cloud Build를 구성합니다.

    명시적인 docker push 단계를 사용하여 Artifact Registry에 이미지를 푸시하는 경우 Cloud Build에서 출처를 생성할 수 없습니다.

    다음 스니펫은 컨테이너 이미지를 빌드하고 Artifact Registry의 Docker 저장소에 이미지를 저장하기 위한 빌드 구성을 보여줍니다.

    YAML

      steps:
      - name: 'gcr.io/cloud-builders/docker'
        args: [ 'build', '-t', 'LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE', '.' ]
      images: ['LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE']
    

    각 항목의 의미는 다음과 같습니다.

    • LOCATION: 저장소의 리전 또는 멀티 리전 위치입니다.
    • PROJECT_ID: Google Cloud 프로젝트 ID입니다.
    • REPOSITORY: Artifact Registry 저장소의 이름입니다.
    • IMAGE: 컨테이너 이미지의 이름입니다.

    JSON

      {
      "steps": [
          {
              "name": "gcr.io/cloud-builders/docker",
              "args": [
                  "build",
                  "-t",
                  "LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE",
                  "."
              ]
          }
      ],
      "images": [
          "LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE"
      ]
      }
    

    각 항목의 의미는 다음과 같습니다.

    • LOCATION: 저장소의 리전 또는 멀티 리전 위치입니다.
    • PROJECT_ID: Google Cloud 프로젝트 ID입니다.
    • REPOSITORY: Artifact Registry 저장소의 이름입니다.
    • IMAGE: 컨테이너 이미지의 이름입니다.
  2. 빌드 구성의 options 섹션에서 requestedVerifyOption 옵션을 추가하고 값을 VERIFIED로 설정합니다.

    이 설정을 사용하면 출처 생성을 사용 설정하고 출처 메타데이터가 있는지 확인하도록 Cloud Build를 구성할 수 있습니다. 출처가 생성된 경우에만 빌드가 성공으로 표시됩니다.

    YAML

    options:
      requestedVerifyOption: VERIFIED
    

    JSON

    {
        "options": {
            "requestedVerifyOption": "VERIFIED"
        }
    }
    
  3. 빌드를 시작합니다.

빌드 출처 기록 보기

이 섹션에서는 Cloud Build에서 만든 빌드 출처 기록 메타데이터를 보는 방법을 설명합니다. 감사 목적으로 이 정보를 가져올 수 있습니다.

Google Cloud 콘솔의 보안 통계 측면 패널을 사용하거나 gcloud CLI를 사용하여 컨테이너의 빌드 출처 기록 메타데이터에 액세스할 수 있습니다.

Console

보안 통계 측면 패널에서는 Artifact Registry에 저장된 아티팩트의 보안 정보에 대한 대략적인 개요를 제공합니다.

보안 통계 패널을 보려면 다음 안내를 따르세요.

  1. Google Cloud 콘솔에서 빌드 기록 페이지를 엽니다.

    빌드 기록 페이지 열기

  2. 프로젝트를 선택하고 열기를 클릭합니다.

  3. 리전 드롭다운 메뉴에서 빌드를 실행한 리전을 선택합니다.

  4. 빌드가 포함된 테이블에서 보안 통계를 보려는 빌드가 있는 행을 찾습니다.

  5. 보안 통계 열에서 보기를 클릭합니다.

    그러면 선택한 아티팩트의 보안 통계 패널이 표시됩니다.

    빌드 카드에는 출처 세부정보와 링크가 표시됩니다. 링크 아이콘을 클릭하면 출처 스니펫을 볼 수 있습니다.

측면 패널 및 Cloud Build를 사용하여 소프트웨어 공급망을 보호하는 방법에 관한 자세한 내용은 빌드 보안 통계 보기를 참고하세요.

gcloud CLI

컨테이너 이미지의 출처 메타데이터를 보려면 다음 명령어를 실행합니다.

  gcloud artifacts docker images describe \
  LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH \
  --show-provenance --format=FORMAT

다음을 바꿉니다.

  • LOCATION: 저장소의 리전 또는 멀티 리전 위치입니다.
  • PROJECT_ID: Google Cloud 프로젝트 ID입니다.
  • REPOSITORY: Artifact Registry 저장소의 이름입니다.
  • IMAGE: 컨테이너 이미지의 이름입니다.
  • HASH: 이미지의 sha256 해시 값입니다. 빌드 출력에서 확인할 수 있습니다.
  • FORMAT: 출력 형식을 지정할 수 있는 선택적 설정입니다.

출력 예

빌드 출처 기록은 다음과 유사합니다.

      image_summary:
      digest: sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
      fully_qualified_digest: us-central1-docker.pkg.dev/my-project/my-repo/my-image@sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
      registry: us-central1-docker.pkg.dev
      repository: my-repo
      slsa_build_level: 0
    provenance_summary:
      provenance:
      - build:
          inTotoSlsaProvenanceV1:
            _type: https://in-toto.io/Statement/v1
            predicate:
              buildDefinition:
                buildType: https://cloud.google.com/build/gcb-buildtypes/google-worker/v1
                externalParameters:
                  buildConfigSource:
                    path: cloudbuild.yaml
                    ref: refs/heads/main
                    repository: git+https://github.com/my-username/my-git-repo
                  substitutions: {}
                internalParameters:
                  systemSubstitutions:
                    BRANCH_NAME: main
                    BUILD_ID: e73ca1d4-ec4a-4ea6-acdd-ac8bb16dcc79
                    COMMIT_SHA: 525c52c501739e6df0609ed1f944c1bfd83224e7
                    LOCATION: us-west1
                    PROJECT_NUMBER: '265426041527'
                    REF_NAME: main
                    REPO_FULL_NAME: my-username/my-git-repo
                    REPO_NAME: my-git-repo
                    REVISION_ID: 525c52c501739e6df0609ed1f944c1bfd83224e7
                    SHORT_SHA: 525c52c
                    TRIGGER_BUILD_CONFIG_PATH: cloudbuild.yaml
                    TRIGGER_NAME: github-trigger-staging
                  triggerUri: projects/265426041527/locations/us-west1/triggers/a0d239a4-635e-4bd3-982b-d8b72d0b4bab
                resolvedDependencies:
                - digest:
                    gitCommit: 525c52c501739e6df0609ed1f944c1bfd83224e7
                  uri: git+https://github.com/my-username/my-git-repo@refs/heads/main
                - digest:
                    sha256: 154fcd4d2d65c6a35b06b98053a0829c581e223d530be5719326f5d85d680e8d
                  uri: gcr.io/cloud-builders/docker@sha256:154fcd4d2d65c6a35b06b98053a0829c581e223d530be5719326f5d85d680e8d
              runDetails:
                builder:
                  id: https://cloudbuild.googleapis.com/GoogleHostedWorker
                byproducts:
                - {}
                metadata:
                  finishedOn: '2023-08-01T19:57:10.734471Z'
                  invocationId: https://cloudbuild.googleapis.com/v1/projects/my-project/locations/us-west1/builds/e73ca1d4-ec4a-4ea6-acdd-ac8bb16dcc79
                  startedOn: '2023-08-01T19:56:57.451553160Z'
            predicateType: https://slsa.dev/provenance/v1
            subject:
            - digest:
                sha256: 7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
              name: https://us-central1-docker.pkg.dev/my-project/my-repo/my-image
            - digest:
                sha256: 7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
              name: https://us-central1-docker.pkg.dev/my-project/my-repo/my-image:latest
        createTime: '2023-08-01T19:57:14.810489Z'
        envelope:
          payload:
          eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdMWQ0LWVjNGEtNGVhNi1hY2RkLWFjOGJiMTZkY2M3OSIsICJzdGFydGVkT24iOiIyMDIzLTA4LTAxVDE5OjU2OjU3LjQ1MTU1MzE2MFoiLCAiZmluaXNoZWRPbiI6IjIwMjMtMDgtMDFUMTk6NTc6MTAuNzM0NDcxWiJ9LCAiYnlwcm9kdWN0cyI6W3t9XX19fQ==...
          payloadType: application/vnd.in-toto+json
          signatures:
          - keyid: projects/verified-builder/locations/global/keyRings/attestor/cryptoKeys/google-hosted-worker/cryptoKeyVersions/1
            sig: MEUCIQCss8UlQL2feFePRJuKTE8VA73f85iqj4OJ9SvVPqTNwAIgYyuyuIrl1PxQC5B109thO24Y6NA4bTa0PJY34EHRSVE=
        kind: BUILD
        name: projects/my-project/occurrences/71787589-c6a6-4d6a-a030-9fd041e40468
        noteName: projects/argo-qa/notes/intoto_slsa_v1_e73ca1d4-ec4a-4ea6-acdd-ac8bb16dcc79
        resourceUri: https://us-central1-docker.pkg.dev/my-project/my-repo/my-image@sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
        updateTime: '2023-08-01T19:57:14.810489Z'
    

이 예시에서 알아야 할 중요한 사항은 다음과 같습니다.

  • 소스: GitHub 저장소에서 빌드가 트리거되었습니다.

  • 객체 참조: 이름이 digestfileHash인 필드는 동일한 객체를 참조합니다. 예시 출력에 포함된 digest 필드는 Base16(16진수로 인코딩)으로 인코딩됩니다. SLSA 버전 0.1 출처를 사용하는 경우 출력은 base64로 인코딩된 fileHash 필드를 사용합니다.

  • 서명: SLSA 버전 0.1 출처를 사용하는 경우 출력에는 envelope 필드에 두 개의 서명이 포함됩니다. 키 이름이 provenanceSigner인 첫 번째 서명에서는 DSSE 적합 서명(사전 인증 인코딩(PAE)으로 형식 지정)을 사용하며 Binary Authorization 정책에서 이를 확인할 수 있습니다. 이 출처의 새로운 사용에 이 서명을 사용하는 것이 좋습니다. 키 이름이 builtByGCB인 두 번째 서명은 기존 사용에 제공됩니다.

  • 서비스 계정: Cloud Build 출처에 자동으로 포함된 서명은 빌드를 실행한 빌드 서비스를 확인하는 데 도움이 됩니다. 또한 빌드를 시작하는 데 사용되는 서비스 계정에 대한 검증 가능한 메타데이터를 기록하도록 Cloud Build를 구성할 수 있습니다. 자세한 내용은 cosign으로 컨테이너 이미지 서명을 참조하세요.

  • 페이로드: 이 페이지에 표시된 출처 예시는 가독성을 위해 축약되었습니다. 페이로드는 모든 출처 메타데이터의 base-64 인코딩 버전이므로 실제 출력은 더 길어집니다.

비컨테이너 아티팩트의 출처 보기

Cloud Build는 빌드 아티팩트를 Artifact Registry에 업로드할 때 독립형 Java(Maven), Python, Node.js(npm) 애플리케이션에 대한 SLSA 출처 메타데이터를 생성합니다. 직접 API를 호출하여 출처 메타데이터를 검색할 수 있습니다.

  1. 아티팩트의 출처 메타데이터를 생성하려면 Cloud Build로 빌드를 실행합니다. 다음 가이드 중 하나를 사용하세요.

    빌드가 완료되면 BuildID를 기록합니다.

  2. 터미널에서 다음 API 호출을 실행하여 출처 메타데이터를 검색합니다. 여기서 PROJECT_ID는 Google Cloud 프로젝트와 연결된 ID입니다.

    alias gcurl='curl -H"Authorization: Bearer $(gcloud auth print-access-token)"'
        gcurl 'https://containeranalysis.googleapis.com/v1/projects/PROJECT_ID/occurrences'
    

    이 유형의 아티팩트에 대한 출처 메타데이터에 액세스하려면 API 호출을 사용해야 합니다. 컨테이너 외 아티팩트에 대한 출처 메타데이터는 Google Cloud 콘솔에 표시되지 않으며 gcloud CLI를 통해 액세스할 수 없습니다.

  3. 프로젝트와 일치하는 항목에서 BuildID로 검색하여 빌드 아티팩트와 연관된 출처 정보를 찾습니다.

출처 검증

이 섹션에서는 컨테이너 이미지의 빌드 출처 기록을 확인하는 방법을 설명합니다.

빌드 출처 기록을 검증하면 다음을 수행하는 데 도움이 됩니다.

  • 빌드 아티팩트가 신뢰할 수 있는 소스 및 빌더에서 생성되는지 확인
  • 빌드 프로세스를 설명하는 출처 메타데이터가 완전하고 진본인지 확인

자세한 내용은 빌드 보호를 참조하세요.

SLSA 인증자를 사용한 출처 검증

SLSA 인증자는 SLSA 사양을 기준으로 빌드 무결성을 검증하는 오픈소스 CLI 도구입니다.

확인자는 문제를 발견하면 빌드 프로세스를 업데이트하고 위험을 완화하는 데 도움이 되는 자세한 오류 메시지를 반환합니다.

SLSA 인증자를 사용하려면 다음 안내를 따르세요.

  1. slsa-verifier 저장소에서 버전 2.1 이상을 설치합니다.

    go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@VERSION
    
  2. CLI에서 이미지 식별자의 변수를 설정합니다.

    export IMAGE=LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH
    

    명령어의 자리 표시자 값을 다음으로 바꿉니다.

    • LOCATION: 리전 또는 멀티 리전 위치
    • PROJECT_ID: Google Cloud 프로젝트 ID입니다.
    • REPOSITORY: 저장소 이름입니다.
    • IMAGE: 이미지 이름입니다.
    • HASH: 이미지의 sha256 해시 값입니다. 빌드 출력에서 확인할 수 있습니다.
  3. SLSA 인증자가 출처 데이터에 액세스할 수 있도록 gcloud CLI를 승인합니다.

    gcloud auth configure-docker LOCATION-docker.pkg.dev
    
  4. 이미지의 출처를 검색하고 JSON으로 저장합니다.

    gcloud artifacts docker images describe $IMAGE --format json --show-provenance > provenance.json
    
  5. 출처를 확인합니다.

    slsa-verifier verify-image "$IMAGE" \
    --provenance-path provenance.json \
    --source-uri SOURCE \
    --builder-id=BUILDER_ID
    

    각 항목의 의미는 다음과 같습니다.

    • SOURCE는 이미지의 소스 저장소 URI입니다(예: github.com/my-repo/my-application).
    • BUILDER_ID 빌더의 고유 ID입니다(예: https://cloudbuild.googleapis.com/GoogleHostedWorker).

    정책 엔진에 사용할 수 있도록 검증된 출처를 출력하려면 --print-provenance 플래그와 함께 이전 명령어를 사용합니다.

    출력은 PASSED: Verified SLSA provenance 또는 FAILED: SLSA verification failed: <error details>와 비슷합니다.

선택적 플래그에 관한 자세한 내용은 옵션을 참고하세요.

gcloud CLI로 출처 메타데이터 검증

빌드 출처 기록이 조작되지 않았는지 확인하려면 다음 단계를 수행하여 출처 유효성을 검사할 수 있습니다.

  1. 새 디렉터리를 만든 후 해당 디렉터리로 이동합니다.

    mkdir provenance && cd provenance
    
  2. keyid 필드의 정보를 사용하여 공개 키를 가져옵니다.

    gcloud kms keys versions get-public-key 1 --location global --keyring attestor \
      --key builtByGCB --project verified-builder --output-file my-key.pub
    
  3. payload에는 base64url로 인코딩된 출처의 JSON 표현이 포함됩니다. 데이터를 디코딩하여 파일에 저장합니다.

    gcloud artifacts docker images describe \
    LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH --show-provenance \
      --format=json | jq -r '.provenance_summary.provenance[] | select(.build.intotoStatement.predicateType == "https://slsa.dev/provenance/v0.1") | .envelope.payload' | tr '\-_' '+/' | base64 -d > provenance.json
    

    가능한 경우 SLSA 버전 0.1 및 1.0 출처 유형은 모두 저장됩니다. 버전 1.0으로 필터링하려면 predicateType을 변경하여 https://slsa.dev/provenance/v1을 사용합니다. 예를 들면 다음과 같습니다.

    gcloud artifacts docker images describe \
    LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH --show-provenance \
      --format=json | jq -r '.provenance_summary.provenance[] | select(.build.intotoStatement.predicateType == "https://slsa.dev/provenance/v1") | .envelope.payload' | tr '\-_' '+/' | base64 -d > provenance.json
    
  4. 봉투에는 출처에 대한 서명도 포함됩니다. 데이터를 디코딩하여 파일에 저장합니다.

      gcloud artifacts docker images describe LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH --show-provenance \
      --format=json | '.provenance_summary.provenance[] | select(.build.intotoStatement.predicateType == "https://slsa.dev/provenance/v0.1") | .envelope.signatures[0].sig' | tr '\-_' '+/' | base64 -d > signature.bin
    

    버전 1.0으로 필터링하려면 predicateType을 변경하여 https://slsa.dev/provenance/v1을 사용합니다. 예를 들면 다음과 같습니다.

    gcloud artifacts docker images describe LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH --show-provenance \
    --format=json | jq -r '.provenance_summary.provenance[] | select(.build.intotoStatement.predicateType == "https://slsa.dev/provenance/v1") | .envelope.signatures[0].sig' | tr '\-_' '+/' | base64 -d > signature.bin
    
  5. 위 명령어는 provenanceSigner 키로 서명된 첫 번째 출처 서명(.provenance_summary.provenance[0].envelope.signatures[0])을 참조합니다. 페이로드는 PAE 형식의 봉투를 통해 서명됩니다. 이를 확인하려면 다음 명령어를 실행하여 출처를 "DSSEv1" + SP + LEN(type) + SP + type + SP + LEN(body) + SP + body의 예상 PAE 형식으로 변환합니다.

    echo -n "DSSEv1 28 application/vnd.in-toto+json $(cat provenance.json | wc -c) $(cat provenance.json)" > provenance.json
    
  6. 서명 유효성을 검사합니다.

    openssl dgst -sha256 -verify my-key.pub -signature signature.bin provenance.json
    

    유효성 검사에 성공하면 출력은 Verified OK입니다.

다음 단계