Skip to content

Instantly share code, notes, and snippets.

@michiel
Created May 16, 2025 00:57
Show Gist options
  • Save michiel/16372fdd8ccc02fb9f452ff66c4d3f5d to your computer and use it in GitHub Desktop.
Save michiel/16372fdd8ccc02fb9f452ff66c4d3f5d to your computer and use it in GitHub Desktop.
Re-usable CodeFresh build step to access GCP Secrets Manager secrets
version: "1.0"
name: Secure Secret Run with GCP
description: |
Securely runs a user-provided shell script/command with a secret fetched from GCP Secret Manager.
The secret is provided directly to the script's standard input (stdin).
This step is designed to prevent the secret value from being exposed in environment variables,
Codefresh logs (by this step itself), or unintentionally written to disk *by this step*.
Security Considerations:
- Shared Responsibility: The ultimate security of the secret, once it is piped to the
`USER_COMMAND_SCRIPT`, depends entirely on how that script handles its stdin.
The provided script must be trusted and verified to handle sensitive data securely.
- USER_COMMAND_SCRIPT Execution: The `USER_COMMAND_SCRIPT` is executed using `bash -c "<script_content>"`.
This provides maximum flexibility but means the script operates with full shell capabilities
within the step's container. It is CRITICAL that the `USER_COMMAND_SCRIPT` itself does not
log the secret, export it to environment variables insecurely, or write it to disk.
- GCP Permissions: The Codefresh pipeline/environment running this step must have GCP
credentials configured with the necessary IAM permissions (specifically, `secretmanager.versions.access`)
for the specified secret in GCP Secret Manager.
- Codefresh Environment: Ensure that broad diagnostic settings like `set -x` (xtrace)
are not enabled at the pipeline or global Codefresh account level, as these could
inadvertently log expanded commands or, in worst-case scenarios with a compromised
`USER_COMMAND_SCRIPT`, parts of the secret.
- Secret Name Logging: The `SECRET_NAME` input parameter (the name of the secret, not its value)
is logged by this step for operational tracing. If secret names themselves are considered highly
sensitive, be aware of this logging.
type: step
tags:
- secrets
- gcp
- secure
- reusable
inputs:
SECRET_NAME:
type: string
required: true
description: |
The full resource name or the short name of the GCP secret to retrieve
(e.g., "my-api-key" or "projects/my-project-id/secrets/my-secret-name").
This name will be logged by the step during the fetching process for auditing and debugging.
USER_COMMAND_SCRIPT:
type: string
required: true
description: |
The shell script or command that will be executed. The fetched secret value from
GCP Secret Manager will be piped directly to this script's standard input (stdin).
The script is solely responsible for reading and securely handling this secret from stdin.
Example for Terraform: terraform apply -var "db_password=$(cat)"
Example for setting and unsetting an environment variable within the script:
export MY_TEMP_SECRET=$(cat); my_application --auth-token="$MY_TEMP_SECRET"; unset MY_TEMP_SECRET
IMPORTANT SECURITY WARNING: This script is executed via `bash -c "$USER_COMMAND_SCRIPT"`.
You MUST ensure that your script:
1. Correctly and securely reads the secret from stdin (e.g., using `cat`, `read`, or a tool's native ability to read from stdin).
2. Does NOT print the raw secret to its own stdout or stderr unless this output is being securely consumed by another trusted process (e.g., further piping). Accidental logging is a common leak vector.
3. Does NOT write the secret to any files on disk.
4. Does NOT export the secret into an environment variable that might persist longer than necessary or be inadvertently inherited and leaked by subprocesses. If temporary export is unavoidable, the variable MUST be unset immediately after use within the same script execution.
The overall security of the secret within the context of this `USER_COMMAND_SCRIPT` is YOUR responsibility.
steps:
run-with-secret:
title: Run user script with secret piped to stdin from GCP
image: google/cloud-sdk:slim # Using a minimal official image
shell: bash # Defines the shell for the commands block below
commands:
- |
# Strict mode: exit on error, error on unset variable, and fail pipeline if any command in a pipe fails
set -euo pipefail
# Validate inputs
if [ -z "${SECRET_NAME:-}" ]; then
echo "Error: SECRET_NAME input is required." >&2
exit 1
fi
if [ -z "${USER_COMMAND_SCRIPT:-}" ]; then
echo "Error: USER_COMMAND_SCRIPT input is required." >&2
exit 1
fi
echo "Attempting to fetch secret '$SECRET_NAME' from GCP Secret Manager..." >&2
# Function to fetch the secret with retries
# Output: Secret value to stdout on success. Logs to stderr.
# Returns: 0 on success, 1 on failure.
fetch_secret_from_gcp() {
local i
for i in {1..5}; do
# Attempt to fetch the secret.
# On success, gcloud prints the secret value to its stdout, which becomes this function's stdout.
# The --quiet flag minimizes gcloud's own chatter on stdout.
# Errors from gcloud will go to its stderr, which is inherited by this function's stderr.
if gcloud secrets versions access latest --secret="$SECRET_NAME" --quiet; then
return 0 # Secret successfully fetched and printed to stdout.
else
# gcloud command failed (exited with non-zero status).
if [ "$i" -lt 5 ]; then # Check if more retries are left
echo "Warning: Attempt $i to fetch secret '$SECRET_NAME' failed. Retrying in $((i * 2)) seconds..." >&2
sleep $((i * 2))
else
# This was the final attempt.
echo "Error: Attempt $i (final) to fetch secret '$SECRET_NAME' failed." >&2
fi
fi
done
# If the loop completes, all retries have failed.
echo "Error: All attempts to fetch secret '$SECRET_NAME' from GCP Secret Manager failed." >&2
return 1
}
# Execute the user's command, piping the secret from fetch_secret_from_gcp to its stdin.
# The < <(command) construct uses process substitution.
# If fetch_secret_from_gcp fails and returns a non-zero exit code (due to `return 1`),
# and `set -e` is active, the `bash -c` line should also fail, causing the step to fail.
# If fetch_secret_from_gcp fails to output the secret, USER_COMMAND_SCRIPT will receive empty or closed stdin.
bash -c "$USER_COMMAND_SCRIPT" < <(fetch_secret_from_gcp)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment