fix(apps): stabilize btcpay and public proxy launch flows
This commit is contained in:
@@ -510,6 +510,22 @@ fix_missing_rootless_ports() {
|
||||
$fixed && return 0 || return 1
|
||||
}
|
||||
|
||||
# ── Fix 11: Nginx Proxy Manager public host bridge ───────────
|
||||
# Host nginx owns public 80/443 on Archipelago. Mirror NPM proxy hosts into
|
||||
# host nginx so issued certs and public traffic reach the intended upstreams.
|
||||
fix_npm_public_hosts() {
|
||||
local script="/opt/archipelago/scripts/sync-npm-public-hosts.sh"
|
||||
[ -x "$script" ] || script="$SCRIPT_DIR/sync-npm-public-hosts.sh"
|
||||
[ -x "$script" ] || return 1
|
||||
[ -f /var/lib/archipelago/nginx-proxy-manager/data/database.sqlite ] || return 1
|
||||
|
||||
if "$script" >/dev/null 2>&1; then
|
||||
log "Synced Nginx Proxy Manager public hosts into host nginx"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Main ─────────────────────────────────────────────────────
|
||||
|
||||
# If remote host provided, run via SSH
|
||||
@@ -539,6 +555,7 @@ run_fix "exit-127" fix_exit_127
|
||||
run_fix "netns-egress" fix_rootless_netns_egress
|
||||
run_fix "stopped-core" fix_stopped_core_containers
|
||||
run_fix "rootless-ports" fix_missing_rootless_ports
|
||||
run_fix "npm-public-hosts" fix_npm_public_hosts
|
||||
|
||||
echo ""
|
||||
if [ $FIXES_APPLIED -gt 0 ]; then
|
||||
|
||||
@@ -273,7 +273,7 @@ load_spec_btcpay-server() {
|
||||
SPEC_PORTS="23000:49392"
|
||||
SPEC_VOLUMES="/var/lib/archipelago/btcpay:/datadir"
|
||||
SPEC_MEMORY="$(mem_limit btcpay-server)"
|
||||
SPEC_HEALTH_CMD="curl -sf http://localhost:49392/ || exit 1"
|
||||
SPEC_HEALTH_CMD="bash -ec '</dev/tcp/127.0.0.1/49392'"
|
||||
SPEC_ENV="ASPNETCORE_URLS=http://0.0.0.0:49392 BTCPAY_PROTOCOL=http BTCPAY_HOST=$HOST_IP:23000 BTCPAY_CHAINS=btc BTCPAY_BTCEXPLORERURL=http://archy-nbxplorer:32838 BTCPAY_BTCRPCURL=http://bitcoin-knots:8332 BTCPAY_BTCRPCUSER=$BITCOIN_RPC_USER BTCPAY_BTCRPCPASSWORD=$BITCOIN_RPC_PASS BTCPAY_POSTGRES=Username=btcpay;Password=$BTCPAY_DB_PASS;Host=archy-btcpay-db;Port=5432;Database=btcpay"
|
||||
SPEC_TIER="2"
|
||||
SPEC_DATA_DIR="/var/lib/archipelago/btcpay"
|
||||
|
||||
@@ -625,7 +625,7 @@ deploy_node() {
|
||||
echo ' Creating btcpay-server...'
|
||||
sudo mkdir -p /var/lib/archipelago/btcpay
|
||||
\$DOCKER run -d --name btcpay-server --restart unless-stopped \$NET_OPT \
|
||||
--health-cmd 'curl -sf http://localhost:49392/' --health-interval=30s --health-timeout=10s --health-retries=3 \
|
||||
--health-cmd "bash -ec '</dev/tcp/127.0.0.1/49392'" --health-interval=30s --health-timeout=10s --health-retries=3 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
|
||||
--security-opt no-new-privileges:true \
|
||||
-p 23000:49392 -v /var/lib/archipelago/btcpay:/datadir \
|
||||
@@ -909,7 +909,8 @@ LNDCONF
|
||||
if \$DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qx nginx-proxy-manager; then
|
||||
\$DOCKER start nginx-proxy-manager 2>/dev/null || true
|
||||
else
|
||||
sudo mkdir -p /var/lib/archipelago/nginx-proxy-manager/data /var/lib/archipelago/nginx-proxy-manager/letsencrypt
|
||||
sudo mkdir -p /var/lib/archipelago/nginx-proxy-manager/data/letsencrypt-acme-challenge/.well-known/acme-challenge /var/lib/archipelago/nginx-proxy-manager/letsencrypt
|
||||
sudo chown -R 1000:1000 /var/lib/archipelago/nginx-proxy-manager 2>/dev/null || true
|
||||
\$DOCKER run -d --name nginx-proxy-manager --restart unless-stopped \
|
||||
--health-cmd 'curl -sf http://localhost:81/' --health-interval=30s --health-timeout=5s --health-retries=3 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add NET_BIND_SERVICE \
|
||||
|
||||
@@ -481,10 +481,13 @@ log "archy-net network ready"
|
||||
log "Fixing rootless podman UID mapping..."
|
||||
# Containers running as root (UID 0 → host UID 100000)
|
||||
for dir in lnd electrumx btcpay nbxplorer jellyfin vaultwarden \
|
||||
home-assistant fedimint fedimint-gateway photoprism ollama filebrowser \
|
||||
nextcloud uptime-kuma nginx-proxy-manager portainer nostr-rs-relay; do
|
||||
home-assistant fedimint fedimint-gateway photoprism ollama filebrowser \
|
||||
nextcloud uptime-kuma portainer nostr-rs-relay; do
|
||||
[ -d "/var/lib/archipelago/$dir" ] && chown -R 100000:100000 "/var/lib/archipelago/$dir" 2>/dev/null
|
||||
done
|
||||
# Nginx Proxy Manager runs as root in the rootless user namespace, which maps to
|
||||
# the archipelago user on host bind mounts. Keep certbot's webroot writable.
|
||||
[ -d /var/lib/archipelago/nginx-proxy-manager ] && chown -R 1000:1000 /var/lib/archipelago/nginx-proxy-manager 2>/dev/null
|
||||
# Bitcoin Knots: container UID 101 → host UID 100101
|
||||
[ -d /var/lib/archipelago/bitcoin ] && chown -R 100101:100101 /var/lib/archipelago/bitcoin 2>/dev/null
|
||||
# Postgres: container UID 70 → host UID 100070
|
||||
@@ -875,7 +878,7 @@ if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q btcpay-server; then
|
||||
log "Creating BTCPay Server..."
|
||||
mkdir -p /var/lib/archipelago/btcpay/Main
|
||||
$DOCKER run -d --name btcpay-server --restart unless-stopped \
|
||||
--health-cmd="curl -sf http://localhost:49392/ || exit 1" --health-interval=120s --health-timeout=5s --health-retries=3 \
|
||||
--health-cmd="bash -ec '</dev/tcp/127.0.0.1/49392'" --health-interval=120s --health-timeout=5s --health-retries=3 \
|
||||
--memory=$(mem_limit btcpay-server) --network archy-net --network-alias btcpay-server \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
|
||||
--security-opt no-new-privileges:true \
|
||||
@@ -1181,6 +1184,8 @@ track_container "filebrowser"
|
||||
if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q nginx-proxy-manager; then
|
||||
log "Creating Nginx Proxy Manager..."
|
||||
mkdir -p /var/lib/archipelago/nginx-proxy-manager/data /var/lib/archipelago/nginx-proxy-manager/letsencrypt
|
||||
mkdir -p /var/lib/archipelago/nginx-proxy-manager/data/letsencrypt-acme-challenge/.well-known/acme-challenge
|
||||
chown -R 1000:1000 /var/lib/archipelago/nginx-proxy-manager 2>/dev/null || true
|
||||
NPM_ADMIN_PORT=$(alloc_port nginx-proxy-manager 8081)
|
||||
NPM_HTTP_PORT=$(alloc_port nginx-proxy-manager-http 8084)
|
||||
NPM_HTTPS_PORT=$(alloc_port nginx-proxy-manager-https 8444)
|
||||
|
||||
@@ -24,7 +24,7 @@ MEMPOOL_WEB_IMAGE="$ARCHY_REGISTRY/mempool-frontend:v3.0.0"
|
||||
MARIADB_IMAGE="$ARCHY_REGISTRY/mariadb:11.4.10"
|
||||
|
||||
# BTCPay
|
||||
BTCPAY_IMAGE="$ARCHY_REGISTRY/btcpayserver:1.13.7"
|
||||
BTCPAY_IMAGE="docker.io/btcpayserver/btcpayserver:2.3.9"
|
||||
NBXPLORER_IMAGE="$ARCHY_REGISTRY/nbxplorer:2.6.0"
|
||||
POSTGRES_IMAGE="$ARCHY_REGISTRY/postgres:15.17"
|
||||
BTCPAY_POSTGRES_IMAGE="$ARCHY_REGISTRY/postgres:15.17"
|
||||
|
||||
@@ -186,7 +186,7 @@ fi
|
||||
# for backward compatibility with older binaries that still look there.
|
||||
SCRIPTS_DEST="/opt/archipelago/scripts"
|
||||
sudo mkdir -p "$SCRIPTS_DEST"
|
||||
for script in image-versions.sh reconcile-containers.sh container-specs.sh container-doctor.sh app-surface-smoke-test.sh bitcoin-stack-lifecycle-test.sh; do
|
||||
for script in image-versions.sh reconcile-containers.sh container-specs.sh container-doctor.sh sync-npm-public-hosts.sh app-surface-smoke-test.sh bitcoin-stack-lifecycle-test.sh; do
|
||||
src="$REPO_DIR/scripts/$script"
|
||||
if [ -f "$src" ]; then
|
||||
sudo install -m 755 "$src" "$SCRIPTS_DEST/$script"
|
||||
|
||||
119
scripts/sync-npm-public-hosts.sh
Normal file
119
scripts/sync-npm-public-hosts.sh
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
DB="/var/lib/archipelago/nginx-proxy-manager/data/database.sqlite"
|
||||
OUT="/etc/nginx/conf.d/public-npm-proxy-hosts.conf"
|
||||
ACME_ROOT="/var/lib/archipelago/nginx-proxy-manager/data/letsencrypt-acme-challenge"
|
||||
LE_ROOT="/var/lib/archipelago/nginx-proxy-manager/letsencrypt/live"
|
||||
|
||||
[ -f "$DB" ] || exit 0
|
||||
mkdir -p "$ACME_ROOT/.well-known/acme-challenge"
|
||||
chown -R 1000:1000 /var/lib/archipelago/nginx-proxy-manager 2>/dev/null || true
|
||||
|
||||
tmp=$(mktemp)
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
|
||||
python3 - "$DB" "$ACME_ROOT" "$LE_ROOT" >"$tmp" <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
|
||||
db, acme_root, le_root = sys.argv[1:]
|
||||
con = sqlite3.connect(db)
|
||||
con.row_factory = sqlite3.Row
|
||||
|
||||
rows = con.execute(
|
||||
"""
|
||||
select p.id, p.domain_names, p.forward_scheme, p.forward_host, p.forward_port,
|
||||
p.certificate_id, p.ssl_forced, c.provider
|
||||
from proxy_host p
|
||||
left join certificate c on c.id = p.certificate_id
|
||||
where p.enabled = 1 and p.certificate_id > 0
|
||||
order by p.id
|
||||
"""
|
||||
).fetchall()
|
||||
|
||||
print("# Generated by sync-npm-public-hosts.sh; do not edit by hand.")
|
||||
for row in rows:
|
||||
try:
|
||||
domains = [d for d in json.loads(row["domain_names"] or "[]") if d]
|
||||
except Exception:
|
||||
domains = []
|
||||
if not domains:
|
||||
continue
|
||||
cert_id = row["certificate_id"]
|
||||
cert = f"{le_root}/npm-{cert_id}/fullchain.pem"
|
||||
key = f"{le_root}/npm-{cert_id}/privkey.pem"
|
||||
if row["provider"] != "letsencrypt":
|
||||
continue
|
||||
if not os.path.isfile(cert) or not os.path.isfile(key):
|
||||
continue
|
||||
names = " ".join(domains)
|
||||
scheme = row["forward_scheme"] or "http"
|
||||
host = row["forward_host"]
|
||||
port = row["forward_port"]
|
||||
if not host or not port:
|
||||
continue
|
||||
# NPM containers use this name to reach host-published services; host nginx
|
||||
# itself should use loopback for the same services.
|
||||
nginx_host = "127.0.0.1" if host == "host.containers.internal" else host
|
||||
|
||||
print(f"""
|
||||
server {{
|
||||
listen 80;
|
||||
server_name {names};
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {{
|
||||
default_type text/plain;
|
||||
root {acme_root};
|
||||
try_files $uri =404;
|
||||
}}
|
||||
|
||||
location / {{
|
||||
return 301 https://$host$request_uri;
|
||||
}}
|
||||
}}
|
||||
|
||||
server {{
|
||||
listen 443 ssl;
|
||||
server_name {names};
|
||||
ssl_certificate {cert};
|
||||
ssl_certificate_key {key};
|
||||
|
||||
location / {{
|
||||
proxy_pass {scheme}://{nginx_host}:{port};
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header X-Forwarded-Scheme https;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}}
|
||||
}}
|
||||
""")
|
||||
PY
|
||||
|
||||
backup=""
|
||||
if [ -f "$OUT" ]; then
|
||||
backup=$(mktemp)
|
||||
cp "$OUT" "$backup"
|
||||
fi
|
||||
|
||||
restore_previous() {
|
||||
if [ -n "$backup" ] && [ -f "$backup" ]; then
|
||||
install -m 0644 "$backup" "$OUT"
|
||||
else
|
||||
rm -f "$OUT"
|
||||
fi
|
||||
}
|
||||
|
||||
if ! install -m 0644 "$tmp" "$OUT" || ! nginx -t >/dev/null; then
|
||||
restore_previous
|
||||
nginx -t >/dev/null 2>&1 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
systemctl reload nginx
|
||||
Reference in New Issue
Block a user