Skip to content
This repository was archived by the owner on Dec 26, 2022. It is now read-only.

Commit 3683755

Browse files
Merge pull request #27 from grapl-security/cm/fail-if-no-secrets
Fail if no secrets are provided
2 parents 9faf8a6 + 244289e commit 3683755

File tree

6 files changed

+167
-41
lines changed

6 files changed

+167
-41
lines changed

.buildkite/pipeline.verify.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ env:
22
PANTS_CONFIG_FILES: "['pants.toml', 'pants.ci.toml']"
33

44
steps:
5-
- label: ":jeans: All files are covered by Pants"
5+
- label: ":pants: All files are covered by Pants"
66
command:
77
- ./pants tailor --check
88

@@ -19,10 +19,9 @@ steps:
1919
command:
2020
- make lint-plugin
2121

22-
# TODO: Add some unit tests!
23-
# - label: ":lint-roller::buildkite: Test Plugin"
24-
# command:
25-
# - make test-plugin
22+
- label: ":lint-roller::buildkite: Test Plugin"
23+
command:
24+
- make test-plugin
2625

2726
- label: ":buildkite::vault: Run Plugin 1"
2827
command: echo 'Hello World'

Makefile

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,53 @@
11
DOCKER_COMPOSE_CHECK := docker compose run --rm
22

3-
# Linting
3+
.PHONY: all
4+
all: format
5+
all: lint
6+
all: test
7+
all: ## Run all operations
8+
9+
.PHONY: help
10+
help: ## Print this help
11+
@awk 'BEGIN {FS = ":.*##"; printf "Usage: make <target>\n"} \
12+
/^[a-zA-Z0-9_-]+:.*?##/ { printf " %-46s %s\n", $$1, $$2 } \
13+
/^##@/ { printf "\n%s\n", substr($$0, 5) } ' \
14+
$(MAKEFILE_LIST)
15+
@printf '\n'
16+
17+
##@ Linting
418
########################################################################
519

620
.PHONY: lint
7-
lint: lint-plugin lint-shell
21+
lint: lint-plugin
22+
lint: lint-shell
23+
lint: ## Perform lint checks on all files
824

925
.PHONY: lint-plugin
10-
lint-plugin:
26+
lint-plugin: ## Lint the Buildkite plugin metadata
1127
$(DOCKER_COMPOSE_CHECK) plugin-linter
1228

1329
.PHONY: lint-shell
14-
lint-shell:
30+
lint-shell: ## Lint the shell scripts
1531
./pants lint ::
1632

17-
# Formatting
33+
##@ Formatting
1834
########################################################################
1935

2036
.PHONY: format
2137
format: format-shell
38+
format: ## Automatically format all code
2239

2340
.PHONY: format-shell
24-
format-shell:
41+
format-shell: ## Format shell scripts
2542
./pants fmt ::
2643

27-
# Testing
44+
##@ Testing
2845
########################################################################
2946

3047
.PHONY: test
3148
test: test-plugin
49+
test: ## Run all tests
3250

3351
.PHONY: test-plugin
34-
test-plugin:
52+
test-plugin: ## Test the Buildkite plugin locally (does *not* run a Buildkite pipeline)
3553
$(DOCKER_COMPOSE_CHECK) plugin-tester
36-
37-
.PHONY: all
38-
all: format lint test

hooks/environment

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22

33
set -euo pipefail
44

5+
# shellcheck source-path=SCRIPTDIR
6+
source "$(dirname "${BASH_SOURCE[0]}")/../lib/log.sh"
7+
58
readonly default_image="hashicorp/envconsul"
69
readonly default_tag="latest"
710
readonly image="${BUILDKITE_PLUGIN_VAULT_ENV_IMAGE:-${default_image}}:${BUILDKITE_PLUGIN_VAULT_ENV_TAG:-${default_tag}}"
811

912
# Fail if there is no Vault token; gotta log in first
1013
########################################################################
1114
if [ -z "${VAULT_TOKEN:-}" ]; then
12-
echo "--- :skull_and_crossbones: Could not find 'VAULT_TOKEN' in the environment!"
13-
exit 1
15+
raise_error "Could not find 'VAULT_TOKEN' in the environment!"
1416
fi
1517

1618
# Resolve Vault address
@@ -20,8 +22,7 @@ if [ -n "${BUILDKITE_PLUGIN_VAULT_ENV_ADDRESS:-}" ]; then
2022
export VAULT_ADDR
2123
fi
2224
if [ -z "${VAULT_ADDR:-}" ]; then
23-
echo "--- :skull_and_crossbones: Could not find 'VAULT_ADDR' in the environment, and 'BUILDKITE_PLUGIN_VAULT_ENV_ADDRESS' was not specified!"
24-
exit 1
25+
raise_error "Could not find 'VAULT_ADDR' in the environment, and 'BUILDKITE_PLUGIN_VAULT_ENV_ADDRESS' was not specified!"
2526
fi
2627

2728
# Resolve Vault namespace
@@ -31,8 +32,7 @@ if [ -n "${BUILDKITE_PLUGIN_VAULT_ENV_NAMESPACE:-}" ]; then
3132
export VAULT_NAMESPACE
3233
fi
3334
if [ -z "${VAULT_NAMESPACE:-}" ]; then
34-
echo "--- :skull_and_crossbones: Could not find 'VAULT_NAMESPACE' in the environment, and 'BUILDKITE_PLUGIN_VAULT_ENV_NAMESPACE' was not specified!"
35-
exit 1
35+
raise_error "Could not find 'VAULT_NAMESPACE' in the environment, and 'BUILDKITE_PLUGIN_VAULT_ENV_NAMESPACE' was not specified!"
3636
fi
3737

3838
# Resolve secret prefix
@@ -45,10 +45,9 @@ if [[ -n "${secret_prefix}" && ! "${secret_prefix}" =~ /$ ]]; then
4545
fi
4646
readonly secret_prefix
4747

48+
# Resolve secrets
4849
########################################################################
4950

50-
readonly container_name="vault-env-plugin-${BUILDKITE_JOB_ID}"
51-
5251
# STOLEN FROM https://github.com/buildkite-plugins/docker-buildkite-plugin/blob/9f90d8ef742d9fa1eb3556720e16f2b842ff1cb2/hooks/command#L25-L47
5352
#
5453
# Reads a list from plugin config into a global result array
@@ -61,8 +60,7 @@ plugin_read_list_into_result() {
6160
local parameter="${prefix}_${i}"
6261

6362
if [[ -n "${!prefix:-}" ]]; then
64-
echo ":rotating_light: Plugin received a string for $prefix, expected an array" >&2
65-
exit 1
63+
raise_error "Plugin received a string for $prefix, expected an array"
6664
fi
6765

6866
while [[ -n "${!parameter:-}" ]]; do
@@ -75,24 +73,32 @@ plugin_read_list_into_result() {
7573
[[ ${#result[@]} -gt 0 ]] || return 1
7674
}
7775

78-
envconsul_env() {
79-
# This populates a `result` array for later use
80-
plugin_read_list_into_result BUILDKITE_PLUGIN_VAULT_ENV_SECRETS
76+
secrets=()
77+
if plugin_read_list_into_result BUILDKITE_PLUGIN_VAULT_ENV_SECRETS; then
78+
secrets=("${result[@]}")
79+
else
80+
raise_error "At least one secret must be specified!"
81+
fi
8182

82-
secrets=()
83-
for secret in "${result[@]}"; do
83+
########################################################################
84+
85+
readonly container_name="vault-env-plugin-${BUILDKITE_JOB_ID}"
86+
87+
envconsul_env() {
88+
secret_args=()
89+
for secret in "${secrets[@]}"; do
8490
# secret_prefix is guaranteed to end with a / if it is non-empty
85-
secrets+=("-secret=${secret_prefix}${secret}")
91+
secret_args+=("-secret=${secret_prefix}${secret}")
8692
done
8793

8894
# Explicitly *not* using `--rm` so we can output the container
8995
# logs in case of a failure.
90-
docker run \
96+
log_and_run docker run \
9197
--env VAULT_TOKEN \
9298
--name="${container_name}" \
9399
-- \
94100
"${image}" \
95-
"${secrets[@]}" \
101+
"${secret_args[@]}" \
96102
-once \
97103
-upcase \
98104
-pristine \
@@ -105,23 +111,23 @@ envconsul_env() {
105111
}
106112

107113
cleanup() {
108-
docker container rm --force "${container_name}" > /dev/null 2>&1
114+
log_and_run docker container rm --force "${container_name}" > /dev/null 2>&1
109115
}
110116

111117
trap cleanup EXIT INT QUIT
112118

113-
echo "--- :vault: Pulling secrets from Vault"
114-
echo "Using Docker image: ${image}"
115-
echo "VAULT_ADDR=${VAULT_ADDR}"
116-
echo "VAULT_NAMESPACE=${VAULT_NAMESPACE}"
119+
log "--- :vault: Pulling secrets from Vault"
120+
log "Using Docker image: ${image}"
121+
log "VAULT_ADDR=${VAULT_ADDR}"
122+
log "VAULT_NAMESPACE=${VAULT_NAMESPACE}"
117123

118124
if vault_env=$(envconsul_env); then
119125
set -o allexport
120126
eval "${vault_env}"
121127
set +o allexport
122128
else
123129
retval=$?
124-
echo "--- :skull_and_crossbones: Failed to retrieve secrets from Vault"
125-
docker container logs "${container_name}"
130+
log "--- :skull_and_crossbones: Failed to retrieve secrets from Vault"
131+
log_and_run docker container logs "${container_name}"
126132
exit ${retval}
127133
fi

lib/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
shell_sources()

lib/log.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env bash
2+
3+
# Various logging functions and helpers to make logging nice.
4+
5+
# ANSI Formatting Codes
6+
########################################################################
7+
# Because who wants to remember all those fiddly details?
8+
9+
# CSI = "Control Sequence Introducer"
10+
CSI="\e["
11+
END=m
12+
13+
NORMAL=0
14+
BOLD=1
15+
16+
WHITE=37
17+
18+
RESET="${CSI}${NORMAL}${END}"
19+
20+
function _bold_color() {
21+
color="${1}"
22+
shift
23+
echo "${CSI}${BOLD};${color}${END}${*}${RESET}"
24+
}
25+
26+
function bright_white() {
27+
_bold_color "${WHITE}" "${@}"
28+
}
29+
30+
# Logging
31+
########################################################################
32+
# NOTE: All logs get sent to standard error
33+
34+
function log() {
35+
echo -e "${@}" >&2
36+
}
37+
38+
# Handy for logging the exact command to be run, and then running it
39+
function log_and_run() {
40+
log ❯❯ "$(bright_white "$(printf "%q " "${@}")")"
41+
"$@"
42+
}
43+
44+
raise_error() {
45+
log "--- :rotating_light:" "${@}"
46+
# Yes, these numbers are correct :/
47+
if [ -z "${BASH_SOURCE[2]:-}" ]; then
48+
# If we're calling raise_error from a script directly, we'll
49+
# have a shorter call stack.
50+
log "Failed in ${FUNCNAME[1]}() at [${BASH_SOURCE[1]}:${BASH_LINENO[0]}]"
51+
else
52+
log "Failed in ${FUNCNAME[1]}() at [${BASH_SOURCE[1]}:${BASH_LINENO[0]}], called from [${BASH_SOURCE[2]}:${BASH_LINENO[1]}]"
53+
fi
54+
exit 1
55+
}

tests/environment.bats

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env bats
2+
3+
load "$BATS_PLUGIN_PATH/load.bash"
4+
5+
# Uncomment to enable stub debugging
6+
# export DOCKER_STUB_DEBUG=/dev/tty
7+
8+
setup() {
9+
export DEFAULT_IMAGE=hashicorp/envconsul
10+
export DEFAULT_TAG=latest
11+
12+
export VAULT_TOKEN=testingtoken
13+
export VAULT_ADDR=default.vault.mycompany.com:8200
14+
export VAULT_NAMESPACE=default_namespace
15+
16+
export BUILDKITE_JOB_ID=2112
17+
}
18+
19+
teardown() {
20+
unset VAULT_TOKEN
21+
unset VAULT_ADDR
22+
unset VAULT_NAMESPACE
23+
24+
unset BUILDKITE_PLUGIN_VAULT_ENV_SECRETS_0
25+
}
26+
27+
@test "The happy path works" {
28+
export BUILDKITE_PLUGIN_VAULT_ENV_SECRETS_0="foo"
29+
30+
docker_vault_cmd="run --env VAULT_TOKEN --name=vault-env-plugin-${BUILDKITE_JOB_ID} -- ${DEFAULT_IMAGE}:${DEFAULT_TAG}"
31+
32+
stub docker \
33+
"${docker_vault_cmd} -secret=foo -once -upcase -pristine -no-prefix=true -vault-addr=${VAULT_ADDR} -vault-namespace=${VAULT_NAMESPACE} -vault-renew-token=false -vault-retry-attempts=1 env : echo 'foo=something_very_secret'" \
34+
"container rm --force vault-env-plugin-2112 : echo 'removing'"
35+
36+
run "${PWD}/hooks/environment"
37+
assert_success
38+
39+
unstub docker
40+
}
41+
42+
@test "Fails if no secrets are specified" {
43+
44+
unset BUILDKITE_PLUGIN_VAULT_ENV_SECRETS_0
45+
46+
run "${PWD}/hooks/environment"
47+
assert_failure
48+
49+
assert_output --partial "At least one secret must be specified"
50+
}

0 commit comments

Comments
 (0)