Skip to content
Snippets Groups Projects
Commit ca78c437 authored by Pierre Smeyers's avatar Pierre Smeyers
Browse files

Merge branch '31-implement-secret-handling' into 'master'

Resolve "Implement Secret handling"

Closes #31

See merge request to-be-continuous/node!63
parents 418d91fe ad135180
No related branches found
No related tags found
No related merge requests found
...@@ -420,3 +420,59 @@ Don't forget to exclude undesired folders and files from the package resources ( ...@@ -420,3 +420,59 @@ Don't forget to exclude undesired folders and files from the package resources (
* the `reports/` folder, that is used by most _to be continuous_ to output all kind of reports, * the `reports/` folder, that is used by most _to be continuous_ to output all kind of reports,
* the Node.js build output dir (if any), * the Node.js build output dir (if any),
* any other undesired file & folder that you don't want to appear in your published package(s). * any other undesired file & folder that you don't want to appear in your published package(s).
## Variants
The Node template can be used in conjunction with template variants to cover specific cases.
### Vault variant
This variant allows delegating your secrets management to a [Vault](https://www.vaultproject.io/) server.
#### Configuration
In order to be able to communicate with the Vault server, the variant requires the additional configuration parameters:
| Input / Variable | Description | Default value |
| ----------------- | -------------------------------------- | ----------------- |
| `TBC_VAULT_IMAGE` | The [Vault Secrets Provider](https://gitlab.com/to-be-continuous/tools/vault-secrets-provider) image to use (can be overridden) | `registry.gitlab.com/to-be-continuous/tools/vault-secrets-provider:master` |
| `vault-base-url` / `VAULT_BASE_URL` | The Vault server base API url | _none_ |
| `vault-oidc-aud` / `VAULT_OIDC_AUD` | The `aud` claim for the JWT | `$CI_SERVER_URL` |
| :lock: `VAULT_ROLE_ID` | The [AppRole](https://www.vaultproject.io/docs/auth/approle) RoleID | **must be defined** |
| :lock: `VAULT_SECRET_ID` | The [AppRole](https://www.vaultproject.io/docs/auth/approle) SecretID | **must be defined** |
#### Usage
Then you may retrieve any of your secret(s) from Vault using the following syntax:
```text
@url@http://vault-secrets-provider/api/secrets/{secret_path}?field={field}
```
With:
| Parameter | Description |
| -------------------------------- | -------------------------------------- |
| `secret_path` (_path parameter_) | this is your secret location in the Vault server |
| `field` (_query parameter_) | parameter to access a single basic field from the secret JSON payload |
#### Example
```yaml
include:
# main template
- component: gitlab.com/to-be-continuous/node/gitlab-ci-node@3.11.1
# Vault variant
- component: gitlab.com/to-be-continuous/node/gitlab-ci-node-vault@3.11.1
inputs:
# audience claim for JWT
vault-oidc-aud: "https://vault.acme.host"
vault-base-url: "https://vault.acme.host/v1"
# $VAULT_ROLE_ID and $VAULT_SECRET_ID defined as a secret CI/CD variable
variables:
NODE_CONFIG_SCOPED_REGISTRIES: "@public-repo:https://public.npm.registry/some/repo @my-priv-repo:https://private.npm.registry/another/repo"
# retrieve private repo auth token from Vault
NODE_REGISTRY_MY_PRIV_REPO_AUTH: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/node/priv-repo/creds?field=token"
```
\ No newline at end of file
...@@ -144,5 +144,43 @@ ...@@ -144,5 +144,43 @@
} }
] ]
} }
],
"variants": [
{
"id": "vault",
"name": "Vault",
"description": "Retrieve secrets from a [Vault](https://www.vaultproject.io/) server",
"template_path": "templates/gitlab-ci-node-vault.yml",
"variables": [
{
"name": "TBC_VAULT_IMAGE",
"description": "The [Vault Secrets Provider](https://gitlab.com/to-be-continuous/tools/vault-secrets-provider) image to use",
"default": "registry.gitlab.com/to-be-continuous/tools/vault-secrets-provider:master",
"advanced": true
},
{
"name": "VAULT_BASE_URL",
"description": "The Vault server base API url",
"mandatory": true
},
{
"name": "VAULT_OIDC_AUD",
"description": "The `aud` claim for the JWT",
"default": "$CI_SERVER_URL"
},
{
"name": "VAULT_ROLE_ID",
"description": "The [AppRole](https://www.vaultproject.io/docs/auth/approle) RoleID",
"mandatory": true,
"secret": true
},
{
"name": "VAULT_SECRET_ID",
"description": "The [AppRole](https://www.vaultproject.io/docs/auth/approle) SecretID",
"mandatory": true,
"secret": true
}
]
}
] ]
} }
# =====================================================================================================================
# === Vault template variant
# =====================================================================================================================
spec:
inputs:
vault-base-url:
description: The Vault server base API url
default: ''
vault-oidc-aud:
description: The `aud` claim for the JWT
default: $CI_SERVER_URL
---
variables:
# variabilized vault-secrets-provider image
TBC_VAULT_IMAGE: registry.gitlab.com/to-be-continuous/tools/vault-secrets-provider:master
VAULT_BASE_URL: $[[ inputs.vault-base-url ]]
# variables have to be explicitly declared in the YAML to be exported to the service
VAULT_ROLE_ID: "$VAULT_ROLE_ID"
VAULT_SECRET_ID: "$VAULT_SECRET_ID"
VAULT_OIDC_AUD: $[[ inputs.vault-oidc-aud ]]
.node-base:
services:
- name: "$TBC_TRACKING_IMAGE"
command: ["--service", "node", "3.11.1"]
- name: "$TBC_VAULT_IMAGE"
alias: "vault-secrets-provider"
variables:
VAULT_JWT_TOKEN: "$VAULT_JWT_TOKEN"
id_tokens:
VAULT_JWT_TOKEN:
aud: "$VAULT_OIDC_AUD"
...@@ -344,6 +344,79 @@ stages: ...@@ -344,6 +344,79 @@ stages:
log_info "... done" log_info "... done"
} }
# evaluate and export a secret
# - $1: secret variable name
function eval_secret() {
name=$1
value=$(eval echo "\$${name}")
# create the /tmp directory (it is required by the mktemp command)
mkdir -p /tmp
case "$value" in
@b64@*)
decoded=$(mktemp)
errors=$(mktemp)
if echo "$value" | cut -c6- | base64 -d > "${decoded}" 2> "${errors}"
then
# shellcheck disable=SC2086
export ${name}="$(cat ${decoded})"
log_info "Successfully decoded base64 secret \\e[33;1m${name}\\e[0m"
else
fail "Failed decoding base64 secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")"
fi
;;
@hex@*)
decoded=$(mktemp)
errors=$(mktemp)
if echo "$value" | cut -c6- | sed 's/\([0-9A-F]\{2\}\)/\\\\x\1/gI' | xargs printf > "${decoded}" 2> "${errors}"
then
# shellcheck disable=SC2086
export ${name}="$(cat ${decoded})"
log_info "Successfully decoded hexadecimal secret \\e[33;1m${name}\\e[0m"
else
fail "Failed decoding hexadecimal secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")"
fi
;;
@url@*)
url=$(echo "$value" | cut -c6-)
if command -v curl > /dev/null
then
decoded=$(mktemp)
errors=$(mktemp)
if curl -s -S -f --connect-timeout 5 -o "${decoded}" "$url" 2> "${errors}"
then
# shellcheck disable=SC2086
export ${name}="$(cat ${decoded})"
log_info "Successfully curl'd secret \\e[33;1m${name}\\e[0m"
else
log_warn "Failed getting secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")"
fi
elif command -v wget > /dev/null
then
decoded=$(mktemp)
errors=$(mktemp)
if wget -T 5 -O "${decoded}" "$url" 2> "${errors}"
then
# shellcheck disable=SC2086
export ${name}="$(cat ${decoded})"
log_info "Successfully wget'd secret \\e[33;1m${name}\\e[0m"
else
log_warn "Failed getting secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")"
fi
else
log_warn "Couldn't get secret \\e[33;1m${name}\\e[0m: no http client found"
fi
;;
esac
}
function eval_all_secrets() {
encoded_vars=$(env | grep -v '^scoped__' | awk -F '=' '/^[a-zA-Z0-9_]*=@(b64|hex|url)@/ {print $1}')
for var in $encoded_vars
do
eval_secret "$var"
done
}
function guess_node_manager_system() { function guess_node_manager_system() {
case "${NODE_MANAGER:-auto}" in case "${NODE_MANAGER:-auto}" in
auto) auto)
...@@ -465,6 +538,7 @@ stages: ...@@ -465,6 +538,7 @@ stages:
} }
unscope_variables unscope_variables
eval_all_secrets
# ENDSCRIPT # ENDSCRIPT
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment