From 0188ac9d4aa8e952060ebc3e485a7c298cc5d4b0 Mon Sep 17 00:00:00 2001
From: Pytgaen <32298455+pytgaen@users.noreply.github.com>
Date: Sat, 23 Mar 2024 18:53:13 +0100
Subject: [PATCH] feat: add GCP auth variant

---
 README.md                          | 39 ++++++++++++++++++++
 kicker.json                        | 24 ++++++++++++-
 templates/gitlab-ci-python-gcp.yml | 58 ++++++++++++++++++++++++++++++
 3 files changed, 120 insertions(+), 1 deletion(-)
 create mode 100644 templates/gitlab-ci-python-gcp.yml

diff --git a/README.md b/README.md
index 18ae827..09216d0 100644
--- a/README.md
+++ b/README.md
@@ -485,3 +485,42 @@ variables:
     PYTHON_REPOSITORY_PASSWORD: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/pip-repo/repository?field=password"
     # $VAULT_ROLE_ID and $VAULT_SECRET_ID defined as a secret CI/CD variable
 ```
+
+### Google Cloud variant
+
+This variant allows to use Python Google Clients. The variant follow the recommendation [Authenticate for using client libraries](https://cloud.google.com/docs/authentication/client-libraries) with [ADC](https://cloud.google.com/docs/authentication/application-default-credentials) 
+
+[Detailed article on internal OIDC impersonated with Workload Identify Federation](https://blog.salrashid.dev/articles/2021/understanding_workload_identity_federation/#oidc-impersonated)
+
+List of requirements before using this variant for use Python Google Clients:
+
+1. You must have a Workload Identity Federation Pool,
+2. You must have a Service Account with enough permissions to run your python job.
+3. Optional, you can define `GOOGLE_CLOUD_PROJECT` in template variable to define the default Google project
+
+#### Configuration
+
+The variant requires the additional configuration parameters:
+
+| Input / Variable | Description                            | Default value     |
+| ----------------- | -------------------------------------- | ----------------- |
+| `gcp-oidc-aud` / `GCP_OIDC_AUD` | The `aud` claim for the JWT token      | `$CI_SERVER_URL` |
+| `gcp-oidc-provider` / `GCP_OIDC_PROVIDER` | Default Workload Identity Provider associated with GitLab to [authenticate with OpenID Connect](https://docs.gitlab.com/ee/ci/cloud_services/google_cloud/) | _none_ |
+| `gcp-oidc-account` / `GCP_OIDC_ACCOUNT` | Default Service Account to which impersonate with OpenID Connect authentication | _none_ |
+
+#### Example
+
+```yaml
+include:
+  - component: gitlab.com/to-be-continuous/python/gitlab-ci-python@6.7.0
+    # 2: set/override component inputs
+    inputs:
+      image: registry.hub.docker.com/library/python:3.10
+      pytest-enabled: true
+
+  - component: gitlab.com/to-be-continuous/python/gitlab-ci-python-gcp@6.7.0
+    inputs:
+      # common OIDC config for non-prod envs
+      gcp-oidc-provider: "projects/<gcp_nonprod_proj_id>/locations/global/workloadIdentityPools/<pool_id>/providers/<provider_id>"
+      gcp-oidc-account: "<name>@$<gcp_nonprod_proj_id>.iam.gserviceaccount.com"
+```
diff --git a/kicker.json b/kicker.json
index 8ec4b45..79860fe 100644
--- a/kicker.json
+++ b/kicker.json
@@ -290,6 +290,28 @@
           "secret": true
         }
       ]
+    },
+    {
+      "id": "gcp-auth-provider",
+      "name": "Google Cloud",
+      "description": "Retrieves an [ADC](https://cloud.google.com/docs/authentication/application-default-credentials) for Python Google Client",
+      "template_path": "templates/gitlab-ci-python-gcp.yml",
+      "variables": [
+        {
+          "name": "GCP_OIDC_AUD",
+          "description": "The `aud` claim for the JWT token _(only required for [OIDC authentication](https://docs.gitlab.com/ee/ci/cloud_services/aws/))_",
+          "default": "$CI_SERVER_URL",
+          "advanced": true
+        },
+        {
+          "name": "GCP_OIDC_ACCOUNT",
+          "description": "Default Service Account to which impersonate with OpenID Connect authentication"
+        },
+        {
+          "name": "GCP_OIDC_PROVIDER",
+          "description": "Default Workload Identity Provider associated with GitLab to [authenticate with OpenID Connect](https://docs.gitlab.com/ee/ci/cloud_services/google_cloud/)"
+        }
+      ]
     }
   ]
-}
+}
\ No newline at end of file
diff --git a/templates/gitlab-ci-python-gcp.yml b/templates/gitlab-ci-python-gcp.yml
new file mode 100644
index 0000000..dcc575b
--- /dev/null
+++ b/templates/gitlab-ci-python-gcp.yml
@@ -0,0 +1,58 @@
+# =====================================================================================================================
+# === GCP Auth template variant (provide ADC authentification)
+# === https://cloud.google.com/docs/authentication/application-default-credentials
+# =====================================================================================================================
+spec:
+  inputs:
+    gcp-oidc-aud:
+      description: The `aud` claim for the JWT token _(only required for [OIDC authentication](https://docs.gitlab.com/ee/ci/cloud_services/google_cloud/)))_
+      default: $CI_SERVER_URL
+    gcp-oidc-account:
+      description: Default Service Account to which impersonate with OpenID Connect authentication
+      default: ''
+    gcp-oidc-provider:
+      description: Default Workload Identity Provider associated with GitLab to [authenticate with OpenID Connect](https://docs.gitlab.com/ee/ci/cloud_services/google_cloud/)
+      default: ''
+---
+variables:
+  GCP_OIDC_AUD: $[[ inputs.gcp-oidc-aud ]]
+  GCP_OIDC_ACCOUNT: $[[ inputs.gcp-oidc-account ]]
+  GCP_OIDC_PROVIDER: $[[ inputs.gcp-oidc-provider ]]
+
+.gcp-provider-auth:
+  before_script:
+    - echo "Installing GCP authentication with env GOOGLE_APPLICATION_CREDENTIALS file"
+    - echo $GCP_JWT > "$CI_BUILDS_DIR/.auth_token.jwt"
+    - |-
+      cat << EOF > "$CI_BUILDS_DIR/google_application_credentials.json"
+      {
+        "type": "external_account",
+        "audience": "//iam.googleapis.com/${GCP_OIDC_PROVIDER}",
+        "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
+        "token_url": "https://sts.googleapis.com/v1/token",
+        "credential_source": {
+          "file": "$CI_BUILDS_DIR/.auth_token.jwt"
+        },
+        "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${GCP_OIDC_ACCOUNT}:generateAccessToken"
+      }
+      EOF
+    - export GOOGLE_APPLICATION_CREDENTIALS="$CI_BUILDS_DIR/google_application_credentials.json"
+
+
+.python-base:
+  image: $PYTHON_IMAGE
+  services:
+    - name: "$TBC_TRACKING_IMAGE"
+      command: ["--service", "python", "6.7.0"]
+  variables:
+    GCP_JWT: $GCP_JWT
+  before_script:
+    - !reference [.gcp-provider-auth, before_script]
+    - !reference [.python-scripts]
+    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
+    - cd ${PYTHON_PROJECT_DIR}
+    - guess_build_system
+
+  id_tokens:
+    GCP_JWT:
+      aud: "$GCP_OIDC_AUD"
-- 
GitLab