diff --git a/README.md b/README.md index 2695443..f8c1a55 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,22 @@ version is the image's digest. { "EMAIL": "me@yopmail.com", "HOW_MANY_THINGS": 1, "DO_THING": false } ``` +* `secrets`: *Optional.* A map of Docker build-time secrets. These will be + available as mounted paths only during the docker build phase. + + Secrets are not stored in any metadata or layers, so they are safe to use for + access tokens and the like during the build. + + Example: + + ```yaml + secrets: + secret1: + env: BUILD_ID + secret2: + source: /a/secret/file.txt + ``` + * `cache`: *Optional.* Default `false`. When the `build` parameter is set, first pull `image:tag` from the Docker registry (so as to use cached intermediate images when building). This will cause the resource to fail diff --git a/assets/out b/assets/out index e605e26..50a4102 100755 --- a/assets/out +++ b/assets/out @@ -78,6 +78,7 @@ tag_prefix=$(jq -r '.params.tag_prefix // ""' < $payload) additional_tags=$(jq -r '.params.additional_tags // ""' < $payload) need_tag_as_latest=$(jq -r '.params.tag_as_latest // "false"' < $payload) build_args=$(jq -r '.params.build_args // {}' < $payload) +secrets=$(jq -r '.params.secrets // {}' < $payload) build_args_file=$(jq -r '.params.build_args_file // ""' < $payload) labels=$(jq -r '.params.labels // {}' < $payload) labels_file=$(jq -r '.params.labels_file // ""' < $payload) @@ -215,6 +216,32 @@ elif [ -n "$build" ]; then fi fi + expanded_secrets=() + + secret_keys=($(echo "$secrets" | jq -r 'keys | join(" ")')) + if [ "${#secret_keys[@]}" -gt 0 ]; then + # Force buildkit on + export DOCKER_BUILDKIT=1 + for key in "${secret_keys[@]}"; do + value=$(echo "$secrets" | jq -r --arg "k" "$key" '.[$k]') + for var in BUILD_ID BUILD_NAME BUILD_JOB_NAME BUILD_PIPELINE_NAME BUILD_TEAM_NAME ATC_EXTERNAL_URL; do + value="${value//\$$var/${!var:-}}" + value="${value//\$\{$var\}/${!var:-}}" + done + secret="id=${key}" + sub=$(jq -r ".params.secrets.${key} // {}" < $payload) + sub_keys=($(echo "$sub" | jq -r 'keys | join(" ")')) + if [ "${#sub_keys[@]}" -gt 0 ]; then + expanded_secrets+=("--secret") + for key in "${sub_keys[@]}"; do + value=$(echo "$sub" | jq -r --arg "k" "$key" '.[$k]') + secret="${secret},${key}=${value}" + done + expanded_secrets+=("${secret}") + fi + done + fi + expanded_labels=() label_keys=($(echo "$labels" | jq -r 'keys | join(" ")')) @@ -261,7 +288,7 @@ elif [ -n "$build" ]; then # NOTE: deactivate amazon-ecr-credential-helper so that builds go through with the DOCKER_BUILDKIT set cp ~/.docker/config.json ~/.docker/config.json.bak cat <<< "$(jq 'del(.credsStore)' ~/.docker/config.json)" > ~/.docker/config.json - docker build -t "${repository}:${tag_name}" "${target[@]}" "${expanded_build_args[@]}" "${expanded_labels[@]}" "${ssh_args[@]}" -f "$dockerfile" $cache_from "$build" + docker build -t "${repository}:${tag_name}" "${target[@]}" "${expanded_build_args[@]}" "${expanded_secrets[@]}" "${expanded_labels[@]}" "${ssh_args[@]}" -f "$dockerfile" $cache_from "$build" mv ~/.docker/config.json.bak ~/.docker/config.json # This restores the credsStore: ecr-login to config.json if needed elif [ -n "$load_file" ]; then diff --git a/tests/out_test.go b/tests/out_test.go index 802df10..b50b0a1 100644 --- a/tests/out_test.go +++ b/tests/out_test.go @@ -133,6 +133,37 @@ var _ = Describe("Out", func() { }) }) + Context("when secrets are provided", func() { + It("passes the arguments correctly to the docker daemon", func() { + session := put(map[string]interface{}{ + "source": map[string]interface{}{ + "repository": "test", + }, + "params": map[string]interface{}{ + "build": "/docker-image-resource/tests/fixtures/build", + "secrets": map[string]interface{}{ + "secret1": map[string]interface{}{ + "env": "GITHUB_TOKEN", + }, + "secret2": map[string]interface{}{ + "source": "/a/file/path.txt", + }, + "secret3": map[string]interface{}{ + "source": "/a/file/path with a space in it.txt", + }, + }, + }, + }) + + Expect(session.Err).To(gbytes.Say(dockerarg(`--secret`))) + Expect(session.Err).To(gbytes.Say(dockerarg(`id=secret1,env=GITHUB_TOKEN`))) + Expect(session.Err).To(gbytes.Say(dockerarg(`--secret`))) + Expect(session.Err).To(gbytes.Say(dockerarg(`id=secret2,source=/a/file/path.txt`))) + Expect(session.Err).To(gbytes.Say(dockerarg(`--secret`))) + Expect(session.Err).To(gbytes.Say(dockerarg(`id=secret3,source=/a/file/path with a space in it.txt`))) + }) + }) + Context("when labels are provided", func() { It("passes the labels correctly to the docker daemon", func() { session := put(map[string]interface{}{