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 wykonuje skanowanie pod kątem luk w zabezpieczeniach obrazów w Artifact Registry i Container Registry, a następnie przechowuje uzyskane metadane i udostępnia je do użycia za pomocą interfejsu API. Przechowywanie metadanych umożliwia przechowywanie informacji z różnych źródeł, w tym z usług skanowania pod kątem luk w zabezpieczeniach, usług Google Cloud i 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, gdy wykrywane są nowe luki.
  • Gdy włączone jest skanowanie na żądanie, musisz uruchomić polecenie, aby zeskanować obraz lokalny lub obraz w Artifact Registry lub Container Registry. Skanowanie na żądanie zapewnia elastyczność w zakresie skanowania kontenerów. Możesz na przykład przeskanować obraz utworzony lokalnie i usunąć luki w zabezpieczeniach, zanim zapiszesz go w rejestrze. Wyniki skanowania są dostępne przez 48 godzin po zakończeniu skanowania, a informacje o lukach w zabezpieczeniach nie są aktualizowane po zakończeniu skanowania.

Dzięki temu, że usługa Container Analysis jest zintegrowana z Twoim kanałem CI/CD, możesz podejmować decyzje na podstawie tych metadanych. Możesz na przykład użyć autoryzacji plików binarnych, aby utworzyć zasady wdrażania, które zezwalają na wdrażanie tylko zgodnych obrazów z zaufanych rejestrów.

Czego się nauczysz

  • Jak włączyć automatyczne skanowanie
  • Jak wykonać skanowanie na żądanie
  • Jak zintegrować skanowanie w przepływie tworzenia
  • Jak podpisywać zatwierdzone obrazy
  • Jak używać kontrolerów dostępu GKE do blokowania obrazów
  • Jak skonfigurować GKE, aby zezwalać tylko na podpisane zatwierdzone obrazy

2. Konfiguracja i wymagania

Konfigurowanie środowiska we własnym tempie

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

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Nazwa projektu to wyświetlana nazwa uczestników tego projektu. Jest to ciąg znaków, którego nie używają interfejsy API Google. Możesz ją zaktualizować w dowolnym momencie.
  • Identyfikator projektu jest niepowtarzalny w ramach wszystkich projektów Google Cloud i nie można go zmienić (po ustawieniu). Cloud Console automatycznie generuje unikalny ciąg znaków. Zwykle nie ma znaczenia, jaki to ciąg. W większości laboratoriów z kodem musisz odwoływać się do identyfikatora projektu (zazwyczaj jest to PROJECT_ID). Jeśli nie podoba Ci się wygenerowany identyfikator, możesz wygenerować inny losowy. Możesz też spróbować użyć własnego adresu e-mail i sprawdzić, czy jest on dostępny. Po tym kroku nie można go zmienić. Pozostanie ona niezmieniona przez cały czas trwania projektu.
  • Informacyjnie: istnieje jeszcze 3 wartość, numer projektu, którego używają niektóre interfejsy API. Więcej informacji o wszystkich 3 wartościach znajdziesz w dokumentacji.
  1. Następnie musisz włączyć rozliczenia w konsoli Cloud, aby korzystać z zasobów i interfejsów API Cloud. Przejście przez ten moduł Codelab nie powinno wiązać się z wielkimi kosztami, jeśli w ogóle z nimi będzie. Aby wyłączyć zasoby, aby nie generować opłat po zakończeniu samouczka, możesz usunąć utworzone zasoby lub cały projekt. Nowi użytkownicy Google Cloud mogą skorzystać z bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.

Uruchamianie edytora Cloud Shell

Ten moduł praktyczny został zaprojektowany i przetestowany pod kątem korzystania z edytora Google Cloud Shell. Aby uzyskać dostęp do edytora:

  1. uzyskać dostęp do projektu Google na stronie https://console.cloud.google.com.
  2. W prawym górnym rogu kliknij ikonę edytora powłoki chmury.

8560cc8d45e8c112.png

  1. Nowy panel otworzy się u dołu okna.

Konfiguracja środowiska

W Cloud Shell ustaw identyfikator i numer projektu. Zapisz je jako zmienne PROJECT_IDPROJECT_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 laboratorium do przechowywania i skanowania obrazów użyjesz Artifact Registry. 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, aby używać danych logowania gcloud podczas uzyskiwania dostępu do Artifact Registry.

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

3. Automatyczne skanowanie

Skanowanie artefaktów jest uruchamiane automatycznie za każdym razem, gdy przesyłasz nowy obraz do Artifact Registry lub Container Registry. Informacje o lukach w zabezpieczeniach są stale aktualizowane, gdy wykrywane są nowe luki. W tej sekcji prześlesz obraz do Artifact Registry i sprawdzisz wyniki.

Tworzenie katalogu roboczego i przechodzenie do niego

mkdir vuln-scan && cd vuln-scan

Definiowanie przykładowego obrazu

Utwórz plik o nazwie Dockerfile z tą 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 tą 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

Tworzenie i przekazywanie obrazu do AR

Użyj Cloud Build, aby skompilować i automatycznie przesłać kontener do Artifact Registry. Zwróć uwagę na tag bad na obrazie. Pomoże Ci to zidentyfikować go w kolejnych krokach.

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

Szczegóły obrazu

Po zakończeniu procesu kompilacji przejrzyj wyniki dotyczące obrazu i luk w zabezpieczeniach w panelu Artifact Registry.

  1. Otwórz Artifact Registry w konsoli Cloud
  2. Aby wyświetlić zawartość, kliknij repozytorium artifact-scanning-repo.
  3. Kliknij szczegóły obrazu.
  4. Kliknij najnowszy przegląd swojego obrazu.
  5. Po zakończeniu skanowania kliknij kartę z podanymi wrażliwościami obrazu.

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

361be7b3bf293fca.png

Automatyczne skanowanie jest domyślnie włączone. W ustawieniach Artifact Registry dowiesz się, jak wyłączyć lub włączyć automatyczne skanowanie.

4. Skanowanie na żądanie

W różnych sytuacjach może być konieczne przeprowadzenie skanowania przed przesłaniem obrazu do repozytorium. Na przykład deweloper kontenera może zeskanować obraz i naprawić problemy, zanim przekaże kod do kontroli źródłowej. W przykładzie poniżej skompilujesz i przeanalizujesz obraz lokalnie, zanim wykorzystasz wyniki.

Kompilowanie obrazu

Na tym etapie użyjesz lokalnego Dockera do skompilowania obrazu do lokalnej pamięci podręcznej.

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

Skanowanie obrazu

Po utworzeniu obrazu poproś o jego zeskanowanie. Wyniki skanowania są przechowywane na serwerze metadanych. Zadanie kończy się z lokalizacją wyników 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

Sprawdzanie pliku wyjściowego

Poświęć chwilę na sprawdzenie danych wyjściowych z 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

Sprawdzanie szczegółowych wyników skanowania

Aby wyświetlić rzeczywiste wyniki skanowania, użyj polecenia list-vulnerabilities w miejscu raportu podanym 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 w obrazie.

Oznaczanie krytycznych problemów

Użytkownicy rzadko korzystają bezpośrednio z danych zapisanych w raporcie. Wyniki są zwykle wykorzystywane przez proces automatyczny. Aby odczytać szczegóły raportu i sprawdzać, czy znaleziono jakieś krytyczne luki, użyj poniższych poleceń.

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

Wynik tego polecenia będzie taki:

Failed vulnerability check for CRITICAL level

5. Skanowanie potoku kompilacji

W tej sekcji utworzysz automatyczny potok kompilacji, który skompiluje obraz kontenera, zeskanuje go, a potem oceni wyniki. Jeśli nie zostaną znalezione żadne krytyczne luki, obraz zostanie przesłany do repozytorium. Jeśli zostaną znalezione luki CRITICAL, kompilacja zakończy się niepowodzeniem i zostanie zamknięta.

Przyznawanie dostępu do konta usługi Cloud Build

Cloud Build będzie potrzebować uprawnień dostępu do interfejsu skanowania na żądanie. Aby przyznać dostęp, użyj 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

Podane niżej polecenie utworzy w katalogu plik cloudbuild.yaml, który będzie używany w ramach procesu zautomatyzowanego. W tym przykładzie czynności ograniczają się do procesu kompilacji kontenera. W praktyce jednak oprócz instrukcji dotyczących kontenera należy dodać 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ć, czy kompilacja się zatrzymuje, gdy zostanie znaleziona luka o poważnym stopniu zagrożenia.

gcloud builds submit

Niepowodzenie kompilacji opinii

Przesłany przez Ciebie pakiet nie zostanie zaakceptowany, ponieważ obraz zawiera KRYTYCZNE luki w zabezpieczeniach.

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

Naprawianie luki w zabezpieczeniach

Zaktualizuj plik Dockerfile, aby używać obrazu bazowego, który nie zawiera podatności na zagrożenia o krytycznym znaczeniu.

Zastąp 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

Uruchom proces CI z dobrym obrazem.

Prześlij kompilację do przetwarzania, aby sprawdzić, czy kompilacja zakończy się sukcesem, gdy nie zostaną znalezione żadne luki o poważnym stopniu zagrożenia.

gcloud builds submit

Sprawdzanie powodzenia kompilacji

Przesłany przez Ciebie pakiet zostanie zaakceptowany, ponieważ zaktualizowany obraz nie zawiera żadnych krytycznych luk w zabezpieczeniach.

Sprawdź, czy kompilacja się powiodła, na stronie Historia Cloud Build.

Sprawdzanie wyników skanowania

Sprawdź prawidłowy obraz w Artifact Registry.

  1. Otwórz Artifact Registry w konsoli Cloud
  2. Aby wyświetlić zawartość, kliknij repozytorium artifact-scanning-repo.
  3. Kliknij szczegóły obrazu.
  4. Kliknij najnowszy przegląd swojego obrazu.
  5. Kliknij kartę Luki w zabezpieczeniach obrazu.

6. Podpisywanie obrazów

Tworzenie notatki atestatora

Uwaga weryfikatora to po prostu niewielka ilość danych, która służy jako etykieta typu podpisu. Na przykład jedna uwaga może dotyczyć skanowania pod kątem podatności, a druga może być używana do zatwierdzenia kontroli jakości. Uwaga będzie się pojawiać 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}"

Sprawdź notatkę

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żywane do przeprowadzania procesu podpisywania obrazu i dołączają do obrazu notatkę na potrzeby późniejszej weryfikacji. Utwórz atestatora na potrzeby 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}

Weryfikacja atestatora

gcloud container binauthz attestors list

Ostatni wiersz wskazuje, że NUM_PUBLIC_KEYS: 0 klucze podasz w następnym kroku.

Pamiętaj też, że Cloud Build automatycznie tworzy w Twoim projekcie atestorów built-by-cloud-build, gdy uruchamiasz kompilację, która generuje obrazy. Dlatego polecenie z powyższego przykładu zwraca 2 osoby potwierdzające: vulnz-attestorbuilt-by-cloud-build. Po pomyślnym skompilowaniu obrazów Cloud Build automatycznie podpisuje je i tworzy dla nich oświadczenia.

Dodawanie roli uprawnień

Konto usługi Binary Authorization będzie musiało mieć uprawnienia do wyświetlania notatek dotyczących uwierzytelniania. Zezwól na dostęp za pomocą tego 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

Użyj pliku do utworzenia zasady uprawnień

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

Aby załączyć notatkę i podać podpisy weryfikowalne, weryfikator musi mieć klucze kryptograficzne. Na tym etapie utworzysz i zapiszesz klucze w KMS, aby Cloud Build mógł z nich korzystać.

Najpierw dodaj zmienne środowiskowe, aby opisać nowy klucz

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

Tworzenie wieszaka na klucze

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

Utwórz nową parę kluczy podpisywania asymetrycznych dla weryfikatora

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.

Teraz powiązać klucz z weryfikatorem 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}"

Jeśli wydrukujesz listę urzędów ponownie, powinieneś zobaczyć zarejestrowany klucz:

gcloud container binauthz attestors list

Tworzenie podpisanego atestu

W tym momencie masz skonfigurowane funkcje, które umożliwiają podpisywanie obrazów. Użyj wcześniej utworzonego narzędzia Attestor 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ć zaświadczenie za pomocą gcloud. Polecenie zawiera informacje o kluczu, którego chcesz użyć do podpisywania, oraz informacje o konkretnym obrazie 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 terminologii Container Analysis spowoduje to utworzenie nowego wystąpienia i dołączenie go do notatki weryfikatora. Aby mieć pewność, że wszystko działa zgodnie z oczekiwaniami, możesz wyświetlić swoje oświadczenia

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

7. Podpisywanie za pomocą Cloud Build

masz włączone podpisywanie obrazu i ręcznie używasz narzędzia Attestor do podpisywania przykładowego obrazu. W praktyce warto stosować uwierzytelnienia podczas zautomatyzowanych procesów, takich jak potoki CI/CD.

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

Role

Dodaj do konta usługi Cloud Build rolę Weryfikator autoryzacji plików binarnych (podgląd):

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

Dodaj do konta usługi Cloud Build rolę Podpisujący/weryfikujący klucze CryptoKey Cloud KMS (podpisywanie na podstawie KMS):

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

Dodaj do konta usługi Cloud Build rolę dodający notatki analizy kontenera:

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

Przygotuj krok niestandardowego procesu tworzenia w Cloud Build

Aby uprościć proces uwierzytelniania, użyjesz w Cloud Build kroku kompilacji niestandardowej. Google udostępnia ten krok kompilacji niestandardowej, który zawiera funkcje pomocnicze ułatwiające ten proces. Zanim użyjesz kodu niestandardowego kroku kompilacji, musisz go skompilować w kontenerze i przesłać do Cloud Build. Aby to zrobić, uruchom te 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

Dodawanie kroku podpisywania do pliku cloudbuild.yaml

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

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

Tylko do sprawdzenia. Nie kopiować

#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 potworkiem.
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

Otwórz Cloud Console na stronie Historia Cloud Build i sprawdź najnowszą kompilację oraz udane wykonanie kroków kompilacji.

8. Zasady kontroli dostępu

Autoryzacja plików binarnych to funkcja w GKE i Cloud Run, która umożliwia sprawdzanie reguł przed zezwoleniem na uruchomienie obrazu kontenera. Weryfikacja jest wykonywana w przypadku każdego żądania uruchomienia obrazu, czy to z zaufanego potoku CI/CD, czy też gdy użytkownik ręcznie próbuje wdrożyć obraz. Ta funkcja pozwala skuteczniej chronić środowisko w czasie wykonywania niż tylko sprawdzanie potoku CI/CD.

Aby zrozumieć tę funkcję, zmodyfikujesz domyślne zasady GKE, aby zastosować ścisłą 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

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

Zasada zezwalająca

Najpierw sprawdź stan domyślnych zasad i możliwość wdrożenia dowolnego obrazu.

  1. Sprawdzanie istniejącej zasady
gcloud container binauthz policy export
  1. Zwróć uwagę, że zasada egzekwowania jest ustawiona na ALWAYS_ALLOW

evaluationMode: ALWAYS_ALLOW

  1. Wdróż przykład, aby sprawdzić, czy możesz wdrożyć cokolwiek
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. Sprawdzanie, czy wdrożenie się powiodło
kubectl get pods

Powinny się wyświetlić te dane wyjściowe

161db370d99ffb13.png

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

Zasada odrzucania wszystkich

Zaktualizuj teraz zasady, aby zablokować wszystkie obrazy.

  1. Eksportowanie bieżących zasad do edytowalnego pliku
gcloud container binauthz policy export  > policy.yaml
  1. Zmień zasadę

W edytorze tekstu zmień wartość parametru 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ę. Poczekaj kilka sekund, aż zmiana zostanie rozpowszechniona.
gcloud container binauthz policy import policy.yaml
  1. Próba wdrożenia przykładowego zadania
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. Wdrożenie kończy się niepowodzeniem z tego powodu
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óć zasady, aby zezwolić na wszystkie

Zanim przejdziesz do następnej sekcji, cofnij zmiany zasad

  1. Zmień zasadę

W edytorze tekstu zmień wartość parametru 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 przywróconą zasadę
gcloud container binauthz policy import policy.yaml

9. Blokowanie luk w zabezpieczeniach w GKE

W tej sekcji połączysz dotychczasową wiedzę, wdrażając potok CI/CD za pomocą usługi Cloud Build, która skanuje obrazy, a potem sprawdza, czy nie zawierają one luk w zabezpieczeniach, zanim zostaną podpisane i wdrożone. Zanim pozwoli na uruchomienie obrazu, GKE użyje autoryzacji plików binarnych, aby sprawdzić, czy obraz ma podpis z skanowania pod kątem luk w zabezpieczeniach.

d5c41bb89e22fd61.png

Zmiana zasad GKE na wymagające uwierzytelniania

Wymagaj, aby obrazy były podpisane przez Attestera, dodając clusterAdmissionRules do zasad autoryzacji Bin GKE.

Zastąp zasadę zaktualizowaną konfiguracją, używając podanego niżej 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

Stosowanie zasad

gcloud beta container binauthz policy import binauth_policy.yaml

Próba wdrożenia niepodpisanego obrazu

Utwórz opis wdrożenia dla wcześniej utworzonej aplikacji za pomocą tego polecenia. Obraz użyty w tym przykładzie to obraz utworzony wcześniej, który zawiera krytyczne luki w zabezpieczeniach i NIE zawiera podpisanego atestu.

Kontrolery dostępu GKE muszą znać dokładny obraz, który ma zostać wdrożony, aby konsekwentnie weryfikować podpis. Aby to zrobić, musisz użyć podsumowania obrazu i prostego tagu.

Pobierz skrót obrazu, który nie spełnia wymogów

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 digesta 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

Próba wdrożenia aplikacji w GKE

kubectl apply -f deploy.yaml

Sprawdź obciążenie w konsoli i zwróć uwagę na błąd informujący o odrzuceniu wdrożenia:

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

Wdrażanie podpisanego obrazu

Pobierz skrót obrazu, który nie spełnia wymogów

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 digesta 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

Sprawdź zadanie w konsoli i sprawdź, czy obraz został prawidłowo wdrożony.

10. Gratulacje!

Gratulacje! Masz ukończone zajęcia.

Omówione zagadnienia:

  • Jak włączyć automatyczne skanowanie
  • Jak wykonać skanowanie na żądanie
  • Jak zintegrować skanowanie w przepływie tworzenia
  • Jak podpisywać zatwierdzone obrazy
  • Jak używać kontrolerów dostępu 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.

Usuwanie projektu

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