#!/usr/bin/env bash # Summary: # - Scans all first-level subdirectories in the current working directory (each treated as a "service"). # - If a service contains .//secrets.sops.yaml, decrypts it with sops. # - Splits the decrypted YAML into individual key/value pairs and writes each value to a separate file: # /run/secrets// # - Output matches Docker "secrets" convention: one file per secret # Assumptions: # - .//secrets.sops.yaml is a flat, single-level key: value YAML map encrypted with sops (dotenv-like, but in YAML) # - sops, age and yq (mikefarah) are installed and in PATH # - The script is executed from the "services root" directory: its direct children are service directories (e.g. ./immich/, ./caddy/, ...) # - For systemd, set WorkingDirectory to this directory. set -euo pipefail umask 077 # 0600 by default OUT_ROOT="/run/secrets" SECRETS_GROUP="apps" # docker container runs under apps user DIR_MODE="0750" FILE_MODE="0640" for service_dir in ./*/; do service_name="${service_dir#./}" # remove leading './': './service/' -> 'service/' service_name="${service_name%/}" # remove trailing '/': 'service/' -> 'service' secrets_file="./${service_name}/secrets.sops.yaml" [[ -f "$secrets_file" ]] || continue out_dir="${OUT_ROOT}/${service_name}" install -d -m "$DIR_MODE" -o root -g "$SECRETS_GROUP" -- "$out_dir" sops -d "$secrets_file" \ | yq -r -0 'to_entries[] | [.key, .value] | .[]' \ | while IFS= read -r -d '' key && IFS= read -r -d '' value; do [[ "$key" =~ ^[A-Za-z0-9_][-A-Za-z0-9_]*$ ]] || { echo "skip bad key: $key" >&2; continue; } tmp_val="$(mktemp "${out_dir}/.${key}.XXXXXX")" printf '%s' "$value" > "$tmp_val" chown root:"$SECRETS_GROUP" "$tmp_val" chmod "$FILE_MODE" "$tmp_val" mv -f -- "$tmp_val" "${out_dir}/${key}" done echo "sops ok: ${service_name}" done