Add external secrets to the webapp container¶
This guide explains how to securely manage and inject additional secrets into a web application container using 1Password, Sanctuary, and Google Secret Manager.
When to use this¶
Use this pattern when your application requires additional secret values (e.g. API credentials, tokens, or private config) that are not part of standard configuration and must be securely delivered at runtime.
Overview¶
To securely handle external secrets, follow this workflow:
- Store secrets in 1Password
- Define the secret in Terraform (without versioning)
- Sync the secret using
sanctuary
- Mount the secret into the web application container
1. Store secrets in 1Password¶
Store your secret values in a product- or team-specific 1Password vault. Secrets can be stored as items of different types, depending on the structure.
Tip
Use consistent naming conventions to make secrets easier to manage and reference across projects.
2. Define the secret in terraform¶
Use the google_secret_manager_secret
resource to define a secret in GCP without creating a version:
resource "google_secret_manager_secret" "external_credential" {
project = "your-gcp-project"
secret_id = "your-secret-name"
replication {
automatic = true
}
}
Warning
Do not define a google_secret_manager_secret_version
in Terraform.
Secret versions will be managed by Sanctuary.
3. Sync the secret using sanctuary¶
Use Sanctuary to pull secrets from 1Password and upload them to GCP Secret Manager. Example .logan.yaml configuration:
# .logan.yaml
version: "1.3"
sanctuary:
secrets:
some-name:
from:
op-cli-item:
item-id: abcdefghijklmnop
fields: [username, password]
use_field_labels: true
to:
google-secret:
project: your-gcp-project
name: your-secret-name
4. Access secrets in your application¶
Preferred approach¶
The recommended and most secure method is to use Google Cloud Client Libraries from the code to access secrets directly. This approach leverages built-in IAM permissions, enabling secure and manageable access control.
If, for some reason, this approach is not feasible in your application, there are alternative options available. The infrastructure boilerplate project uses the gcp-cloud-run-app Terraform module. This module supports mounting secrets either as volumes or environment variables.
Alternative option #1: mount as a volume¶
This approach makes secrets available as files inside the container, which is generally more secure and compatible with secret rotation.
module "webapp" {
source = "gitlab.developers.cam.ac.uk/uis/gcp-cloud-run-app/devops"
version = "~> 10.0"
region = "europe-west2"
project = "example-project-id-1234"
containers = {
webapp = {
image = "us-docker.pkg.dev/cloudrun/container/hello"
volume_mounts = [
{
name = "secret-volume"
mount_path = "/secrets"
}
]
}
}
volumes = [
{
name = "secret-volume"
secret = {
secret = google_secret_manager_secret.main.id
items = [
{
version = "latest"
path = "my-secret"
}
]
}
}
]
}
In this example, the secret is available inside the container at /secrets/my-secret
.
Alternative option #2: mount as an environment variable¶
If the application does not support file-based secrets, you can inject a secret directly as an environment variable.
env = [
{
name = "SECRET"
value_source = {
secret_key_ref = {
secret = google_secret_manager_secret.main.id
version = "latest"
}
}
}
]
Warning
Environment variables are exposed to the process and child processes, and can be logged or dumped accidentally. Use file-based secrets if possible.
Additional notes¶
- This workflow avoids committing secrets to Git or Terraform state.
- It supports secure, automated updates of secrets from a single source of truth.
- It aligns with best practices for secret management.