{"id":47880314,"url":"https://github.com/getditto/gcp-workload-identity-federation-webhook","last_synced_at":"2026-04-04T01:44:32.050Z","repository":{"id":300878563,"uuid":"1004679816","full_name":"getditto/gcp-workload-identity-federation-webhook","owner":"getditto","description":"This webhook is for mutating pods that will require GCP Workload Identity Federation access from Kubernetes Cluster.","archived":false,"fork":false,"pushed_at":"2026-03-23T20:51:53.000Z","size":238,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-04T01:44:31.273Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/getditto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-19T02:41:02.000Z","updated_at":"2026-03-23T20:37:47.000Z","dependencies_parsed_at":"2025-06-24T04:28:48.965Z","dependency_job_id":"9a506c4d-b919-48b7-9a46-cb159e2cb30d","html_url":"https://github.com/getditto/gcp-workload-identity-federation-webhook","commit_stats":null,"previous_names":["getditto/gcp-workload-identity-federation-webhook"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/getditto/gcp-workload-identity-federation-webhook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getditto%2Fgcp-workload-identity-federation-webhook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getditto%2Fgcp-workload-identity-federation-webhook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getditto%2Fgcp-workload-identity-federation-webhook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getditto%2Fgcp-workload-identity-federation-webhook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/getditto","download_url":"https://codeload.github.com/getditto/gcp-workload-identity-federation-webhook/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getditto%2Fgcp-workload-identity-federation-webhook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31384845,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T01:22:39.193Z","status":"ssl_error","status_checked_at":"2026-04-04T01:22:33.970Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T01:44:30.653Z","updated_at":"2026-04-04T01:44:32.038Z","avatar_url":"https://github.com/getditto.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GCP Workload Identity Federation Webhook\n\nThis webhook is for mutating pods that will require GCP Workload Identity Federation access from Kubernetes Cluster.\n\nNote: GKE or Anthos natively support injecting workload identity for pods.  This webhook is useful mainly for Kubernetes clusters running in other cloud providers or on-premise.\n\n## Prerequisites\n\n1. Kubernetes cluster v1.21 or later.\n\n2. Configure kube-apiserver with `--service-account-issuer` and `--service-account-jwks-uri` properly.\n\n3. Expose [Your Kubernetes Cluster's ServiceAccount issuer discovery endpoint (OIDC discovery endpoint)](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-issuer-discovery) to the public so that it can reach by the url set in `--service-account-issuer`\n  - _Hint: you can use S3/GCS to expose the endpoint to the public._\n  - __WARNING: If your public JWKS(JSON Web Key Set) endpoint are compromised by the malicious attacker, the attacker can hijack your JWKS endpoint (i.e., issue OIDC ID Tokens) that can impersonate any roles for GCP service accounts that are configured to trust to the issuer. Thus, JWKS endpoint must be secured all the time.__\n\n## Walk Through\n\n1. [Create an external identity pool and provider][wif] with your OIDC issuer and allowed audience (default: `sts.googleapis.com`) in IAM for your project. It will need to configure attribute mappings and conditions from OIDC ID Tokens to identities in the pool.\n\n2. [Granting external identities permission to impersonate a service account][grant-sa] so that a Kubernetes `ServiceAccount` can work as a _federated_ workload identity (i.e., it can impersonate a GCP service account).\n\n3. Annotate a Kubernetes `ServiceAccount` with the identity provider and GCP service account that it will impersonate.\n\n    ```yaml\n    apiVersion: v1\n    kind: ServiceAccount\n    metadata:\n      name: app-x\n      namespace: service-a\n      annotations:\n        # assume\n        #   you grant k8s service account \"service-a/app-x\" to impersonate \"app-x\" GCP service account\n        #   this k8s cluster's service account belongs the annotated workload identity provider here\n        cloud.google.com/workload-identity-provider: \"projects/12345/locations/global/workloadIdentityPools/on-prem-kubernetes/providers/this-cluster\"\n        cloud.google.com/service-account-email: \"app-x@project.iam.googleapis.com\"\n\n        # optional: Defaults to \"sts.googleapis.com\" if not set\n        #   this value must be allowed in the annotated workload identity provider above\n        cloud.google.com/audience: \"sts.googleapis.com\"\n\n        # optional: Defaults to 86400 for expirationSeconds if not set\n        #   Note: This value can be overwritten if specified in the pod\n        #         annotation as shown in the next step.\n        cloud.google.com/token-expiration: \"86400\"\n\n        # optional: This value defines the container security context with runAsUser\n        #           with the defined user. This could avoid problems related with root requirement from gcloud image\n        cloud.google.com/gcloud-run-as-user: \"1000\"\n\n        # optional: gcloud external configuration injection mode.\n        #           The value must be one of 'gcloud'(default) or 'direct'.\n        #           Refer to the next section for 'direct' injection mode\n        cloud.google.com/injection-mode: \"gcloud\"\n\n        # optional: Defaults to value inside `service-account-email`\n        #           \n        cloud.google.com/project: \"12345\"\n    ```\n\n4. All new pods launched using the Kubernetes `ServiceAccount` will be mutated so that they can impersonate the GCP service account. Below is an example pod spec with the environment variables and volume fields mutated by the webhook.\n\n    ```yaml\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: app-x-pod\n      namespace: service-a\n      annotations:\n        # optional: A comma-separated list of initContainers and container names\n        #   to skip adding volumeMounts and environment variables\n        cloud.google.com/skip-containers: \"init-first,sidecar\"\n        # optional: Defaults to 86400, or value specified in ServiceAccount\n        #   annotation as shown in previous step, for expirationSeconds if not set\n        cloud.google.com/token-expiration: \"86400\"\n    spec:\n      serviceAccountName: app-x\n      initContainers:\n        ### gcloud-setup init container is injected by the webhook ###\n      - name: gcloud-setup\n        image: gcr.io/google.com/cloudsdktool/google-cloud-cli:stable\n        command:\n        - sh\n        - -c\n        - |\n          gcloud iam workload-identity-pools create-cred-config \\\n              $(GCP_WORKLOAD_IDENTITY_PROVIDER) \\\n              --service-account=$(GCP_SERVICE_ACCOUNT) \\\n              --output-file=$(CLOUDSDK_CONFIG)/federation.json \\\n              --credential-source-file=/var/run/secrets/sts.googleapis.com/serviceaccount/token\n              gcloud auth login --cred-file=$(CLOUDSDK_CONFIG)/federation.json\n        env:\n        - name: GCP_WORKLOAD_IDENTITY_PROVIDER\n          value: \"projects/12345/locations/global/workloadIdentityPools/on-prem-kubernetes/providers/this-cluster\"\n        - name: GCP_SERVICE_ACCOUNT\n          value: app-x@project.iam.gserviceaccount.com\n        - name: CLOUDSDK_CONFIG\n          value: /var/run/secrets/gcloud/config\n        volumeMounts:\n        - name: gcp-iam-token\n          readOnly: true\n          mountPath: /var/run/secrets/sts.googleapis.com/serviceaccount\n        - name: gcloud-config\n          mountPath: /var/run/secrets/gcloud/config\n      - name: init-first\n        image: container-image:version\n      containers:\n      - name: sidecar\n        image: container-image:version\n      - name: container-name\n        image: container-image:version\n        ### Everything below is added by the webhook ###\n        env:\n        - name: GOOGLE_APPLICATION_CREDENTIALS\n          value: /var/run/secrets/gcloud/config/federation.json\n        - name: CLOUDSDK_CONFIG\n          value: /var/run/secrets/gcloud/config\n        - name: CLOUDSDK_COMPUTE_REGION\n          value: asia-northeast1\n        volumeMounts:\n        - name: gcp-iam-token\n          readOnly: true\n          mountPath: /var/run/secrets/sts.googleapis.com/serviceaccount\n        - name: gcloud-config\n          mountPath: /var/run/secrets/gcloud/config\n      volumes:\n      - name: gcp-iam-token\n        projected:\n          sources:\n          - serviceAccountToken:\n              audience: sts.googleapis.com\n              expirationSeconds: 86400\n              path: token\n      - name: gcloud-config\n        emptyDir: {}\n    ```\n\n[wif]: https://cloud.google.com/iam/docs/configuring-workload-identity-federation#oidc\n[grant-sa]: https://cloud.google.com/iam/docs/using-workload-identity-federation#impersonate\n\n### Usage with non-root container user\n\nWhen running a container with a non-root user, you need to give user id for GCloud SDK container using the annotation `cloud.google.com/gcloud-run-as-user` in the service account.\n\n## Experimental Direct Credential Injection Mode\n\nIn this mode, the Workload Identity Federation Webhook controller directly generates the Gcloud external credentials configuration and injects into the pod.\nThis means the `gcloud-setup` init container is not required which can speed up pod start time.\n\nTo use direct injection mode:\n\n1. Annotate a Kubernetes `ServiceAccount` with the `injection-mode` value of `direct`.\n\n    ```yaml\n    apiVersion: v1\n    kind: ServiceAccount\n    metadata:\n      name: app-x\n      namespace: service-a\n      annotations:\n\n        # Set the injection mode to 'direct', instead of 'gcloud'.\n        cloud.google.com/injection-mode: \"direct\"\n    ```\n\n2. Below is an example pod spec with the environment variables and volume fields mutated by the webhook. Notice there is no `gcloud-setup` init container or Volumes, instead there is an extra annotation and `external-credential-config` volume and volumeMount.\n\n    ```yaml\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: app-x-pod\n      namespace: service-a\n      annotations:\n        # optional: A comma-separated list of initContainers and container names\n        #   to skip adding volumeMounts and environment variables\n        cloud.google.com/skip-containers: \"init-first,sidecar\"\n        #\n        # The Generated External Credentials Json is added as an annotation, and mounted into the container filesystem via the DownwardAPI Volume\n        #\n        cloud.google.com/external-credentials-json: |-\n          {\n            \"type\": \"external_account\",\n            \"audience\": \"//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/on-prem-kubernetes/providers/this-cluster\",\n            \"subject_token_type\": \"urn:ietf:params:oauth:token-type:jwt\",\n            \"token_url\": \"https://sts.googleapis.com/v1/token\",\n            \"service_account_impersonation_url\": \"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/app-x@project.iam.gserviceaccount.com:generateAccessToken\",\n            \"credential_source\": {\n              \"file\": \"/var/run/secrets/sts.googleapis.com/serviceaccount/token\",\n              \"format\": {\n                \"type\": \"text\"\n              }\n            }\n          }\n    spec:\n      serviceAccountName: app-x\n      initContainers:\n      - name: init-first\n        image: container-image:version\n      containers:\n      - name: sidecar\n        image: container-image:version\n      - name: container-name\n        image: container-image:version\n        ### Everything below is added by the webhook ###\n        env:\n        - name: GOOGLE_APPLICATION_CREDENTIALS\n          value: /var/run/secrets/gcloud/config/federation.json\n        - name: CLOUDSDK_COMPUTE_REGION\n          value: asia-northeast1\n        volumeMounts:\n        - name: gcp-iam-token\n          readOnly: true\n          mountPath: /var/run/secrets/sts.googleapis.com/serviceaccount\n        - mountPath: /var/run/secrets/gcloud/config\n          name: external-credential-config\n          readOnly: true\n      volumes:\n      - name: gcp-iam-token\n        projected:\n          sources:\n          - serviceAccountToken:\n              audience: sts.googleapis.com\n              expirationSeconds: 86400\n              path: token\n      - downwardAPI:\n          defaultMode: 288\n          items:\n          - fieldRef:\n              apiVersion: v1\n              fieldPath: metadata.annotations['cloud.google.com/external-credentials-json']\n            path: federation.json\n        name: external-credential-config\n    ```\n\n## Usage\n\n```console\nUsage of /gcp-workload-identity-federation-webhook:\n  -annotation-prefix string\n        The Service Account annotation to look for (default \"cloud.google.com\")\n  -gcloud-image string\n        Container image for the init container setting up GCloud SDK (default \"gcr.io/google.com/cloudsdktool/google-cloud-cli:stable\")\n  -gcp-default-region string\n        If set, CLOUDSDK_COMPUTE_REGION will be set to this value in mutated containers\n  -health-probe-bind-address string\n        The address the probe endpoint binds to. (default \":8081\")\n  -kubeconfig string\n        Paths to a kubeconfig. Only required if out-of-cluster.\n  -metrics-bind-address string\n        The address the metric endpoint binds to. (default \":8080\")\n  -setup-container-resources string\n        Resource spec in json for the init container setting up GCloud SDK, e.g. '{\"requests\":{\"cpu\":\"100m\"}}'\n  -token-audience string\n        The default audience for tokens. Can be overridden by annotation (default \"sts.googleapis.com\")\n  -token-expiration duration\n        The token expiration (default 24h0m0s)\n  -token-default-mode int\n        DefaultMode for the token volume (default 0440)\n  -zap-devel\n        Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn). Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error) (default true)\n  -zap-encoder value\n        Zap log encoding (one of 'json' or 'console')\n  -zap-log-level value\n        Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', or any integer value \u003e 0 which corresponds to custom debug levels of increasing verbosity\n  -zap-stacktrace-level value\n        Zap Level at and above which stacktraces are captured (one of 'info', 'error', 'panic').\n  -zap-time-encoding value\n        Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano'). Defaults to 'epoch'.\n```\n\n## Installation\n\n### Pre-requisites\n\n- cert-manager: See [cert-manager installation](https://cert-manager.io/docs/installation/)\n- (optional) prometheus-operator: See https://github.com/prometheus-operator/prometheus-operator\n\n### Deploy\n\n#### Helm chart\n\n```shell\n$ helm repo add gcp-workload-identity-federation-webhook https://pfnet-research.github.io/gcp-workload-identity-federation-webhook\n$ helm repo update\n$ helm install gcp-wif-webhook gcp-workload-identity-federation-webhook/gcp-workload-identity-federation-webhook \\\n    --namespace gcp-wif-webhook-system --create-namespace\n```\n\n#### Kustomize\n\n```shell\nmake deploy\n```\n\nOr, please inspect `config/default` directory.\n\n## Release\n\nThe release process is automated by [tagpr](https://github.com/Songmu/tagpr). To release, just merge [the latest release PR](https://github.com/pfnet-research/gcp-workload-identity-federation-webhook/pulls?q=is:pr+is:open+label:tagpr).\n\n## License\n\nApache 2.0 - Copyright 2022 Preferred Networks, Inc. or its affiliates. All Rights Reserved.\nSee [LICENSE](LICENSE)\n\n## Acknowledgment\n\nThis project is greatly inspired by [aws/amazon-eks-pod-identity-webhook](https://github.com/aws/amazon-eks-pod-identity-webhook).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetditto%2Fgcp-workload-identity-federation-webhook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgetditto%2Fgcp-workload-identity-federation-webhook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetditto%2Fgcp-workload-identity-federation-webhook/lists"}