120 lines
3.1 KiB
Bash
120 lines
3.1 KiB
Bash
|
|
#!/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
|