Secure your cloud build secrets
Let's talk about secrets. No, not the "I still listen to early 2000s pop music" kind. I mean the juicy stuff - API keys, database passwords, service account credentials. The things that make your Cloud Build actually do things.
The problem? You need these secrets during your build, but you also need to not accidentally broadcast them to the world. Enter Secret Manager, your new best friend.
"I'll just hardcode it real quick" - Famous last words
We've all been there. It's 4pm on a Friday. You just need to get this deploy working. Surely nobody will notice if you just... put the API key right there in the YAML?
Narrator: They noticed.
Here's why hardcoding secrets is a terrible, no-good, very bad idea:
- Build logs are gossipy - They'll tell anyone who asks exactly what your secrets are
- Git remembers everything - That "quick fix" is now immortalized in your commit history forever. Congrats, it's a git tattoo
- No audit trail - When something goes wrong (and it will), you'll have zero idea who accessed what
Secret Manager fixes all of this. It's like a really secure diary for your sensitive data, except it actually keeps secrets instead of leaving itself open on the kitchen table.
Let's set this thing up
First, create a secret. You can click around in the console if you're feeling fancy, or use gcloud like a cool kid:
echo -n "my-super-secret-value" | gcloud secrets create MY_SECRET --data-file=-
Now for the important bit - your [[Cloud Build]] service account needs permission to peek at the secret. It's like giving someone the combination to your locker, except way more enterprise-y:
gcloud secrets add-iam-policy-binding MY_SECRET \
--member="serviceAccount:PROJECT_NUMBER@cloudbuild.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
The YAML magic
Here's where it all comes together in your cloudbuild.yaml:
steps:
- name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- 'docker build --build-arg API_KEY=$$API_KEY -t gcr.io/$PROJECT_ID/my-app .'
secretEnv: ['API_KEY']
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_ID/secrets/MY_SECRET/versions/latest
env: 'API_KEY'
A few things to notice (take notes, there will be a quiz):
- The
availableSecretsblock - This is the VIP list. It maps your Secret Manager secrets to environment variables - The
secretEnvfield - Tells the step "hey, you're allowed to use this secret." No permission slip, no secret - The
$$prefix - Use two dollar signs. One dollar sign is for substitutions. Two is for secrets. I don't make the rules
Hard-earned wisdom (aka stuff that broke my builds)
- Pin your versions in prod - Using
latestis fine until someone updates the secret and your 3am deployment explodes. Ask me how I know - Rotate secrets like you rotate your tires - Regularly, and before something bad happens
- Principle of least privilege - Your build doesn't need access to ALL the secrets. Don't be that person
- Substitutions are your friend - Combine them with secrets for that sweet, sweet environment flexibility
Gotchas that got me
- IAM is fashionably late - Just added the permission? Cool, wait a minute. Maybe two. Grab a coffee
- Case sensitivity is cruel -
MY_SECRETandmy_secretare completely different secrets. GCP doesn't care about your intentions, only your spelling - The secret must actually exist - This sounds stupid, but I've definitely stared at a failing build wondering why it couldn't find a secret I forgot to create. Multiple times
TL;DR
Use Secret Manager with Cloud Build. Yes, it's more work than just slapping your API key in the YAML. But you know what's even more work? Rotating all your credentials at 2am because you accidentally pushed them to a public repo.
Your future self will send you a thank-you card. Or at least won't curse your name.