Bezpieczna kompilacja Wdrażanie za pomocą Cloud Build, Artifact Registry i GKE

1. Wprowadzenie

Container Analysis umożliwia skanowanie pod kątem luk w zabezpieczeniach i przechowywanie metadanych kontenerów. Usługa skanowania skanuje obrazy w Artifact Registry i Container Registry, a następnie przechowuje uzyskane metadane i udostępnia je do wykorzystania za pomocą interfejsu API. Magazyn metadanych umożliwia przechowywanie informacji z różnych źródeł, takich jak skanowanie pod kątem luk w zabezpieczeniach, usługi Google Cloud i od dostawców zewnętrznych.

Skanowanie pod kątem luk w zabezpieczeniach może odbywać się automatycznie lub na żądanie:

  • Gdy automatyczne skanowanie jest włączone, skanowanie uruchamia się automatycznie za każdym razem, gdy przesyłasz nowy obraz do Artifact Registry lub Container Registry. Informacje o lukach w zabezpieczeniach są stale aktualizowane w przypadku wykrywania nowych luk.
  • Gdy skanowanie na żądanie jest włączone, musisz uruchomić polecenie skanowania lokalnego obrazu lub obrazu w Artifact Registry lub Container Registry. Skanowanie na żądanie zapewnia elastyczność podczas skanowania kontenerów. Możesz na przykład przeskanować lokalnie utworzony obraz i usunąć luki w zabezpieczeniach przed zapisaniem go w rejestrze. Wyniki skanowania są dostępne przez maksymalnie 48 godzin po zakończeniu skanowania, a informacje o lukach w zabezpieczeniach nie są po nim aktualizowane.

Dzięki integracji narzędzia Container Analysis z potokiem CI/CD możesz podejmować decyzje na podstawie tych metadanych. Możesz na przykład użyć usługi Binary Authorization, aby utworzyć zasady wdrażania zezwalające na wdrożenia tylko w przypadku zgodnych obrazów z zaufanych rejestrów.

Czego się nauczysz

  • Jak włączyć automatyczne skanowanie
  • Jak wykonywać skanowanie na żądanie
  • Jak zintegrować skanowanie z potokiem kompilacji
  • Jak podpisywać zatwierdzone obrazy
  • Jak używać kontrolerów akceptacji GKE do blokowania obrazów
  • Jak skonfigurować GKE, aby zezwalał tylko na podpisane zatwierdzone obrazy

2. Konfiguracja i wymagania

Samodzielne konfigurowanie środowiska

  1. Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub wykorzystaj już istniejący. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Nazwa projektu jest wyświetlaną nazwą uczestników tego projektu. To ciąg znaków, który nie jest używany przez interfejsy API Google. W każdej chwili możesz ją zmienić.
  • Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić (po jego ustawieniu nie można go zmienić). Cloud Console automatycznie wygeneruje unikalny ciąg znaków. zwykle nieważne, co ona jest. W większości ćwiczeń z programowania konieczne jest odwołanie się do identyfikatora projektu (zwykle nazywa się on PROJECT_ID). Jeśli nie podoba Ci się wygenerowany identyfikator, możesz wygenerować kolejny losowy. Możesz też spróbować własnych sił i sprawdzić, czy jest dostępna. Potem nie będzie można go zmienić. Pozostanie ono przez czas trwania projektu.
  • Dostępna jest trzecia wartość, numer projektu, z którego korzystają niektóre interfejsy API. Więcej informacji o wszystkich 3 wartościach znajdziesz w dokumentacji.
  1. Następnie musisz włączyć płatności w Cloud Console, aby korzystać z zasobów Cloud/interfejsów API. Ukończenie tego ćwiczenia z programowania nie powinno kosztować zbyt wiele. Aby wyłączyć zasoby, aby nie naliczać opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub cały projekt. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartości 300 USD.

Uruchom edytor Cloud Shell

Ten moduł został opracowany i przetestowany pod kątem użycia z edytorem Google Cloud Shell. Aby uzyskać dostęp do edytora:

  1. wejdź na stronę swojego projektu Google na https://console.cloud.google.com.
  2. W prawym górnym rogu kliknij ikonę edytora Cloud Shell.

8560cc8d45e8c112.png

  1. Na dole okna otworzy się nowy panel

Konfiguracja środowiska

W Cloud Shell ustaw identyfikator i numer projektu. Zapisz je jako zmienne PROJECT_ID i PROJECT_ID.

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

Włączanie usług

Włącz wszystkie niezbędne usługi:

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 

Tworzenie repozytorium Artifact Registry

W tym module będziesz używać Artifact Registry do przechowywania i skanowania obrazów. Utwórz repozytorium za pomocą tego polecenia.

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

Skonfiguruj Dockera tak, aby wykorzystywał dane logowania gcloud podczas uzyskiwania dostępu do Artifact Registry.

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

3. Skanowanie automatyczne

Skanowanie artefaktów jest aktywowane automatycznie za każdym razem, gdy wypchniesz nowy obraz do Artifact Registry lub Container Registry. Informacje o lukach w zabezpieczeniach są stale aktualizowane w przypadku wykrywania nowych luk. W tej sekcji przekażesz obraz do Artifact Registry i przejrzysz wyniki.

Tworzenie katalogu roboczego i przekształcanie go w katalog roboczy

mkdir vuln-scan && cd vuln-scan

Definiowanie przykładowego obrazu

Utwórz plik o nazwie Dockerfile z poniższą zawartością.

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

Utwórz plik o nazwie main.py z następującą zawartością

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

Kompilowanie i przekazywanie obrazu do AR

Użyj Cloud Build, aby skompilować i automatycznie wypchnąć kontener do Artifact Registry. Zwróć uwagę na tag bad na obrazie. Pomoże Ci to zidentyfikować go na później.

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

Sprawdź szczegóły obrazu

Po zakończeniu procesu kompilacji sprawdź obraz i wyniki luk w zabezpieczeniach w panelu Artifact Registry.

  1. Otwórz Artifact Registry w konsoli Cloud
  2. Kliknij repozytorium skanowania artefaktów, aby wyświetlić zawartość
  3. Kliknij szczegóły obrazu
  4. Kliknij najnowsze podsumowanie obrazu
  5. Po zakończeniu skanowania kliknij kartę Luki w zabezpieczeniach, aby wyświetlić obraz.

Na karcie Luki w zabezpieczeniach zobaczysz wyniki automatycznego skanowania utworzonego właśnie obrazu.

361be7b3bf293fca.png

Automatyzacja skanowania jest domyślnie włączona. Zapoznaj się z ustawieniami Artifact Registry, aby dowiedzieć się, jak włączyć lub wyłączyć skanowanie automatyczne.

4. Skanowanie na żądanie

Istnieją różne sytuacje, w których konieczne może być uruchomienie skanowania przed przekazaniem obrazu do repozytorium. Programista kontenerów może na przykład przeskanować obraz i rozwiązać problemy przed przekazaniem kodu do elementu sterującego źródła. W przykładzie poniżej skompilujesz i przeanalizujesz obraz lokalnie, zanim zaczniesz korzystać z wyników.

Utwórz obraz

W tym kroku użyjesz lokalnego Dockera, aby utworzyć obraz w lokalnej pamięci podręcznej.

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

Zeskanuj obraz

Po utworzeniu obrazu poproś o jego zeskanowanie. Wyniki skanowania są przechowywane na serwerze metadanych. Zadanie zakończy się lokalizacją z wynikami na serwerze metadanych.

gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --format="value(response.scan)" > scan_id.txt

Sprawdź plik wyjściowy

Poświęć chwilę na zapoznanie się z danymi wyjściowymi poprzedniego kroku, które zostały zapisane w pliku scan_id.txt. Zwróć uwagę na lokalizację raportu wyników skanowania na serwerze metadanych.

cat scan_id.txt

Sprawdź szczegółowe wyniki skanowania

Aby wyświetlić rzeczywiste wyniki skanowania, użyj polecenia list-vulnerabilities w lokalizacji raportu podanej w pliku wyjściowym.

gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) 

Dane wyjściowe zawierają znaczną ilość danych o wszystkich lukach w zabezpieczeniach obrazu.

Oznacz problemy krytyczne

Ludzie rzadko korzystają z danych przechowywanych w raporcie bezpośrednio. Zwykle wyniki są używane w ramach zautomatyzowanego procesu. Za pomocą poniższych poleceń przeczytaj szczegóły raportu i zarejestruj, czy zostały wykryte KRYTYCZNE luki w zabezpieczeniach

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

Wynikiem tego polecenia będzie

Failed vulnerability check for CRITICAL level

5. Skanowanie potoku kompilacji

W tej sekcji utworzysz automatyczny potok kompilacji, który utworzy obraz kontenera, a następnie przeskanuj go i oceń wyniki. Jeśli nie zostaną wykryte kluczowe luki w zabezpieczeniach, obraz zostanie przeniesiony do repozytorium. W przypadku wykrycia KRYTYCZNYCH luk w zabezpieczeniach kompilacja zakończy się niepowodzeniem i zostanie zakończona.

Przyznaj dostęp kontu usługi Cloud Build

Cloud Build wymaga uprawnień dostępu do interfejsu API skanowania na żądanie. Przyznaj dostęp przy użyciu tych poleceń.

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"

Tworzenie potoku Cloud Build

Poniższe polecenie utworzy w katalogu plik cloudbuild.yaml, który będzie używany w automatycznym procesie. W tym przykładzie kroki są ograniczone do procesu tworzenia kontenera. W praktyce oprócz kroków kontenera musisz jednak umieścić instrukcje i testy dotyczące konkretnej aplikacji.

Utwórz plik za pomocą tego polecenia.

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

Uruchamianie potoku CI

Prześlij kompilację do przetworzenia, aby sprawdzić przerwy w działaniu kompilacji w przypadku wykrycia luki w zabezpieczeniach o znaczeniu KRYTYCZNYM.

gcloud builds submit

Sprawdź błąd kompilacji

Przesłana kompilacja zakończy się niepowodzeniem, ponieważ obraz zawiera krytyczne luki w zabezpieczeniach.

Sprawdź błąd kompilacji na stronie Historia Cloud Build

Usuwanie luki w zabezpieczeniach

Zaktualizuj plik Dockerfile, aby użyć obrazu podstawowego, który nie zawiera luk w zabezpieczeniach o charakterze krytycznym.

Zastąp plik Dockerfile, aby używać obrazu Debian 10 za pomocą tego polecenia

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

Uruchamianie procesu CI z użyciem prawidłowego obrazu

Prześlij kompilację do przetworzenia, aby sprawdzić, czy kompilacja się powiedzie, jeśli nie znajdziemy żadnych luk w zabezpieczeniach o poziomie krytycznym.

gcloud builds submit

Sprawdź udaną kompilację

Przesłana kompilacja zakończy się powodzeniem, ponieważ zaktualizowany obraz nie zawiera luk w zabezpieczeniach KRYTYCZNYCH.

Sprawdź udaną kompilację na stronie Historia Cloud Build

Sprawdź wyniki skanowania

Sprawdź dobry obraz w Artifact Registry

  1. Otwórz Artifact Registry w konsoli Cloud
  2. Kliknij repozytorium skanowania artefaktów, aby wyświetlić zawartość
  3. Kliknij szczegóły obrazu
  4. Kliknij najnowsze podsumowanie obrazu
  5. Kliknij kartę luk w zabezpieczeniach obrazu

6. Podpisywanie obrazów

Tworzenie notatki atestatora

Notatka atestatora to po prostu niewielka ilość danych służąca jako etykieta stosowanego typu podpisu. Jedna notatka może na przykład wskazywać na skanowanie pod kątem luk w zabezpieczeniach, a druga – do zatwierdzenia przez kontrolę jakości. Zostanie ona przywołana podczas procesu podpisywania.

Tworzenie notatki

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

Zapisywanie notatki

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}"

Sprawdzanie notatki

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

Tworzenie atestatora

Atestatory służą do przeprowadzania rzeczywistego procesu podpisywania obrazu i dołączają wystąpienie notatki do obrazu na potrzeby późniejszej weryfikacji. Utwórz atestator do późniejszego użycia.

Utwórz atestator

ATTESTOR_ID=vulnz-attestor

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

Zweryfikuj atestator

gcloud container binauthz attestors list

Uwaga: ostatni wiersz wskazuje NUM_PUBLIC_KEYS: 0, które przekażesz w następnym kroku.

Pamiętaj też, że Cloud Build automatycznie tworzy w projekcie atestator built-by-cloud-build po uruchomieniu kompilacji generującej obrazy. Polecenie powyżej zwraca 2 atestatory: vulnz-attestor i built-by-cloud-build. Po skompilowaniu obrazów Cloud Build automatycznie podpisuje i tworzy dla nich atesty.

Dodawanie roli uprawnień

Konto usługi Binary Authorization musi mieć uprawnienia do wyświetlania informacji o atestach. Przyznaj dostęp za pomocą następującego wywołania interfejsu 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

Utwórz zasadę uprawnień przy użyciu pliku

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"

Dodawanie klucza KMS

Atestator potrzebuje kluczy kryptograficznych, aby dołączyć notatkę i dostarczyć możliwe do zweryfikowania podpisy. W tym kroku utworzysz i zapiszesz klucze w KMS, aby umożliwić Cloud Build później dostęp do nich.

Najpierw dodaj zmienne środowiskowe do opisania nowego klucza

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

Tworzenie pęku kluczy do przechowywania zestawu kluczy

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

Utwórz nową asymetryczną parę kluczy podpisywania dla atestatora

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

Klucz powinien pojawić się na stronie KMS w konsoli Google Cloud.

Powiąż klucz z atestatorem za pomocą polecenia 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}"

Po ponownym wydrukowaniu listy urzędów powinien pojawić się zarejestrowany klucz:

gcloud container binauthz attestors list

Tworzenie podpisanego atestu

W tej chwili masz skonfigurowane funkcje, które umożliwiają podpisywanie obrazów. Użyj utworzonego wcześniej atestatora do podpisania obrazu kontenera, z którym pracujesz

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)')

Teraz możesz utworzyć atest za pomocą gcloud. Polecenie pobiera po prostu szczegóły klucza, którego chcesz użyć do podpisywania, oraz konkretny obraz kontenera, który chcesz zatwierdzić.

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}"

W warunkach analizy kontenera spowoduje to utworzenie nowego wystąpienia i dołączenie go do notatki atestatora. Aby mieć pewność, że wszystko działa zgodnie z oczekiwaniami, możesz dodać listę atestów

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

7. Podpisywanie za pomocą Cloud Build

Masz włączone podpisywanie obrazów i ręcznie używasz atestatora do podpisania przykładowego obrazu. W praktyce warto stosować atesty podczas zautomatyzowanych procesów, takich jak potoki CI/CD.

W tej sekcji skonfigurujesz Cloud Build tak, aby automatycznie atestował obrazy

Role

Dodaj rolę wyświetlającego atestator autoryzacji plików binarnych do konta usługi Cloud Build:

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

Dodaj rolę podpisującego lub weryfikatora klucza CryptoKey Cloud KMS do konta usługi Cloud Build (podpisywanie przy użyciu KMS):

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

Dodaj rolę dołączającego notatki analizy kontenera do konta usługi Cloud Build:

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

Przygotuj krok kompilacji niestandardowej Cloud Build

Aby uprościć proces poświadczania, będziesz używać kroku kompilacji niestandardowej w Cloud Build. Google udostępnia ten etap kompilacji niestandardowej, który zawiera funkcje pomocnicze, które upraszczają ten proces. Przed użyciem kodu wykonania niestandardowego kroku kompilacji należy go umieścić w kontenerze i przekazać do Cloud Build. Aby to zrobić, uruchom następujące polecenia:

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

Dodaj krok podpisywania do pliku cloudbuild.yaml

W tym kroku dodasz krok poświadczania do utworzonego wcześniej potoku Cloud Build.

  1. Zapoznaj się z nowym krokiem, który chcesz dodać.

Tylko recenzja. Nie kopiuj

#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. Zastąp plik cloudbuild.yaml zaktualizowanym kompletnym potokiem.
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

Uruchamianie kompilacji

gcloud builds submit

Sprawdzanie kompilacji w historii Cloud Build

W konsoli Cloud otwórz stronę Historia Cloud Build i sprawdź tę najnowszą kompilację oraz udane jej wykonanie.

8. Zasady kontroli wstępu

Binary Authorization to funkcja w GKE i Cloud Run, która umożliwia sprawdzanie reguł przed uruchomieniem obrazu kontenera. Weryfikacja jest wykonywana przy każdym żądaniu uruchomienia obrazu, niezależnie od tego, czy jest to zaufany potok CI/CD, czy też użytkownik próbujący ręcznie wdrożyć obraz. Ta funkcja pozwala skuteczniej zabezpieczać środowiska wykonawcze niż same testy potoku CI/CD.

Aby zrozumieć tę funkcję, zmodyfikuj domyślną zasadę GKE tak, aby wymuszała rygorystyczną regułę autoryzacji.

Tworzenie klastra GKE

Utwórz klaster GKE:

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

Zezwól Cloud Build na wdrażanie w tym klastrze:

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

Zezwalaj na wszystkie zasady

Najpierw sprawdź domyślny stan zasady i możliwość wdrożenia dowolnego obrazu

  1. Sprawdź obecną zasadę
gcloud container binauthz policy export
  1. Zauważ, że zasada egzekwowania jest ustawiona na ALWAYS_ALLOW

evaluationMode: ALWAYS_ALLOW

  1. Wdróż przykład, aby sprawdzić, czy możesz wdrożyć wszystko
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. Sprawdź, czy wdrożenie zadziałało
kubectl get pods

Zobaczysz następujące dane wyjściowe

161db370d99ffb13.png

  1. Usuń wdrożenie
kubectl delete pod hello-server

Odrzuć wszystkie zasady

Teraz zaktualizuj zasadę, żeby nie zezwalać na wyświetlanie wszystkich obrazów.

  1. Eksportuj bieżącą zasadę do pliku z możliwością edycji
gcloud container binauthz policy export  > policy.yaml
  1. Zmień zasadę

W edytorze tekstu zmień ustawienie evaluationMode z ALWAYS_ALLOW na ALWAYS_DENY.

edit policy.yaml

Plik YAML zasad powinien wyglądać tak:

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_DENY
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. Otwórz Terminal i zastosuj nową zasadę, a następnie poczekaj kilka sekund na rozpowszechnienie zmiany
gcloud container binauthz policy import policy.yaml
  1. Próba wdrożenia przykładowego zbioru zadań
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. Nie udało się wdrożyć i wyświetla się następujący komunikat
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

Przywróć zasadę, by zezwolić na wszystkie

Zanim przejdziesz do następnej sekcji, cofnij zmiany zasad.

  1. Zmień zasadę

W edytorze tekstu zmień wartość evaluationMode z ALWAYS_DENY na ALWAYS_ALLOW.

edit policy.yaml

Plik YAML zasad powinien wyglądać tak:

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_ALLOW
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. Zastosuj cofniętą zasadę
gcloud container binauthz policy import policy.yaml

9. Blokuj luki w zabezpieczeniach w GKE

W tej sekcji połączysz zdobytą wiedzę dzięki wdrożeniu potoku CI/CD za pomocą Cloud Build, który skanuje obrazy, a potem sprawdzisz, czy przed podpisaniem obrazu i próbą wdrożenia masz do czynienia z lukami w zabezpieczeniach. GKE użyje autoryzacji plików binarnych do sprawdzenia, czy obraz ma podpis ze skanowania pod kątem luk w zabezpieczeniach, zanim zezwoli na uruchomienie obrazu.

d5c41bb89e22fd61.png

Zaktualizuj zasadę GKE, aby wymagać atestu

Wymagaj, aby obrazy były podpisane przez atestator przez dodanie clusterAdmissionRules do zasady GKE BinAuth

Zastąp zasadę zaktualizowaną konfiguracją za pomocą poniższego polecenia.

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

Zastosuj zasadę

gcloud beta container binauthz policy import binauth_policy.yaml

Spróbuj wdrożyć niepodpisany obraz

Utwórz deskryptor wdrożenia dla aplikacji utworzonej wcześniej za pomocą poniższego polecenia. Użyty tutaj obraz to utworzony wcześniej obraz, który zawiera krytyczne luki w zabezpieczeniach i NIE zawiera podpisanego atestu.

Kontrolery ruchu przychodzącego GKE muszą znać dokładnego obrazu, który ma zostać wdrożony, aby spójnie weryfikować podpis. W tym celu użyj skrótu obrazu i prostego tagu.

Pobierz podsumowanie obrazu niewłaściwego

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)')

Używanie skrótu w konfiguracji 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

Spróbuj wdrożyć aplikację w GKE.

kubectl apply -f deploy.yaml

Przejrzyj zbiór zadań w konsoli i zanotuj błąd z informacją o odrzuceniu wdrożenia:

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

Wdrażanie podpisanego obrazu

Pobierz podsumowanie obrazu niewłaściwego

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)')

Używanie skrótu w konfiguracji 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

Wdrażanie aplikacji w GKE

kubectl apply -f deploy.yaml

Przejrzyj zbiór zadań w konsoli i zanotuj udane wdrożenie obrazu.

10. Gratulacje!

Gratulacje. Udało Ci się ukończyć ćwiczenia z programowania.

Omówione zagadnienia:

  • Jak włączyć automatyczne skanowanie
  • Jak wykonywać skanowanie na żądanie
  • Jak zintegrować skanowanie z potokiem kompilacji
  • Jak podpisywać zatwierdzone obrazy
  • Jak używać kontrolerów akceptacji GKE do blokowania obrazów
  • Jak skonfigurować GKE, aby zezwalał tylko na podpisane zatwierdzone obrazy

Co dalej:

Czyszczenie danych

Aby uniknąć obciążenia konta Google Cloud opłatami za zasoby zużyte w tym samouczku, możesz usunąć projekt zawierający te zasoby lub zachować projekt i usunąć poszczególne zasoby.

Usuwam projekt

Najprostszym sposobem na uniknięcie płatności jest usunięcie projektu utworzonego na potrzeby samouczka.