From e82211b8b1419bd4af9daee3d0436b2d5234566c Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Thu, 13 Oct 2022 09:32:42 -0400 Subject: [PATCH] start workload identity federation --- .../set-up-workload-identity-federation.md | 116 ++++++++++++++++++ content/whereami/deploy-app.md | 2 +- .../whereami/deploy-authorization-policy.md | 2 +- content/whereami/deploy-network-policy.md | 2 +- content/whereami/deploy-sidecar.md | 2 +- content/whereami/prepare-container.md | 85 ++++++++++--- content/whereami/set-up-whereami-git-repo.md | 3 + .../set-up-workload-identity-federation.md | 69 +++++++++++ 8 files changed, 258 insertions(+), 23 deletions(-) create mode 100644 content/tenant-project/set-up-workload-identity-federation.md create mode 100644 content/whereami/set-up-workload-identity-federation.md diff --git a/content/tenant-project/set-up-workload-identity-federation.md b/content/tenant-project/set-up-workload-identity-federation.md new file mode 100644 index 00000000..29944079 --- /dev/null +++ b/content/tenant-project/set-up-workload-identity-federation.md @@ -0,0 +1,116 @@ +--- +title: "Set up Workload Identity Federation" +weight: 4 +description: "Duration: 10 min | Persona: Platform Admin" +tags: ["kcc", "platform-admin", "security-tips", "wif"] +--- +![Platform Admin](/images/platform-admin.png) +_{{< param description >}}_ + +In this section, you will set up Workload Identity Federation for the Tenant project. Workload Identity Federation will be used later in this workshop by the GitHub actions building the containers for the applications like Whereami and Online Boutique and then will push them into the private Artifact Registry. This replaces the need to download the Google Service Account keys, so it's improving your security posture. + +Define variables: +```Bash +WORK_DIR=~/ +source ${WORK_DIR}acm-workshop-variables.sh +echo "export WORKLOAD_IDENTITY_POOL_NAME=container-images-builder-wi-pool" >> ${WORK_DIR}acm-workshop-variables.sh +echo "export CONTAINERS_BUILDER_SERVICE_ACCOUNT_NAME=container-images-builder" >> ${WORK_DIR}acm-workshop-variables.sh +source ${WORK_DIR}acm-workshop-variables.sh +``` + +## Configure the Workload Identity Federation + +Define the [Workload Identity Pool](https://cloud.google.com/config-connector/docs/reference/resource-docs/iam/iamworkloadidentitypool): +```Bash +cat < ${WORK_DIR}$HOST_PROJECT_DIR_NAME/projects/$TENANT_PROJECT_ID/workkloadidentitypool.yaml +apiVersion: iam.cnrm.cloud.google.com/v1beta1 +kind: IAMWorkloadIdentityPool +metadata: + name: ${WORKLOAD_IDENTITY_POOL_NAME} +spec: + location: global + displayName: ${WORKLOAD_IDENTITY_POOL_NAME} + projectRef: + external: projects/${TENANT_PROJECT_ID} +EOF +``` + +Define the [Workload Identity Pool Provider](https://cloud.google.com/config-connector/docs/reference/resource-docs/iam/iamworkloadidentitypoolprovider) for the GitHub repositories: +```Bash +cat < ${WORK_DIR}$HOST_PROJECT_DIR_NAME/projects/$TENANT_PROJECT_ID/workkloadidentitypoolprovider.yaml +apiVersion: iam.cnrm.cloud.google.com/v1beta1 +kind: IAMWorkloadIdentityPoolProvider +metadata: + name: ${WORKLOAD_IDENTITY_POOL_NAME} +spec: + projectRef: + external: projects/${TENANT_PROJECT_ID} + location: global + workloadIdentityPoolRef: + name: ${WORKLOAD_IDENTITY_POOL_NAME} + attributeMapping: + google.subject: assertion.repository + attribute.actor: assertion.actor + attribute.aud: assertion.aud + attribute.repository: assertion.repository + oidc: + issuerUri: "https://token.actions.githubusercontent.com" +EOF +``` + +## Configure the Google Service Account + +Define a [Google Service Account](https://cloud.google.com/config-connector/docs/reference/resource-docs/iam/iamserviceaccount) which will used later by the GitHub actions in order to build the container images: +```Bash +cat < ${WORK_DIR}$HOST_PROJECT_DIR_NAME/projects/$TENANT_PROJECT_ID/containers-builder-sa.yaml +apiVersion: iam.cnrm.cloud.google.com/v1beta1 +kind: IAMServiceAccount +metadata: + name: ${CONTAINERS_BUILDER_SERVICE_ACCOUNT_NAME} + namespace: ${TENANT_PROJECT_ID} +spec: + displayName: ${CONTAINERS_BUILDER_SERVICE_ACCOUNT_NAME} +EOF +``` + +## Deploy Kubernetes manifests + +```Bash +cd ${WORK_DIR}$HOST_PROJECT_DIR_NAME/ +git add . && git commit -m "Setting up Workload Identity Federation" && git push origin main +``` + +## Check deployments + +{{< mermaid >}} +graph TD; + IAMServiceAccount-.->Project + IAMWorkloadIdentityPool-.->Project + IAMWorkloadIdentityPoolProvider-->IAMWorkloadIdentityPool +{{< /mermaid >}} + +List the Kubernetes resources managed by Config Sync in **Config Controller** for the **Host project configs** repository: +```Bash +gcloud alpha anthos config sync repo describe \ + --project $HOST_PROJECT_ID \ + --managed-resources all \ + --sync-name root-sync \ + --sync-namespace config-management-system +``` +Wait and re-run this command above until you see `"status": "SYNCED"` for this `RootSync`. All the `managed_resources` listed should have `STATUS: Current` as well. + +List the GitHub runs for the **Host project configs** repository: +```Bash +cd ${WORK_DIR}$HOST_PROJECT_DIR_NAME && gh run list +``` + +List the Google Cloud resources created: +```Bash +gcloud iam workload-identity-pools describe $WORKLOAD_IDENTITY_POOL_NAME \ + --location global \ + --project $TENANT_PROJECT_ID +gcloud iam workload-identity-pools providers describe $WORKLOAD_IDENTITY_POOL_NAME \ + --workload-identity-pool $WORKLOAD_IDENTITY_POOL_NAME \ + --location global \ + --project $TENANT_PROJECT_ID +``` \ No newline at end of file diff --git a/content/whereami/deploy-app.md b/content/whereami/deploy-app.md index b539cd82..bf1b1c0b 100644 --- a/content/whereami/deploy-app.md +++ b/content/whereami/deploy-app.md @@ -1,6 +1,6 @@ --- title: "Deploy app" -weight: 4 +weight: 5 description: "Duration: 10 min | Persona: Apps Operator" tags: ["apps-operator", "asm"] --- diff --git a/content/whereami/deploy-authorization-policy.md b/content/whereami/deploy-authorization-policy.md index 08c81a16..b0b58451 100644 --- a/content/whereami/deploy-authorization-policy.md +++ b/content/whereami/deploy-authorization-policy.md @@ -1,6 +1,6 @@ --- title: "Deploy AuthorizationPolicy" -weight: 7 +weight: 8 description: "Duration: 5 min | Persona: Apps Operator" tags: ["apps-operator", "asm", "security-tips"] --- diff --git a/content/whereami/deploy-network-policy.md b/content/whereami/deploy-network-policy.md index 59877a19..575b4e86 100644 --- a/content/whereami/deploy-network-policy.md +++ b/content/whereami/deploy-network-policy.md @@ -1,6 +1,6 @@ --- title: "Deploy NetworkPolicy" -weight: 6 +weight: 7 description: "Duration: 5 min | Persona: Apps Operator" tags: ["apps-operator", "security-tips"] --- diff --git a/content/whereami/deploy-sidecar.md b/content/whereami/deploy-sidecar.md index fd578d58..6cb698c7 100644 --- a/content/whereami/deploy-sidecar.md +++ b/content/whereami/deploy-sidecar.md @@ -1,6 +1,6 @@ --- title: "Deploy Sidecar" -weight: 5 +weight: 6 description: "Duration: 5 min | Persona: Apps Operator" tags: ["apps-operator", "asm"] --- diff --git a/content/whereami/prepare-container.md b/content/whereami/prepare-container.md index 307525d4..1ea34914 100644 --- a/content/whereami/prepare-container.md +++ b/content/whereami/prepare-container.md @@ -1,6 +1,6 @@ --- title: "Prepare container" -weight: 3 +weight: 4 description: "Duration: 5 min | Persona: Apps Operator" tags: ["apps-operator", "security-tips", "shift-left"] --- @@ -19,29 +19,76 @@ echo "export PRIVATE_WHEREAMI_IMAGE_NAME=${PRIVATE_WHEREAMI_IMAGE_NAME}" >> ${WO source ${WORK_DIR}acm-workshop-variables.sh ``` -Copy the public image to your private registry: +Create the GitHub actions definition: ```Bash -UPSTREAM_WHEREAMI_IMAGE_NAME=us-docker.pkg.dev/google-samples/containers/gke/whereami:$WHEREAMI_VERSION -docker pull $UPSTREAM_WHEREAMI_IMAGE_NAME -docker tag $UPSTREAM_WHEREAMI_IMAGE_NAME $PRIVATE_WHEREAMI_IMAGE_NAME -docker push $PRIVATE_WHEREAMI_IMAGE_NAME +cd ${WORK_DIR}$WHERE_AMI_DIR_NAME +cat < ${WORK_DIR}$WHERE_AMI_DIR_NAME/.github/workflows/copy-container.yaml +name: copy-container +permissions: + id-token: write + contents: read +env: + SEVERITY: CRITICAL + WHEREAMI_APP_NAME: whereami + WHEREAMI_VERSION: 1.2.10 + UPSTREAM_WHEREAMI_IMAGE_NAME: us-docker.pkg.dev/google-samples/containers/gke/whereami +jobs: + job: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3.0.2 + with: + submodules: true + - uses: google-github-actions/auth@v0.8.1 + with: + workload_identity_provider: '${{ secrets.WORKLOAD_IDENTITY_POOL_PROVIDER }}' + service_account: '${{ secrets.CONTAINER_IMAGE_BUILDER_SERVICE_ACCOUNT_ID }}' + token_format: 'access_token' + - uses: google-github-actions/setup-gcloud@v0.6.0 + with: + version: latest + - name: copy the container + run: | + docker pull ${UPSTREAM_WHEREAMI_IMAGE_NAME}:${WHEREAMI_VERSION} + imageName=${{ secrets.CONTAINER_REGISTRY_HOST_NAME }}/${{ secrets.CONTAINER_REGISTRY_PROJECT_ID }}/${{ secrets.CONTAINER_REGISTRY_NAME }}/${WHEREAMI_APP_NAME} + echo "PRIVATE_WHEREAMI_IMAGE_NAME=$imageName" >> $GITHUB_ENV + docker tag ${UPSTREAM_WHEREAMI_IMAGE_NAME}:${WHEREAMI_VERSION} ${PRIVATE_WHEREAMI_IMAGE_NAME}:${WHEREAMI_VERSION} + docker push ${PRIVATE_WHEREAMI_IMAGE_NAME}:${WHEREAMI_VERSION} + - name: scan the container + run: | + gcloud components install local-extract --quiet + gcloud artifacts docker images scan ${PRIVATE_WHEREAMI_IMAGE_NAME}:${WHEREAMI_VERSION} --format='value(response.scan)' > scan_id.txt + gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) --format='table(vulnerability.effectiveSeverity, vulnerability.cvssScore, noteName, vulnerability.packageIssue[0].affectedPackage, vulnerability.packageIssue[0].affectedVersion.name, vulnerability.packageIssue[0].fixedVersion.name)' + gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) --format='value(vulnerability.effectiveSeverity)' | if grep -Fxq ${{ env.SEVERITY }}; then echo 'Failed vulnerability check' && exit 1; else exit 0; fi +EOF ``` +{{% notice tip %}} +You could see that we scan the container image as part of the Continuous Integration pipeline and generate an error if any `Critical` vulnerabilities is found. +{{% /notice %}} -List the container images in your private registry: +Set GitHub actions secrets: ```Bash -gcloud artifacts docker images list $CONTAINER_REGISTRY_REPOSITORY \ - --include-tags +gh secret set CONTAINER_REGISTRY_PROJECT_ID -b"${projectId}" +gh secret set CONTAINER_REGISTRY_NAME -b"${artifactRegistryName}" +gh secret set CONTAINER_REGISTRY_HOST_NAME -b"${artifactRegistryLocation}-docker.pkg.dev" +gh secret set CONTAINER_IMAGE_BUILDER_SERVICE_ACCOUNT_ID -b"${saId}" +gh secret set WORKLOAD_IDENTITY_POOL_PROVIDER -b"${providerId}" ``` -[Scan the `whereami` container image](https://cloud.google.com/container-analysis/docs/on-demand-scanning-howto): +Commit the file: ```Bash -gcloud artifacts docker images scan $PRIVATE_WHEREAMI_IMAGE_NAME \ - --project ${TENANT_PROJECT_ID} \ - --format='value(response.scan)' > ${WORK_DIR}scan_id.txt -gcloud artifacts docker images list-vulnerabilities $(cat ${WORK_DIR}scan_id.txt) \ - --project ${TENANT_PROJECT_ID} \ - --format='table(vulnerability.effectiveSeverity, vulnerability.cvssScore, noteName, vulnerability.packageIssue[0].affectedPackage, vulnerability.packageIssue[0].affectedVersion.name, vulnerability.packageIssue[0].fixedVersion.name)' + ``` -{{% notice tip %}} -You could use this `gcloud artifacts docker images scan` command in your Continuous Integration system in order to detect as early as possible for example `Critical` or `High` vulnerabilities. -{{% /notice %}} \ No newline at end of file + +Manually trigger the GitHub actions run: +```Bash + +``` + + + +List the container images in your private registry: +```Bash +gcloud artifacts docker images list $CONTAINER_REGISTRY_REPOSITORY \ + --include-tags +``` \ No newline at end of file diff --git a/content/whereami/set-up-whereami-git-repo.md b/content/whereami/set-up-whereami-git-repo.md index 095194ed..8af48e8c 100644 --- a/content/whereami/set-up-whereami-git-repo.md +++ b/content/whereami/set-up-whereami-git-repo.md @@ -49,6 +49,9 @@ cd ${WORK_DIR}$WHERE_AMI_DIR_NAME git pull git checkout main WHERE_AMI_REPO_URL=$(gh repo view --json url --jq .url) +WHERE_AMI_REPO_NAME_WITH_OWNER=$(gh repo view --json nameWithOwner --jq .nameWithOwner) +echo "export WHERE_AMI_REPO_NAME_WITH_OWNER=${WHERE_AMI_REPO_NAME_WITH_OWNER}" >> ${WORK_DIR}acm-workshop-variables.sh +source ${WORK_DIR}acm-workshop-variables.sh ``` ## Define RepoSync diff --git a/content/whereami/set-up-workload-identity-federation.md b/content/whereami/set-up-workload-identity-federation.md new file mode 100644 index 00000000..9f831894 --- /dev/null +++ b/content/whereami/set-up-workload-identity-federation.md @@ -0,0 +1,69 @@ +--- +title: "Set up Workload Identity Federation" +weight: 3 +description: "Duration: 3 min | Persona: Platform Admin" +tags: ["kcc", "platform-admin", "security-tips", "wif"] +--- +![Platform Admin](/images/platform-admin.png) +_{{< param description >}}_ + +In this section, you will set up Workload Identity Federation for the Whereami GitHub repository. + +Define variables: +```Bash +WORK_DIR=~/ +source ${WORK_DIR}acm-workshop-variables.sh +``` + +## Configure the Workload Identity Federation + +Define the [Workload Identity Pool](https://cloud.google.com/config-connector/docs/reference/resource-docs/iam/iamworkloadidentitypool): +```Bash +cat < ${WORK_DIR}$HOST_PROJECT_DIR_NAME/projects/$TENANT_PROJECT_ID/containers-builder-workload-identity-user.yaml +apiVersion: iam.cnrm.cloud.google.com/v1beta1 +kind: IAMPartialPolicy +metadata: + name: containers-builder-sa-wi-user + namespace: ${TENANT_PROJECT_ID} + annotations: + config.kubernetes.io/depends-on: iam.cnrm.cloud.google.com/namespaces/${TENANT_PROJECT_ID}/IAMServiceAccount/${CONTAINERS_BUILDER_SERVICE_ACCOUNT_NAME} +spec: + resourceRef: + name: ${CONTAINERS_BUILDER_SERVICE_ACCOUNT_NAME} + kind: IAMServiceAccount + bindings: + - role: roles/iam.workloadIdentityUser + members: + - member: principalSet://iam.googleapis.com/projects/${TENANT_PROJECT_NUMBER}/locations/global/workloadIdentityPools/${WORKLOAD_IDENTITY_POOL_NAME}/attribute.repository/${WHERE_AMI_REPO_NAME_WITH_OWNER} +EOF +``` + +## Deploy Kubernetes manifests + +```Bash +cd ${WORK_DIR}$HOST_PROJECT_DIR_NAME/ +git add . && git commit -m "Setting up Workload Identity Federation for Whereami repository" && git push origin main +``` + +## Check deployments + +{{< mermaid >}} +graph TD; + IAMPartialPolicy-.->IAMServiceAccount + IAMPartialPolicy-.->Project +{{< /mermaid >}} + +List the Kubernetes resources managed by Config Sync in **Config Controller** for the **Host project configs** repository: +```Bash +gcloud alpha anthos config sync repo describe \ + --project $HOST_PROJECT_ID \ + --managed-resources all \ + --sync-name root-sync \ + --sync-namespace config-management-system +``` +Wait and re-run this command above until you see `"status": "SYNCED"` for this `RootSync`. All the `managed_resources` listed should have `STATUS: Current` as well. + +List the GitHub runs for the **Host project configs** repository: +```Bash +cd ${WORK_DIR}$HOST_PROJECT_DIR_NAME && gh run list +``` \ No newline at end of file