diff --git a/.icons/vault.svg b/.icons/vault.svg new file mode 100644 index 0000000..c90525c --- /dev/null +++ b/.icons/vault.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.images/vault-login.png b/.images/vault-login.png new file mode 100644 index 0000000..4e04036 Binary files /dev/null and b/.images/vault-login.png differ diff --git a/vault/README.md b/vault/README.md new file mode 100644 index 0000000..94f51bb --- /dev/null +++ b/vault/README.md @@ -0,0 +1,64 @@ +--- +display_name: vault +description: Authenticates with Vault and injects secrets into the environment. +icon: ../.icons/vault.svg +maintainer_github: coder +verified: true +tags: [helper, integration, vault] +--- + +# Hashicorp Vault + +This module authenticates with Vault and injects secrets into the environment. +> **Note:** This module does not cover setting up and configuring Vault. For that, see the [Vault documentation](https://www.vaultproject.io/docs). + +```hcl +module "vault" { + source = "https://registry.coder.com/modules/vault" + vault_addr = "https://vault.example.com" +} +``` + +![Vault login](./.images/vault-login.png) + +## Examples + +### Configure Vault integration with a custom Vault auth id + +See [docs](https://coder.com/docs/v2/latest/admin/external-auth) for more information what are external auth ids. + +```hcl +module "vault" { + source = "https://registry.coder.com/modules/vault" + vault_addr = "https://vault.example.com" + vault_auth_id = "my-auth-id" +} +``` + +### Configure Vault integration and automatically fetch secrets from Vault + +Configure Vault integration and automatically fetch secrets from Vault and inject them into the workspace environment. This works by specifying the `secrets` variable with a list of secrets paths and keys to fetch from Vault. Multiple secrets can be specified by using a map of secret paths to a list of keys to fetch from each secret. For more information, see the [Vault documentation](https://www.vaultproject.io/api-docs/secret/kv/kv-v2#read-secret-version). + +```hcl +For more information, see the [Vault documentation](https://www.vaultproject.io/docs/secrets/kv/kv-v2). + +```hcl +module "vault" { + source = "https://registry.coder.com/modules/vault" + vault_addr = "https://vault.example.com" + secrets = { + "secret/data/foo" = ["FOO", "BAR"] + "secret/data/bar" = ["BAZ"] + } +} +``` + +### Configure Vault integration and install a specific version of the Vault CLI + +```hcl +module "vault" { + source = "https://registry.coder.com/modules/vault" + vault_addr = "https://vault.example.com" + vault_cli_version = "1.15.0" +} +``` diff --git a/vault/main.tf b/vault/main.tf new file mode 100644 index 0000000..2872503 --- /dev/null +++ b/vault/main.tf @@ -0,0 +1,61 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.12" + } + } +} + +# Add required variables for your modules and remove any unneeded variables +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +variable "vault_addr" { + type = string + description = "The address of the Vault server." +} + +variable "vault_auth_id" { + type = string + description = "The ID of the Vault auth method to use." + default = "vault" +} + +variable "secrets" { + type = map(string) + description = "A map of secrets to set as environment variables." + default = {} +} + +variable "vault_cli_version" { + type = string + description = "The version of Vault to install." + default = "latest" + # validate the version is in the format 0.0.0 or latest + validation { + condition = can(regex("^(latest|[0-9]+\\.[0-9]+\\.[0-9]+)$", var.vault_version)) + error_message = "Vault version must be in the format 0.0.0 or latest" + } +} + +resource "coder_script" "vault" { + agent_id = var.agent_id + display_name = "vault" + icon = "/icon/vault.svg" + script = templatefile("${path.module}/run.sh", { + VAULT_ADDR : var.vault_addr, + VAULT_TOKEN : data.coder_external_auth.vault.access_token, + VERSION : var.vault_cli_version, + SECRETS : jsonencode(var.secrets), + }) + run_on_start = true +} + +data "coder_external_auth" "vault" { + id = var.vault_auth_id +} diff --git a/vault/run.sh b/vault/run.sh new file mode 100644 index 0000000..5ea9b2b --- /dev/null +++ b/vault/run.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env sh + +BOLD='\033[0;1m' + +# Check if vault is installed +if ! command -v vault &> /dev/null; then + printf "$${BOLD}Installing vault CLI ...\n\n" + # check if wget is installed + if ! command -v wget &> /dev/null; then + printf "wget is not installed. Please install wget in your image.\n" + exit 1 + fi + # check if unzip is installed + if ! command -v unzip &> /dev/null; then + printf "unzip is not installed. Please install unzip in your image.\n" + exit 1 + fi + # check if VERSION is latest + if [ "${VERSION}" = "latest" ]; then + INSTALL_VERSION=$(curl -s https://releases.hashicorp.com/vault/ | grep -oP '[0-9]+\.[0-9]+\.[0-9]' | tr -d '<>' | head -1) + else + INSTALL_VERSION=${VERSION} + fi + + # download vault + wget -O vault.zip https://releases.hashicorp.com/vault/$${INSTALL_VERSION}/vault_$${INSTALL_VERSION}_linux_amd64.zip + unzip vault.zip + sudo mv vault /usr/local/bin + rm vault.zip +fi + +printf "🄳 Installation comlete!\n\n" + +# Set up Vault address and token +export VAULT_ADDR=${VAULT_ADDR} +export VAULT_TOKEN=${VAULT_TOKEN} + +# Verify Vault address and token +printf "šŸ”Ž Verifying Vault address and token ...\n\n" +vault status + +# Set secrets if $SECRETS is set +if [ -z "${SECRETS}" ]; then + printf "\nšŸ”‘ No secrets to set ...\n\n" + exit 0 +fi + +printf "\nšŸ”‘ Fetching secrets ...\n\n" + +# Check if jq is installed +if ! command -v jq >/dev/null; then + echo "jq is not installed. Please install jq to automatically set the secrets." + exit 0 # exit with 0 to prevent failure (this is not a hard requirement, a user can still set the secrets manually) +fi + +# Decode the JSON string to a temporary file +echo "$SECRETS" | jq '.' > temp.json + +# Iterate through the keys and values in the JSON file +for key in $(jq -r 'keys[]' temp.json); do + path=$(echo $key | tr -d \") + # Fetch the secrets from Vault + secrets=$(vault kv get -format=json $path) + # Get the array of secret names from the JSON file + sceret_names=$(jq -r ".$key[]" temp.json) + # Convert the list of environment variables to an array + IFS=', ' read -r -a sceret_array <<< "$sceret_names" + # Set the environment variables with the secret values + for secret_name in "${sceret_array[@]}"; do + # Remove quotes from the variable name + secret_name=$(echo $secret_name | tr -d \") + # Assuming the secrets are stored in a key named 'data' in Vault + secret_value=$(echo $secrets | jq -r ".data.data.$secret_name") + export $secret_name=$secret_value + done +done + +# Remove the temporary file +rm temp.json + +