This tutorial walks you through deploying Hashicorp's Vault on Cloud Run, Google Cloud's container based Serverless compute platform.
Vault is a tool for encrypting data, managing secrets, and auditing access to them. Vault should be deployed to a secure and highly available environment to ensure applications have reliable access to secrets and credentials. Vault can leverage managed services such as Cloud KMS and Google Cloud Storage to protect and store its data, and Cloud Run to serve it and capture audit logs.
While Vault can be deployed to Kubernetes, or a virtual machine, Cloud Run reduces operational complexity by delegating infrastructure management to the cloud provider. Cloud Run can scale Vault to zero when not in use, or be configured to keep a single instance available to serve concurrent request at the lowest latency possible. Cloud Run automatically captures Vault's audit logs and sends them to Cloud Logging for centralized storage and analysis.
Cloud Run can also be configured to ensure only one instance of Vault is running at a given time. See the Cloud Run maximum instances docs.
Create a new Google Cloud project:
gcloud projects create \
--name vault-on-cloud-run \
--set-as-default
Type y
at the prompt and press enter
:
No project id provided.
Use [vault-on-cloud-run-XXXXXX] as project id (Y/n)? y
Before you can continue you must enable billing on the project to enable the use of Cloud KMS as an auto-unseal mechanism.
To streamline the rest of the tutorial define the key configuration settings and assign them to environment variables:
PROJECT_ID=$(gcloud config get-value project)
PROJECT_ID
holds the project id generated at the start of the tutorial.
GCS_BUCKET_NAME="gs://${PROJECT_ID}-data"
GCS_BUCKET_NAME
holds the Google Cloud Storage bucket name used to persist Vault's data.
SERVICE_ACCOUNT_EMAIL="vault-server@${PROJECT_ID}.iam.gserviceaccount.com"
SERVICE_ACCOUNT_EMAIL
holds the Google Cloud IAM email address representing thevault-server
service account.
CURRENT_USER_EMAIL=$(gcloud config list account --format "value(core.account)")
CURRENT_USER_EMAIL
holds the email address representing the current logged in Google Cloud user.
Enable the Cloud KMS, Cloud Run, and Cloud Storage APIs:
gcloud services enable --async \
cloudkms.googleapis.com \
run.googleapis.com \
storage.googleapis.com
Create a GCS storage bucket to hold Vault's encrypted data:
gsutil mb ${GCS_BUCKET_NAME}
Creating gs://vault-on-cloud-run-XXXXXX-data/...
Create a service account for the Vault server:
gcloud iam service-accounts create vault-server
Grant the necessary permissions on GCS storage bucket for the vault-server
service account:
gsutil iam ch \
serviceAccount:${SERVICE_ACCOUNT_EMAIL}:objectAdmin \
${GCS_BUCKET_NAME}
Create a Cloud KMS key ring that will be used to hold Vault seal
encryption key:
gcloud kms keyrings create "vault-server" \
--location "global"
Create the Cloud KMS encryption key that will be used to encrypt and decrypt the Vault master key:
gcloud kms keys create "seal" \
--location "global" \
--keyring "vault-server" \
--purpose "encryption"
Grant the necessary permissions on the seal
Cloud KMS encryption key for the vault-server
service account:
gcloud kms keys add-iam-policy-binding seal \
--keyring "vault-server" \
--location "global" \
--member serviceAccount:${SERVICE_ACCOUNT_EMAIL} \
--role roles/cloudkms.cryptoKeyEncrypterDecrypter
Deploy the vault-server
Cloud Run service:
The initial deployment will be made private to prevent someone else from initializing the Vault server. Clients will need to provide authentication credentials that will be validated by the Cloud Run control plane. After Vault has been initialized the Cloud Run deployment will be updated to bypass Cloud Run authentication and delegate the authentication to Vault.
gcloud beta run deploy vault-server \
--no-allow-unauthenticated \
--concurrency 50 \
--cpu 2 \
--image gcr.io/hightowerlabs/vault:run \
--memory '2G' \
--min-instances 1 \
--max-instances 1 \
--platform managed \
--port 8200 \
--service-account ${SERVICE_ACCOUNT_EMAIL} \
--set-env-vars="GOOGLE_PROJECT=${PROJECT_ID},GOOGLE_STORAGE_BUCKET=${GCS_BUCKET_NAME}" \
--timeout 300 \
--region us-west1
Grant permission to the current logged in GCP user to invoke the vault-server
Cloud Run service:
gcloud run services add-iam-policy-binding vault-server \
--member="user:${CURRENT_USER_EMAIL}" \
--role='roles/run.invoker' \
--platform managed \
--region us-west1
Retrieve the vault-server
service URL:
VAULT_SERVICE_URL=$(gcloud run services describe vault-server \
--platform managed \
--region us-west1 \
--format 'value(status.url)')
Use curl
to retrieve the status of the Vault server:
The
gcloud
command can be used to generate an identity token which can be used to authenticate to the privatevault-server
Cloud Run service.
curl -s -X GET \
${VAULT_SERVICE_URL}/v1/sys/seal-status \
-H "Authorization: Bearer $(gcloud auth print-identity-token)"
{
"type": "gcpckms",
"initialized": false,
"sealed": true,
"t": 0,
"n": 0,
"progress": 0,
"nonce": "",
"version": "1.6.0",
"migration": false,
"recovery_seal": true,
"storage_type": "gcs"
}
From the output above you can see that the Vault server has not been initialized and is currently sealed. Use the curl
command to initialize the Vault server:
cat <<EOF > init.json
{
"recovery_shares": 1,
"recovery_threshold": 1,
"secret_shares": 1,
"secret_threshold": 1,
"stored_share": 1
}
EOF
curl -s -X PUT \
${VAULT_SERVICE_URL}/v1/sys/init \
-H "Authorization: Bearer $(gcloud auth print-identity-token)" \
--data @init.json
{
"keys": [],
"keys_base64": [],
"recovery_keys": [
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
],
"recovery_keys_base64": [
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
],
"root_token": "X.XXXXXXXXXXXXXXXXXXXXXXXX"
}
At this point the Vault server has been initialized. If you list the GCS storage bucket you will see a new set of directories created by Vault:
gsutil ls ${GCS_BUCKET_NAME}
gs://vault-on-cloud-run-XXXXXX-data/core/
gs://vault-on-cloud-run-XXXXXX-data/logical/
gs://vault-on-cloud-run-XXXXXX-data/sys/
Now that Vault has been initialized make the vault-server
service public:
Requests to Vault will no longer be authenticated by the Cloud Run control plane. Requests will be authenticated directly by Vault.
gcloud beta run deploy vault-server \
--allow-unauthenticated \
--concurrency 50 \
--cpu 2 \
--image gcr.io/hightowerlabs/vault:run \
--memory '2G' \
--min-instances 1 \
--max-instances 1 \
--platform managed \
--port 8200 \
--service-account ${SERVICE_ACCOUNT_EMAIL} \
--set-env-vars="GOOGLE_PROJECT=${PROJECT_ID},GOOGLE_STORAGE_BUCKET=${GCS_BUCKET_NAME}" \
--timeout 300 \
--region us-west1
At this point Vault is up and running and can be configured using the Vault UI by visiting the vault-server
service URL in browser:
gcloud run services describe vault-server \
--platform managed \
--region us-west1 \
--format 'value(status.url)'
You can also use the vault
command line tool as described in the next section.
Download the Vault binary and add it to your path:
vault version
Vault v1.6.0 (7ce0bd9691998e0443bc77e98b1e2a4ab1e965d4)
Configure the vault CLI to use the vault-server
Cloud Run service URL by setting the VAULT_ADDR
environment variable:
VAULT_ADDR=$(gcloud run services describe vault-server \
--platform managed \
--region us-west1 \
--format 'value(status.url)')
Retrieve the status of the remote Vault server:
vault status
Key Value
--- -----
Recovery Seal Type shamir
Initialized true
Sealed false
Total Recovery Shares 1
Threshold 1
Version 1.6.0
Storage Type gcs
Cluster Name vault-cluster-XXXXXXXX
Cluster ID XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
HA Enabled false
gcloud projects delete ${PROJECT_ID}