Using environment secrets as build arguments in Google Cloud Build

Google Cloud Build is a pretty nice tool for building your docker images continually, and cloud-build-local is pretty great for working on your images in dev. All around, a nice piece of kit to have in a Kubernetes shop.

The docs are pretty good, but one thing that I've recently dealt with did not show up in my searching; how to use an environment secret as a build argument to Docker. So here's how I found to do it.

First, we will follow the encrypted secrets guide to get a secret wrapped up by KMS.

$ # Create a keyring and encryption key
$ gcloud kms keyrings create tinkering --location=global
$ gcloud kms keys create cloud-build-demo \
--keyring=tinkering \
--purpose=encryption \

$ Now encrypt our secret string
$ echo -n "This is the super secret secret." | gcloud kms encrypt \
--plaintext-file=- \
--ciphertext-file=- \
--location=global \
--keyring=tinkering \
--key=cloud-build-demo | base64


Next, we will create a super simple Dockerfile to show how it is used.

FROM busybox
RUN echo "::${THE_SECRET}::"

Last, we set up the cloudbuild.yaml. In the documentation demo files they use a shell entrypoint to access the environment variable.

# Note: You need a shell to resolve environment variables with $$
- name: ''
entrypoint: 'bash'
args: ['-c', 'docker login --username=[MY-USER] --password=$$PASSWORD']
secretEnv: ['PASSWORD']

However, it would be nicer to not have to stringify our whole Docker build command.

Luckily, using --build-arg without a value falls through to the environment variable of the same name.

$ export HTTP_PROXY=
$ docker build --build-arg HTTP_PROXY .

So, we can just use it directly:

- id: docker
name: ''
args: ['build', '--build-arg', 'THE_SECRET', '.']
secretEnv: ['THE_SECRET']
- kmsKeyName: projects/hobbs-tinkering/locations/global/keyRings/tinkering/cryptoKeys/cloud-build-demo
THE_SECRET: CiQATajs0GI7M6ZFM68Qu+GbJTfJ/d3tqqLcHz69RY1AaHkzV20SSQDt7E4V65imqbOnq8DvieiaglxjEztxWQCwrr2Mtu+xwT6tko6FHB+NNauyos6X1nnh5x217Cwx5QbX3h0YtjOJ15I4dnHDM+I=

Testing locally, it happily runs:

$ cloud-build-local --dryrun=false .
Using default tag: latest
latest: Pulling from cloud-builders/metadata
Digest: sha256:bcdb85e67ab9719c6441cb80fe9e8badc6d5ab0ab8bc73ee67adc0112233d20c
Status: Image is up to date for
2018/12/23 13:18:29 Started spoofed metadata server
2018/12/23 13:18:29 Build id = localbuild_9cef1240-3a68-4ec3-a273-f49cd018316d
2018/12/23 13:18:29 status changed to "BUILD"
Starting Step #0 - "docker"
Step #0 - "docker": Already have image (with digest):
Step #0 - "docker": Sending build context to Docker daemon 5.12kB
Step #0 - "docker": Step 1/3 : FROM busybox
Step #0 - "docker": ---> 59788edf1f3e
Step #0 - "docker": Step 2/3 : ARG THE_SECRET
Step #0 - "docker": ---> Using cache
Step #0 - "docker": ---> f289a756b157
Step #0 - "docker": Step 3/3 : RUN echo "::${THE_SECRET}::"
Step #0 - "docker": ---> Running in 0e90f8f4f349
Step #0 - "docker": ::This is the super secret secret.::
Step #0 - "docker": Removing intermediate container 0e90f8f4f349
Step #0 - "docker": ---> 75d19dee1d47
Step #0 - "docker": Successfully built 75d19dee1d47
Finished Step #0 - "docker"
2018/12/23 13:18:35 status changed to "DONE"

It is worth noting that using build args for secrets is not recommended. Anyone with the image can see what the argument passed in was.

$ docker history 75d19dee1d47
75d19dee1d47 4 days ago |1 THE_SECRET=This is the super secret secre…¦ 0B
f289a756b157 4 days ago /bin/sh -c #(nop) ARG THE_SECRET 0B
59788edf1f3e 2 months ago /bin/sh -c #(nop) CMD ["sh"] 0B
<missing> 2 months ago /bin/sh -c #(nop) ADD file:63eebd629a5f7558c…¦ 1.15MB

Docker 18.09, added build secrets for a better solution, but GCB is still running Docker 17.12, so we will have to wait for that update.

A gist of the code is available at: