HashiCorp Vault is a popular open source tool for secrets management that codifies many of the best practices around secrets management, such as time-based access control, encryption, dynamic credentials and much more. Authentication methods are a key feature of Vault, allowing for a variety of ways to authenticate to Vault and obtain a set of secrets under Vault's fine-grained access control. This codelab teaches you how to use Vault's Google Cloud authentication method to authorize a Google Cloud IAM service account to obtain a specific set of secrets from Vault.

What you'll learn

Self-paced environment setup

If you don't already have a Google Account (Gmail or Google Apps), you must create one. Sign-in to Google Cloud Platform console (console.cloud.google.com) and create a new project:

Remember the project ID, a unique name across all Google Cloud projects (the name above has already been taken and will not work for you, sorry!). It will be referred to later in this codelab as PROJECT_ID.

Next, you'll need to enable billing in the Cloud Console in order to use Google Cloud resources.

Running through this codelab shouldn't cost you more than a few dollars, but it could be more if you decide to use more resources or if you leave them running (see "cleanup" section at the end of this document).

New users of Google Cloud Platform are eligible for a $300 free trial.

Start Cloud Shell

While Google Cloud can be operated remotely from your laptop, in this codelab you will be using Google Cloud Shell, a command line environment running in the Cloud.

From the GCP Console click the Cloud Shell icon on the top right toolbar:

It should only take a few moments to provision and connect to the environment. When it is finished, you should see something like this:

This virtual machine is loaded with all the development tools you'll need. It offers a persistent 5GB home directory, and runs on the Google Cloud, greatly enhancing network performance and authentication. All of your work in this lab can be done with simply a browser.

Before deploying Vault in production, first install Vault locally. This will enable you to use the vault CLI locally and will be used to interact with the cluster later.

You could browse to the Vault website, but this section will teach you how to download, verify, and install Vault securely. Even though Vault is downloaded over a TLS connection, it may still be possible for a skilled attacker to compromise the underlying storage system or network transport. For that reason, in addition to serving the binaries over TLS, HashiCorp also signs the checksums of each release with their private key. Thus, to verify the integrity of a download, we must:

  1. Import and trust HashiCorp's GPG public key
  2. Download the Vault binary
  3. Download the Vault checksums
  4. Download the Vault checksum signature
  5. Verify the signature of the checksum against HashiCorp's GPG key
  6. Verify the checksums of the binary against the file

This way, even if an attacker were able to compromise the network transport and underlying storage component, they wouldn't be able to sign the checksums with HashiCorp's GPG key. If this operation is successful, we have an extremely high degree of confidence that the software is untainted.

Since that process can be tedious, we will leverage a Docker container to do it for us. Execute the following command to install Vault locally. We install Vault into $HOME/bin because that will persist between restarts on Cloud Shell.

$ docker run -v $HOME/bin:/software sethvargo/hashicorp-installer vault 1.0.0-beta1
$ sudo chown -R $(whoami):$(whoami) $HOME/bin/

Add the bin to our path:

$ export PATH=$HOME/bin:$PATH

Finally, optionally, explore the Vault CLI help. Most Vault commands will not work because there is no Vault server running. Do not start a Vault server yet.

$ vault -h

Start a local, development Vault server. This Vault server runs entirely in memory and does not represent a best practices installation. However, it is useful for getting started quickly and exploring Vault's functionality. We will also create an initial token in Vault with the value of "root", which will be used to authenticate to the Vault server.

$ export VAULT_ADDR=http://127.0.0.1:8200
$ export VAULT_DEV_ROOT_TOKEN_ID=root
$ vault server -dev &> vault.log &

Vault is now running in the background. You can query its status to verify:

$ vault status

Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
# ...

One of the easiest uses for Vault is simple, encrypted storage of key-value secrets. Store two secrets in Vault. Pretend these are sensitive keys, where one value is only used for testing and one is to be used (and protected) in production.

$ vault kv put secret/test/mysecret key=notasecret
$ vault kv put secret/prod/mysecret key=arealsecret

Next, create a Vault policy. Policies in Vault are a declarative way of allowing or denying access to specific secrets or operations in Vault. This policy will only allow access to secrets under secret/test.

$ vault write sys/policy/test policy=-<<EOF 
path "secret/data/test/*" {
  capabilities = ["read"]
}
EOF

Policies are deny by default, so this policy automatically denies access to the prod secret above. Note the path includes data/ as this is where the KV secrets reside.

Make sure the Google Cloud IAM API is enabled. This only needs to be done once per project to make the API accessible.

$ gcloud services enable iam.googleapis.com

This codelab will use two service accounts. In this section, we are creating the service account which Vault will use to communicate with GCP. By default, Vault has no permissions to talk to Google Cloud, so we need to provide it a service account with adequate permissions. You can think of this service account as the "vault admin" service account. The Vault server will use this account to make underlying verification calls to GCP.

Create the vault admin service account and a JSON key file:

$ gcloud iam service-accounts create vaultadmin
$ export VAULTADMIN_SA_EMAIL="vaultadmin@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com"
$ gcloud iam service-accounts keys create vaultadmin-key.json \
    --iam-account=${VAULTADMIN_SA_EMAIL}

Next, grant this account the appropriate permissions so that Vault can verify service accounts. The easiest way to do this is to create a custom role with only the specific permissions the Vault GCP auth method requires:

$ gcloud iam roles create vaultAdmin \
    --permissions="iam.serviceAccounts.get,iam.serviceAccountKeys.get" \
    --project=${GOOGLE_CLOUD_PROJECT}

For simplicity in this codelab, give this role to the Vault admin service account on the entire project. In a larger or more secure setup, you may choose to assign this role at a project or resource level.

$ gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member="serviceAccount:${VAULTADMIN_SA_EMAIL}" \
    --role="projects/${GOOGLE_CLOUD_PROJECT}/roles/vaultAdmin"

In the previous section, we created the admin service account which Vault uses to communicate to Google Cloud. In this section, we will create the user service account. This is a less privileged service account, acting as a user who can only get specific secrets from Vault. This would correspond to a human or machine's service account.

Create a service account and a JSON key for jordan, our fictitious user.

$ gcloud iam service-accounts create jordan
$ export JORDAN_SA_EMAIL="jordan@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com"
$ gcloud iam service-accounts keys create jordan-key.json \
    --iam-account=${JORDAN_SA_EMAIL}

Vault expects this account to give a signed JWT as proof-of-identity. As such, we need to grant this service account the ability to use the GCP IAM API method signJwt on itself.

$ gcloud iam service-accounts add-iam-policy-binding ${JORDAN_SA_EMAIL} \
    --member="serviceAccount:${JORDAN_SA_EMAIL}" \
    --role="roles/iam.serviceAccountTokenCreator"

We can now set up the Vault GCP auth method. Enable the Google Cloud auth method in Vault.

$ vault auth enable gcp

Enable Vault to use the admin service account we created earlier by saving the key in Vault under configuration for this auth method. Vault will store this service account internally.

$ vault write auth/gcp/config credentials=@vaultadmin-key.json

Add a GCP role in Vault that binds allowed service accounts to the declared policies. In this example, we are limiting the service accounts to Jordan's service account we created in the previous step. It is possible to specify multiple service accounts (comma separated) or * to allow all service accounts to authenticate.

$ vault write auth/gcp/role/my-role \
    type="iam" \
    project_id=${GOOGLE_CLOUD_PROJECT} \
    policies="test" \
    bound_service_accounts=${JORDAN_SA_EMAIL}

That's it! Vault is now set up to allow authentication via service account to authenticate and obtain these secrets.

Up until this point, you have acted as the Vault root sysadmin, setting up the Vault with secrets and authentication methods for future users. We will now switch roles and act as a user of Vault.

Authenticate to Vault using Jordan's service account we created in the previous steps.

$ vault login -method=gcp \
    role="my-role" \
    credentials=@jordan-key.json \
    project="${GOOGLE_CLOUD_PROJECT}" \
    service_account="${JORDAN_SA_EMAIL}"

Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                                 Value
---                                 -----
token                               280kcbb95EDlSQjgnruMjCsT
token_accessor                      33wtv0AIPymogWKmr8HPGHRv

# ...

Now, let's get some secrets! First, try accessing the prod secret.

$ vault kv get secret/prod/mysecret

Error reading secret/data/prod/mysecret: Error making API request.

URL: GET http://127.0.0.1:8200/v1/secret/data/prod/mysecret
Code: 403. Errors:

* 1 error occurred:

* permission denied

As expected, you are unable to read this secret. Now, try the test secret:

$ vault kv get secret/test/mysecret

====== Metadata ======
Key              Value
---              -----
created_time     #...
deletion_time    n/a
destroyed        false
version          1

=== Data ===
Key    Value
---    -----
key    notasecret

Success! You have authenticated to Vault and have read an appropriate secret.

You learned how to run and configure HashiCorp Vault on Google Cloud to authenticate users via service accounts.

Clean up

If you are done exploring, please consider deleting your project.

Learn More

License

This work is licensed under a Creative Commons Attribution 2.0 Generic License.