From e25891308df3c6ebf60014e516b8a6361357276e Mon Sep 17 00:00:00 2001 From: Floris van der Grinten Date: Thu, 20 May 2021 11:27:09 +0200 Subject: [PATCH] Create env-export action --- action.yml | 12 ++++++ entrypoint.sh | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 action.yml create mode 100755 entrypoint.sh diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..247ca13 --- /dev/null +++ b/action.yml @@ -0,0 +1,12 @@ +name: env-export-action +inputs: + unset-previous: + description: Whether to unset environment variables populated by 1Password in earlier job steps + default: false +runs: + using: composite + steps: + - run: | + export INPUT_UNSET_PREVIOUS=${{ inputs.unset-previous }} + ${{ github.action_path }}/entrypoint.sh + shell: bash diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..dbff759 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,108 @@ +#!/bin/bash +set -e + +managed_by_statement="Managed by 1Password" + +# Unset all secrets managed by 1Password if `unset-previous` is set. +if [ "$INPUT_UNSET_PREVIOUS" == "true" ]; then + echo "Unsetting previous values..." + + # Iterate over 'Managed by 1Password' comments in environment. + printenv | grep "$managed_by_statement" | while read -r comment; do + # Extract env var name and heredoc identifier from comment. + env_var=$(echo "$comment" | sed -e "s/.*$managed_by_statement: \(.*\)=.*/\1/") + + echo "Unsetting $env_var" + unset $env_var + + echo "$env_var=" >> $GITHUB_ENV + + # Keep the masks, just in case. + done +fi + +# Iterate over environment varables to find 1Password references, load the secret values, +# and make them available as environment variables in the next steps. +printenv | grep "=op://" | grep -v "^#" | while read -r possible_ref; do + env_var=$(echo "$possible_ref" | cut -d '=' -f1) + ref=$(printenv $env_var) + + if [[ ! $ref == "op://"* ]]; then + echo "Not really a reference: $ref" + continue + fi + + path=$(echo $ref | sed -e "s/^op:\/\///") + if [ $(echo "$path" | tr -cd '/' | wc -c) -lt 2 ]; then + echo "Expected path to be in format op:///[/
]/: $ref" + continue + fi + + echo "Populating variable: $env_var" + + vault="" + item="" + section="" + field="" + i=0 + IFS="/" + for component in $path; do + ((i+=1)) + case "$i" in + 1) vault=$component ;; + 2) item=$component ;; + 3) section=$component ;; + 4) field=$component ;; + esac + done + unset IFS + + # If field is not set, it may have wrongfully been interpreted as the section. + if [ -z "$field" ]; then + field="$section" + section="" + fi + + echo "Loading item $item from vault $vault..." + item_json=$(curl -sSf -H "Content-Type: application/json" -H "Authorization: Bearer $OP_CONNECT_TOKEN" "$OP_CONNECT_HOST/v1/vaults/$vault/items/$item") + jq_field_selector=".id == \"$field\" or .label == \"$field\"" + + # If the reference contains a section, edit the jq selector to take that into account. + if [ -n "$section" ]; then + echo "Looking for section: $section" + section_id=$(echo "$item_json" | jq -r ".sections[] | select(.id == \"$section\" or .label == \"$section\") | .id") + jq_field_selector=".section.id == \"$section_id\" and ($jq_field_selector)" + else + jq_field_selector=".section == null" + fi + + echo "Looking for field: $field" + secret_value=$(echo "$item_json" | jq -r "first(.fields[] | select($jq_field_selector) | .value)") + + # Register a mask for the secret to prevent accidental log exposure. + # To support multiline secrets, escape percent signs and add a mask per line. + escaped_mask_value=$(echo "$secret_value" | sed -e 's/%/%25/g') + IFS=$'\n' + for line in $escaped_mask_value; do + if [ "${#line}" -lt 3 ]; then + # To avoid false positives and unreadable logs, omit mask for lines that are too short. + continue + fi + echo "::add-mask::$line" + done + unset IFS + + # To support multiline secrets, we'll use the heredoc syntax to populate the environment variables. + # As the heredoc identifier, we'll use a randomly generated 64-character string, + # so that collisions are practically impossible. + random_heredoc_identifier=$(openssl rand -hex 16) + + { + # Add 'Managed by 1Password' comment, so in a later step it the secret can be unset again. + echo "# $managed_by_statement: $env_var=$ref" + # Populate env var, using heredoc syntax with generated identifier + echo "$env_var<<${random_heredoc_identifier}" + echo "$secret_value" + echo "${random_heredoc_identifier}" + } >> $GITHUB_ENV +done