fix(01-11): remove every shipped Fedimint gateway credential (FED-07)

Six code paths configured the Lightning gateway with a bcrypt hash committed
to this repository — and one deploy path with a plaintext password literal —
whenever the per-install secret was missing. Anyone holding a copy of the repo
held the admin credential for every gateway that ever took a fallback.

container::secrets now owns the credential end to end: ensure_gateway_credential
(idempotent, delegates to ensure_one's bcrypt arm) and gateway_bcrypt_hash,
which returns Err when the secret is missing/empty and when the stored value is
on the KNOWN_DEFAULT_GATEWAY_HASHES denylist — so this codebase cannot hand
back the compromised value even to a node already carrying it.

get_app_config was widened to Result so a credential-less install cannot reach
podman run at all; configure_fedimint_lnd takes the resolved hash instead of
re-reading with its own fallback. The four shell paths stop generating
credentials entirely (dropping the htpasswd host dependency) and skip container
creation with a printed reason rather than substituting anything.

Naming converges on the manifest's fedimint-gateway-hash/.pw, with legacy
fedimint-gateway-password values copied forward rather than regenerated so no
node loses a working unique credential. Plan 01-16 owns rotation of installs
already carrying the default.

Verified: cargo build clean; cargo test -p archipelago 999 passed (2
boot_reconciler timing tests failed under concurrent load, green in isolation,
untouched by this diff); bash -n clean on all five scripts; the compromised
literal now appears exactly once in the tree, as the denylist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-01 05:46:02 -04:00
co-authored by Claude Opus 5
parent 5faf1a3c5f
commit 4265254700
10 changed files with 558 additions and 139 deletions
+55 -40
View File
@@ -493,15 +493,17 @@ deploy_node() {
sudo chmod 600 "$SECRETS_DIR/${svc}-db-password"
fi
done
# Fedimint gateway
if [ ! -f "$SECRETS_DIR/fedimint-gateway-password" ]; then
FEDI_PASS=$(openssl rand -base64 16)
echo "$FEDI_PASS" | sudo tee "$SECRETS_DIR/fedimint-gateway-password" > /dev/null
sudo chmod 600 "$SECRETS_DIR/fedimint-gateway-password"
if command -v htpasswd >/dev/null 2>&1; then
htpasswd -bnBC 10 "" "$FEDI_PASS" | tr -d ":\n" | sudo tee "$SECRETS_DIR/fedimint-gateway-hash" > /dev/null
sudo chmod 600 "$SECRETS_DIR/fedimint-gateway-hash"
fi
# FED-07: no shipped fallback, ever. The canonical per-install gateway
# credential (fedimint-gateway-hash / .pw) is generated by the daemon
# via container::secrets::ensure_gateway_credential — this deploy script
# no longer generates it (removes the htpasswd host dependency too).
# Legacy migration only: carry an existing fedimint-gateway-password
# value forward to the canonical fedimint-gateway-hash.pw name if that
# name does not exist yet; never regenerate a working credential, and
# never delete the legacy file (plan 01-16 owns retirement).
if [ -f "$SECRETS_DIR/fedimint-gateway-password" ] && [ ! -f "$SECRETS_DIR/fedimint-gateway-hash.pw" ]; then
sudo cp "$SECRETS_DIR/fedimint-gateway-password" "$SECRETS_DIR/fedimint-gateway-hash.pw"
sudo chmod 600 "$SECRETS_DIR/fedimint-gateway-hash.pw"
fi
' 2>/dev/null
# Read each password individually (avoids eval on SSH output)
@@ -509,7 +511,12 @@ deploy_node() {
BTCPAY_DB_PASS=$(ssh $SSH_OPTS "$TARGET" 'sudo cat /var/lib/archipelago/secrets/btcpay-db-password 2>/dev/null' 2>/dev/null)
MYSQL_ROOT_PASS=$(ssh $SSH_OPTS "$TARGET" 'sudo cat /var/lib/archipelago/secrets/mysql-root-db-password 2>/dev/null' 2>/dev/null)
FEDI_HASH=$(ssh $SSH_OPTS "$TARGET" 'sudo cat /var/lib/archipelago/secrets/fedimint-gateway-hash 2>/dev/null' 2>/dev/null)
[ -z "${FEDI_HASH:-}" ] && FEDI_HASH='$2y$10$t9YjjxkiktrlYvjajB/zgOMDnSNVg4HqrbDqh47u7Jf42whNdxNqC'
# FED-07: no fallback literal. If the target has not generated its
# per-install gateway credential yet, FEDI_HASH stays empty and the
# gateway container creation below is skipped, never substituted.
if [ -z "${FEDI_HASH:-}" ]; then
echo " NOTE: no fedimint-gateway credential on target yet — gateway container creation will be skipped (no shipped default; the daemon generates one on next install/reconcile)"
fi
if [ -z "$BITCOIN_RPC_PASS" ]; then
echo " WARNING: Could not read Bitcoin RPC password — skipping container setup"
@@ -772,37 +779,45 @@ LNDCONF
fi
fi
if ! \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q fedimint-gateway; then
echo ' Creating fedimint-gateway...'
sudo mkdir -p /var/lib/archipelago/fedimint-gateway
FEDI_PASS=\$(sudo cat /var/lib/archipelago/secrets/fedimint-gateway-password 2>/dev/null || echo 'archipelago')
LND_CERT=/var/lib/archipelago/lnd/tls.cert
LND_MACAROON=/var/lib/archipelago/lnd/data/chain/bitcoin/mainnet/admin.macaroon
if \$DOCKER ps --format '{{.Names}}' | grep -q '^lnd\$' && sudo test -f \$LND_CERT && sudo test -f \$LND_MACAROON; then
\$DOCKER run -d --name fedimint-gateway --restart unless-stopped \$NET_OPT \
--health-cmd 'curl -sf http://localhost:8176/' --health-interval=30s --health-timeout=5s --health-retries=3 \
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 8176:8176 -v /var/lib/archipelago/fedimint-gateway:/data \
-v /var/lib/archipelago/lnd/tls.cert:/lnd/tls.cert:ro \
-v /var/lib/archipelago/lnd/data/chain/bitcoin/mainnet/admin.macaroon:/lnd/admin.macaroon:ro \
$FEDIMINT_GATEWAY_IMAGE \
gatewayd --data-dir /data --listen 0.0.0.0:8176 \
--password \"\$FEDI_PASS\" \
--network bitcoin --bitcoind-url http://\$TARGET_IP:8332 \
--bitcoind-username $BITCOIN_RPC_USER --bitcoind-password $BITCOIN_RPC_PASS \
lnd --lnd-rpc-host \$TARGET_IP:10009 --lnd-tls-cert /lnd/tls.cert --lnd-macaroon /lnd/admin.macaroon
# FED-07: the gateway is configured from the per-install bcrypt
# hash only — no shipped default, and no plaintext --password
# flag. If the target has no credential yet, skip creation and
# say why rather than starting a gateway with a known password.
GW_HASH=\$(sudo cat /var/lib/archipelago/secrets/fedimint-gateway-hash 2>/dev/null)
if [ -z \"\$GW_HASH\" ]; then
echo ' Skipping fedimint-gateway — no per-install credential on target yet (no shipped default; the daemon generates one on next install/reconcile)'
else
\$DOCKER run -d --name fedimint-gateway --restart unless-stopped \$NET_OPT \
--health-cmd 'curl -sf http://localhost:8176/' --health-interval=30s --health-timeout=5s --health-retries=3 \
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 8176:8176 -p 9737:9737 -v /var/lib/archipelago/fedimint-gateway:/data \
$FEDIMINT_GATEWAY_IMAGE \
gatewayd --data-dir /data --listen 0.0.0.0:8176 \
--password \"\$FEDI_PASS\" \
--network bitcoin --bitcoind-url http://\$TARGET_IP:8332 \
--bitcoind-username $BITCOIN_RPC_USER --bitcoind-password $BITCOIN_RPC_PASS \
ldk --ldk-lightning-port 9737 --ldk-alias archipelago-gateway
echo ' Creating fedimint-gateway...'
sudo mkdir -p /var/lib/archipelago/fedimint-gateway
LND_CERT=/var/lib/archipelago/lnd/tls.cert
LND_MACAROON=/var/lib/archipelago/lnd/data/chain/bitcoin/mainnet/admin.macaroon
if \$DOCKER ps --format '{{.Names}}' | grep -q '^lnd\$' && sudo test -f \$LND_CERT && sudo test -f \$LND_MACAROON; then
\$DOCKER run -d --name fedimint-gateway --restart unless-stopped \$NET_OPT \
--health-cmd 'curl -sf http://localhost:8176/' --health-interval=30s --health-timeout=5s --health-retries=3 \
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 8176:8176 -v /var/lib/archipelago/fedimint-gateway:/data \
-v /var/lib/archipelago/lnd/tls.cert:/lnd/tls.cert:ro \
-v /var/lib/archipelago/lnd/data/chain/bitcoin/mainnet/admin.macaroon:/lnd/admin.macaroon:ro \
$FEDIMINT_GATEWAY_IMAGE \
gatewayd --data-dir /data --listen 0.0.0.0:8176 \
--bcrypt-password-hash \"\$GW_HASH\" \
--network bitcoin --bitcoind-url http://\$TARGET_IP:8332 \
--bitcoind-username $BITCOIN_RPC_USER --bitcoind-password $BITCOIN_RPC_PASS \
lnd --lnd-rpc-host \$TARGET_IP:10009 --lnd-tls-cert /lnd/tls.cert --lnd-macaroon /lnd/admin.macaroon
else
\$DOCKER run -d --name fedimint-gateway --restart unless-stopped \$NET_OPT \
--health-cmd 'curl -sf http://localhost:8176/' --health-interval=30s --health-timeout=5s --health-retries=3 \
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
--security-opt no-new-privileges:true \
-p 8176:8176 -p 9737:9737 -v /var/lib/archipelago/fedimint-gateway:/data \
$FEDIMINT_GATEWAY_IMAGE \
gatewayd --data-dir /data --listen 0.0.0.0:8176 \
--bcrypt-password-hash \"\$GW_HASH\" \
--network bitcoin --bitcoind-url http://\$TARGET_IP:8332 \
--bitcoind-username $BITCOIN_RPC_USER --bitcoind-password $BITCOIN_RPC_PASS \
ldk --ldk-lightning-port 9737 --ldk-alias archipelago-gateway
fi
fi
fi