diff --git a/Dockerfile b/Dockerfile index 2d76021..8187ceb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,12 @@ FROM alpine:latest # Install only the tools we need: SOPS and GnuPG RUN apk add --no-cache sops gnupg +# Create the .gnupg directory with correct permissions first +RUN mkdir -m 700 /root/.gnupg + +# Copy our GPG agent config file into the image +COPY gpg.conf /root/.gnupg/gpg.conf + # Set a working directory WORKDIR /app diff --git a/entrypoint.sh b/entrypoint.sh index a058974..6344ea6 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,54 +1,66 @@ #!/bin/sh -# Exit immediately if any command fails set -e # --- Configuration (from Environment Variables) --- -# Use ':-' to set a default value if the variable is unset or null. APP_NAME="${APP_NAME:-sops-init}" GPG_KEY_PATH="${GPG_KEY_PATH:-/run/secrets/gpg_key}" - -# The directory containing encrypted files. +GPG_PASSPHRASE_PATH="${GPG_PASSPHRASE_PATH}" SOPS_INPUT_DIR="${SOPS_INPUT_DIR:-/config_in}" -# The directory where decrypted files will be written. SOPS_OUTPUT_DIR="${SOPS_OUTPUT_DIR:-/config_out}" -# The suffix of files to search for. -SOPS_FILE_SUFFIX="${SOPS_FILE_SUFFIX:-.sops.yaml}" - # --- Script Logic --- -echo "✅ $APP_NAME: Decryption container started." -echo "➡️ $APP_NAME: Input Dir: '$SOPS_INPUT_DIR', Output Dir: '$SOPS_OUTPUT_DIR', Suffix: '$SOPS_FILE_SUFFIX'" +echo "✅ $APP_NAME: Configuration materializer started." +echo "➡️ $APP_NAME: Input Dir: '$SOPS_INPUT_DIR', Output Dir: '$SOPS_OUTPUT_DIR'" + +# --- GPG Key Import and Unlocking --- +# (This section is unchanged as it is working correctly) +echo "🔄 $APP_NAME: Starting GPG Agent..." +unset GPG_AGENT_INFO +eval $(gpg-agent --daemon --pinentry-mode loopback) -# Validate that the required files/directories exist if [ ! -f "$GPG_KEY_PATH" ]; then echo "❌ $APP_NAME: ERROR: GPG secret key not found at '$GPG_KEY_PATH'" exit 1 fi - -if [ ! -d "$SOPS_INPUT_DIR" ]; then - echo "❌ $APP_NAME: ERROR: Input directory not found at '$SOPS_INPUT_DIR'" - exit 1 -fi - -# Ensure the output directory exists -mkdir -p "$SOPS_OUTPUT_DIR" - echo "🔐 $APP_NAME: Importing GPG private key..." gpg --batch --import "$GPG_KEY_PATH" -echo "🔎 $APP_NAME: Searching for files ending in '$SOPS_FILE_SUFFIX'..." +if [ -n "$GPG_PASSPHRASE_PATH" ]; then + if [ ! -f "$GPG_PASSPHRASE_PATH" ]; then + echo "❌ $APP_NAME: ERROR: GPG passphrase file not found at '$GPG_PASSPHRASE_PATH'" + exit 1 + fi + echo "🔑 $APP_NAME: Unlocking key to cache passphrase with GPG Agent..." + KEY_FINGERPRINT=$(gpg --with-colons --import-options import-show --import < "$GPG_KEY_PATH" | awk -F: '/^sec:/ { print $5 }') + if [ -z "$KEY_FINGERPRINT" ]; then + echo "❌ $APP_NAME: Could not determine GPG key fingerprint from key file." + exit 1 + fi + echo " - Unlocking key with fingerprint: $KEY_FINGERPRINT" + echo "test" | gpg --quiet --batch --pinentry-mode loopback --passphrase-file "$GPG_PASSPHRASE_PATH" --sign -u "$KEY_FINGERPRINT" > /dev/null + echo " - Key unlocked and passphrase cached successfully." +fi -# Use 'find' to locate all target files and loop through them -find "$SOPS_INPUT_DIR" -type f -name "*${SOPS_FILE_SUFFIX}" | while read -r encrypted_file; do - # Get the filename without the full path - base_filename_with_suffix=$(basename "$encrypted_file") - # Remove the suffix to get the clean output filename - output_filename=$(basename "$encrypted_file" "$SOPS_FILE_SUFFIX") - # Construct the full output path - decrypted_file="${SOPS_OUTPUT_DIR}/${output_filename}" - echo " - Decrypting '$base_filename_with_suffix' to '$decrypted_file'" - sops --decrypt "$encrypted_file" > "$decrypted_file" +# --- File Processing Loop --- +echo "🔎 $APP_NAME: Processing all files in '$SOPS_INPUT_DIR'..." +# Find ALL files (-type f) in the input directory +find "$SOPS_INPUT_DIR" -type f | while read -r source_file; do + relative_path="${source_file#$SOPS_INPUT_DIR/}" + destination_file="${SOPS_OUTPUT_DIR}/${relative_path}" + + # Ensure the destination directory exists + mkdir -p "$(dirname "$destination_file")" + + # Attempt to decrypt the file. Redirect stderr to /dev/null to keep logs clean. + if sops --decrypt "$source_file" > "$destination_file" 2>/dev/null; then + # If decryption succeeds, log it. + echo " decrypted ✅ $relative_path" + else + # If decryption fails, it's not a SOPS file. Copy it verbatim instead. + echo " copied ↪️ $relative_path" + cp "$source_file" "$destination_file" + fi done -echo "🎉 $APP_NAME: All files decrypted. Exiting." +echo "🎉 $APP_NAME: All files processed. Exiting." diff --git a/gpg.conf b/gpg.conf new file mode 100644 index 0000000..740fb39 --- /dev/null +++ b/gpg.conf @@ -0,0 +1,2 @@ +use-agent +pinentry-mode loopback