Hashicorp Vault’s Identity Secrets Engine acts as an OIDC provider and can provide any Vault user an OpenID token (id_token).
GCP Workload Identity Federation allows you to configure and exchange an external identity sources for one that can be used to access GCP services.
In other words, you should be able to exchange a Vault identity source directly with GCP and access GCP without resorting to using Vault GCP Secrets Engine.
Well, sure, but the implementation details for this makes it impractical and this tutorial pretty academic:
The reason being is that for GCP Workload Federation to use an external source as an OIDC provider, GCP needs a way to access the identity providers oidc .well-known/openid-configuration endpoint.
In otherwords, https://your_vault_server/v1/identity/oidc/.well-known/openid-configuration needs to be publicly accessible by GCP.
Thats a non starter almost for everyone but this is my blog and i can write about what i choose.

So i’m going to make this tutorial even more unrealistic:  we’re going to run vault on your laptop and expose its openID discovery endpoint using ngrok.  I’m only doing this for simplicity…if you actually have vault or the well-known endpoint exposed publicly somehow, you dont’ need to do this silly stuff with ngrok.
For more information, see
This is what you’ll need for this tutorial:
First start ngrok and get the allocated endpoint.
This endpoint is dynamic and you have 1hour to do this tutorial. (pressure..no worries, i did the steps here)
./ngrok http   localhost:8200
# note down the https discovery endpoint and export it
export DISCOVERY_URL="https://random_id.ngrok.io"
Start Vault in dev mode
vault server --dev -dev-root-token-id root
## please be aware your vault server is now *visible* to the internet as $DISCOVERY_URL
# in a new window, attach to the root token and configure
export VAULT_ADDR='http://localhost:8200'
export VAULT_TOKEN=root
## Configure the OIDC issuer
vault write identity/oidc/config  issuer=$DISCOVERY_URL
vault read identity/oidc/config 
## check if the Discovery Endpoint works externally
curl -s $DISCOVERY_URL/v1/identity/oidc/.well-known/openid-configuration | jq '.'
curl -s $DISCOVERY_URL/v1/identity/oidc/.well-known/keys | jq '.'
# enable the username password authentication for vault
# you can use any auth but for this tutorial, w'ere just going to use something basic
vault auth enable userpass
# create a policy to read a token in a role
vault policy write config-policy  - <<EOF
path "identity/oidc/token/role-001" {
  capabilities = [ "create", "read" ]
}
path "/auth/token/create" {
  capabilities = ["create"]
}
path "auth/token/renew" {
  capabilities = ["update", "create"]
}
path "auth/token/lookup-self" {
  capabilities = ["read"]
}
path "auth/token/renew" {
  capabilities = ["update", "create"]
}
path "auth/token/lookup-accessor" {
  capabilities = [ "read", "update" ]
}
EOF
## create a user with a given password and allow alice to get
## a token for the "config-policy" we just defined
vault write auth/userpass/users/alice \
    password="password" \
    token_ttl="1h" \
    policies=config-policy
## create an identity entity (this is a vault artifact that allows identity token mappings)
vault write identity/entity \
    name="alice" \
    metadata="email=alice@domain.com" \
    metadata="phone_number=123-456-7890" \
    disabled=false
export ENTITY_ID=$(vault read -field=id identity/entity/name/alice)
## also create an "engineering" group 
vault write identity/group \
    name="engineering" \
    member_entity_ids="$ENTITY_ID"
export GROUP_ID=$(vault read -field=id identity/group/name/engineering)
export USERPASS_ACCESSOR=$(vault auth list -detailed -format json | jq -r '.["userpass/"].accessor')
echo $ENTITY_ID
echo $GROUP_ID
echo $USERPASS_ACCESSOR
# now alias the identity (i.,e associate alice with the stuff we just defined)
vault write identity/entity-alias \
    name="alice" \
    canonical_id="$ENTITY_ID" \
    mount_accessor="$USERPASS_ACCESSOR"
# create a key 
vault write identity/oidc/key/my-key \
    allowed_client_ids="*" \
    verification_ttl="2h" \
    rotation_period="1h" \
    algorithm="RS256"
# define the role alice can use and the template for that role's id_tokens
## the id_tokens will contain the claims below
export USER_TEMPLATE=$(cat << EOF
{
    "username": {{identity.entity.name}},
    "contact": {
        "email": {{identity.entity.metadata.email}},
        "phone_number": {{identity.entity.metadata.phone_number}}
    },
    "groups": {{identity.entity.groups.names}}
}
EOF
)
vault write identity/oidc/role/role-001 \
    description="The testrole" \
    key=my-key \
    ttl=1h \
    template="$(echo ${USER_TEMPLATE} | base64 -)"
vault read identity/oidc/role/role-001
export CLIENT_ID=`vault read -field client_id identity/oidc/role/role-001`
The role output will contain something like this:
vault read identity/oidc/role/role-001
Key          Value
---          -----
client_id    PvnKrIhX5lAZ2gBhjnZhhbf3ez
key          my-key
template     { "username": {{identity.entity.name}}, "contact": { "email": {{identity.entity.metadata.email}}, "phone_number": {{identity.entity.metadata.phone_number}} }, "groups": {{identity.entity.groups.names}} }
ttl          1h
the id_token that vault will issue will have the audience aud: value set to PvnKrIhX5lAZ2gBhjnZhhbf3ez  (yours will be different)
Now in a new window, login to vault as alice
export VAULT_ADDR='http://localhost:8200'
vault login -method=userpass \
    username=alice \
    password=password 
# read back the id_token
vault read identity/oidc/token/role-001  
The output will return the user’s id_token
Key          Value
---          -----
client_id    PvnKrIhX5lAZ2gBhjnZhhbf3ez
token        eyJhbGciOiJSUzI1NiIsImtpZCI6ImYxMWM1MDJmLWFmYzUtZTc0ZS0wYzAyLWNhNzdmNzA2MTFjMSJ9.eyJhdWQiOiJQdm5LckloWDVsQVoyZ0Joam5aaGhiZjNleiIsImNvbnRhY3QiOnsiZW1haWwiOiJhbGljZUBkb21haW4uY29tIiwicGhvbmVfbnVtYmVyIjoiMTIzLTQ1Ni03ODkwIn0sImV4cCI6MTY0OTcwMzU3NywiZ3JvdXBzIjpbImVuZ2luZWVyaW5nIl0sImlhdCI6MTY0OTY5OTk3NywiaXNzIjoiaHR0cHM6Ly9iMDY0LTcyLTgzLTY3LTE3NC5uZ3Jvay5pby92MS9pZGVudGl0eS9vaWRjIiwibmFtZXNwYWNlIjoicm9vdCIsInN1YiI6IjBjYThlZTFmLTUyNTYtYTQ5OC1jMTdlLTNmOGM2YTI0YTBmNCIsInVzZXJuYW1lIjoiYWxpY2UifQ.HvgHnYaHfsCU7O9nnRxz3V_gUS2MZi2bO_od8dgkZ1r_7Uarx7iiVRLMUyPb-DIFfZxISGHWajDA3uWLXSycsQvy5-amYszxTzZeKNF8h-YOpIidybnTYIgHyYgNQ33NBFvivpXSy90Q1SqHZxW6NhrHk4jKyQJBb-v8CM8j2rTAsimy1PLK5bnNlKJRposYGRXI-K7EIPxvRVP2uwThSs4HOD5MQGHYi3SWlpd7IDhZ54q3uj60ZrF6nWtZvH8WS-l-fjflC1l9PYfnNuJKWX8OIb85HJs476YOG5gt_Zye_CFZOjfNBc-F_xZlIPVElzgWMxCd-KpQIgFgfJdBvw
ttl          1h
The oidc token is in this format.  Note the aud is the client_id and the sub uniquely represents alice  : 0ca8ee1f-5256-a498-c17e-3f8c6a24a0f4.
$ vault token lookup
Key                 Value
---                 -----
accessor            G7i0sBdvM9fAaPTscg6XJIxk
creation_time       1649699950
creation_ttl        1h
display_name        userpass-alice
entity_id           0ca8ee1f-5256-a498-c17e-3f8c6a24a0f4  <<<<<<<<<
expire_time         2022-04-11T14:59:10.264446178-04:00
explicit_max_ttl    0s
id                  s.rqgInsjuooeCo5j0lSohl3uG
issue_time          2022-04-11T13:59:10.264450729-04:00
meta                map[username:alice]
num_uses            0
orphan              true
path                auth/userpass/login/alice
policies            [config-policy default]
renewable           true
ttl                 55m22s
type                service
We’re going to directly bind alice’s sub filed here (its very specific binding)
{
  "alg": "RS256",
  "kid": "f11c502f-afc5-e74e-0c02-ca77f70611c1"
}
{
  "aud": "PvnKrIhX5lAZ2gBhjnZhhbf3ez",
  "contact": {
    "email": "alice@domain.com",
    "phone_number": "123-456-7890"
  },
  "exp": 1649703577,
  "groups": [
    "engineering"
  ],
  "iat": 1649699977,
  "iss": "https://b064-72-83-67-174.ngrok.io/v1/identity/oidc",
  "namespace": "root",
  "sub": "0ca8ee1f-5256-a498-c17e-3f8c6a24a0f4",
  "username": "alice"
}
Now w’ere going to configure workload identity.
Back on the window where you are logged in as admin (not alice), configure a federation config just for alice
export ALICE="0ca8ee1f-5256-a498-c17e-3f8c6a24a0f4"
export CLIENT_ID=`vault read -field client_id identity/oidc/role/role-001`
export PROJECT_ID=`gcloud config get-value core/project`
export PROJECT_NUMBER=`gcloud projects describe $PROJECT_ID --format='value(projectNumber)'`
gcloud beta iam workload-identity-pools create pool-vault \
    --location="global" \
    --description="Vault OIDC Pool" \
    --display-name="Vault OIDC Pool" --project $PROJECT_ID
gcloud beta iam workload-identity-pools providers create-oidc oidc-provider-vault-1 \
    --workload-identity-pool=pool-vault \
    --issuer-uri="$DISCOVERY_URL/v1/identity/oidc" \
    --location="global" \
    --attribute-mapping="google.subject=assertion.sub,attribute.aud=assertion.aud[0]" \
    --allowed-audiences="$CLIENT_ID" \
    --project $PROJECT_ID    

Now allow the the federated token for alice access to a GCS bucket
# create a test bucket with uniform bucket policy
##  i can't get the single command with the -b on working so omit that and use the cloud console..
gsutil mb -b on gs://$PROJECT_ID-vault-federated
echo -n "foobar" > /tmp/foo.txt
gsutil cp /tmp/foo.txt gs://$PROJECT_ID-vault-federated
gsutil iam ch \
    principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/pool-vault/subject/$ALICE:objectViewer \
    gs://$PROJECT_ID-vault-federated

Please note that we are using the federated token without impersonation
export OIDC_TOKEN=`vault read -field token identity/oidc/token/role-001`
# get the token manually
export FEDERATED_TOKEN=`curl -s -X POST    -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange"  \
   -d "audience=//iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/pool-vault/providers/oidc-provider-vault-1" \
   -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \
   -d "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
   -d "scope=https://www.googleapis.com/auth/cloud-platform" \
   -d "subject_token=$OIDC_TOKEN"     https://sts.googleapis.com/v1beta/token | jq  -r '.access_token'`
echo $FEDERATED_TOKEN
Now w’ere finally going to access the gcs object
curl    -H "Authorization: Bearer $FEDERATED_TOKEN"  https://storage.googleapis.com/storage/v1/b/$PROJECT_ID-vault-federated/o/foo.txt?alt=media
foobar
done…we’ve just used vault’s own provider to access a storage bucket without vault directly issuing a GCP access_token
and…as for ngrok, i had enough time to spare

This site supports webmentions. Send me a mention via this form.