From d314069ca9191a8a448b3d395b7ced4bc2357b39 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 20 Jun 2026 23:18:57 -0700 Subject: [PATCH] Add create-secrets.sh helper for Docker Swarm secret provisioning --- deploy/create-secrets.sh | 76 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 deploy/create-secrets.sh diff --git a/deploy/create-secrets.sh b/deploy/create-secrets.sh new file mode 100644 index 0000000..240223f --- /dev/null +++ b/deploy/create-secrets.sh @@ -0,0 +1,76 @@ +#!/bin/bash +################################################################################ +# create-secrets.sh — Docker Swarm secret provisioning helper +# +# Usage: +# source deploy/create-secrets.sh +# create_or_update_secret "secret_name" "secret_value" +# +# What it does: +# - Creates a Docker secret if it does not already exist +# - Detects value changes via a SHA256 checksum stored as a secret label +# - If the value has changed: removes the old secret and recreates it +# - If the value is unchanged: skips silently (no service disruption) +# +# Important notes: +# - Docker Swarm has no native "update secret" API. Changing a secret +# requires removing and recreating it, then redeploying all services +# that reference it. This script handles removal + recreation, but the +# stack redeploy still happens via the normal deploy step. +# - Secrets are stored encrypted in Swarm's Raft consensus database and +# automatically replicated across all nodes. +# - The checksum label is NOT the secret value — it is a SHA256 hash used +# only to detect whether the value has changed, avoiding unnecessary +# secret rotation. +################################################################################ + +create_or_update_secret() { + local NAME="$1" + local VALUE="$2" + + if [ -z "$NAME" ]; then + echo " [ERROR] create_or_update_secret: NAME is required" + return 1 + fi + + if [ -z "$VALUE" ]; then + echo " [WARN] $NAME: value is empty, skipping" + return 0 + fi + + # Compute SHA256 of the new value (no trailing newline to keep hash stable) + local NEW_HASH + NEW_HASH=$(printf '%s' "$VALUE" | sha256sum | cut -d' ' -f1) + + if docker secret inspect "$NAME" >/dev/null 2>&1; then + # Secret exists — check if value has changed via stored checksum label + local OLD_HASH + OLD_HASH=$(docker secret inspect "$NAME" \ + --format '{{index .Spec.Labels "checksum"}}' 2>/dev/null || echo "") + + if [ "$NEW_HASH" = "$OLD_HASH" ]; then + echo " [SKIP] $NAME (unchanged)" + return 0 + fi + + # Value changed — must remove and recreate (Docker limitation) + echo " [UPDATE] $NAME (value changed, rotating)" + docker secret rm "$NAME" >/dev/null 2>&1 || { + echo " [ERROR] $NAME: failed to remove existing secret" + echo " Secret may be in use by a running service." + echo " Scale down the service first, then re-run." + return 1 + } + else + echo " [CREATE] $NAME" + fi + + # Create the secret with checksum label for future change detection + printf '%s' "$VALUE" | docker secret create \ + --label "checksum=${NEW_HASH}" \ + --label "managed-by=woodpecker" \ + "$NAME" - >/dev/null && echo " [OK] $NAME" || { + echo " [ERROR] $NAME: failed to create secret" + return 1 + } +}