- Add secubox-landing script to generate landing pages from HAProxy vhosts - Integrate landing command into secubox CLI - Add boot hook to regenerate landing pages on startup - Fix HAProxy multi-cert SNI using crt-list instead of directory mode - Fix backend IPs from 127.0.0.1 to 192.168.255.1 for LXC compatibility - Auto-convert localhost IPs in RPCD handler and CLI tools Landing page features: - Groups all services by zone with stats header - Shows SSL certificate status per domain - Categorizes by type: Streamlit, Blog, Admin, Media, Dev, etc. - Regenerates at boot (30s after startup) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
94 lines
2.9 KiB
Bash
94 lines
2.9 KiB
Bash
#!/bin/sh
|
|
# Sync ACME certificates to HAProxy format
|
|
# Combines fullchain + private key into .pem files
|
|
# Called by ACME renewal or manually via haproxyctl
|
|
|
|
ACME_DIR="/etc/acme"
|
|
HAPROXY_CERTS_DIR="/srv/haproxy/certs"
|
|
|
|
log_info() { echo "[haproxy-sync-certs] $*"; logger -t haproxy-sync-certs "$*"; }
|
|
log_error() { echo "[haproxy-sync-certs] ERROR: $*" >&2; logger -t haproxy-sync-certs -p err "$*"; }
|
|
|
|
mkdir -p "$HAPROXY_CERTS_DIR"
|
|
|
|
# Find all ACME certificates and deploy them
|
|
for domain_dir in "$ACME_DIR"/*/; do
|
|
[ -d "$domain_dir" ] || continue
|
|
|
|
# Skip non-domain directories
|
|
case "$(basename "$domain_dir")" in
|
|
ca|*.ecc) continue ;;
|
|
esac
|
|
|
|
domain=$(basename "$domain_dir")
|
|
fullchain="$domain_dir/fullchain.cer"
|
|
key="$domain_dir/${domain}.key"
|
|
|
|
# Try alternate paths
|
|
[ -f "$fullchain" ] || fullchain="$domain_dir/fullchain.pem"
|
|
[ -f "$key" ] || key="$domain_dir/privkey.pem"
|
|
[ -f "$key" ] || key="$domain_dir/${domain}.key"
|
|
|
|
if [ -f "$fullchain" ] && [ -f "$key" ]; then
|
|
log_info "Syncing certificate for $domain"
|
|
cat "$fullchain" "$key" > "$HAPROXY_CERTS_DIR/$domain.pem"
|
|
chmod 600 "$HAPROXY_CERTS_DIR/$domain.pem"
|
|
else
|
|
log_error "Missing cert or key for $domain (fullchain=$fullchain, key=$key)"
|
|
fi
|
|
done
|
|
|
|
log_info "Certificate sync complete"
|
|
|
|
# Generate certs.list for multi-certificate SNI support
|
|
CERTS_LIST="$HAPROXY_CERTS_DIR/certs.list"
|
|
CONTAINER_CERTS_PATH="/opt/haproxy/certs"
|
|
|
|
log_info "Generating certificate list file..."
|
|
> "$CERTS_LIST"
|
|
|
|
for cert_file in "$HAPROXY_CERTS_DIR"/*.pem; do
|
|
[ -f "$cert_file" ] || continue
|
|
|
|
filename=$(basename "$cert_file")
|
|
container_cert_path="$CONTAINER_CERTS_PATH/$filename"
|
|
|
|
# Extract domain names from certificate SANs
|
|
domains=$(openssl x509 -in "$cert_file" -noout -text 2>/dev/null | \
|
|
grep -A1 "Subject Alternative Name" | \
|
|
tail -n1 | \
|
|
sed 's/DNS://g' | \
|
|
tr ',' '\n' | \
|
|
tr -d ' ')
|
|
|
|
# If no SANs, try to get CN
|
|
if [ -z "$domains" ]; then
|
|
domains=$(openssl x509 -in "$cert_file" -noout -subject 2>/dev/null | \
|
|
sed -n 's/.*CN *= *\([^,/]*\).*/\1/p')
|
|
fi
|
|
|
|
if [ -n "$domains" ]; then
|
|
echo "$domains" | while read -r domain; do
|
|
[ -n "$domain" ] || continue
|
|
echo "$container_cert_path $domain" >> "$CERTS_LIST"
|
|
done
|
|
else
|
|
log_info "No domain found in certificate: $filename, adding without SNI filter"
|
|
echo "$container_cert_path" >> "$CERTS_LIST"
|
|
fi
|
|
done
|
|
|
|
# Deduplicate entries
|
|
if [ -f "$CERTS_LIST" ]; then
|
|
sort -u "$CERTS_LIST" > "$CERTS_LIST.tmp"
|
|
mv "$CERTS_LIST.tmp" "$CERTS_LIST"
|
|
count=$(wc -l < "$CERTS_LIST")
|
|
log_info "Generated certs.list with $count entries"
|
|
fi
|
|
|
|
# Reload HAProxy if running
|
|
if pgrep haproxy >/dev/null 2>&1 || lxc-info -n haproxy -s 2>/dev/null | grep -q RUNNING; then
|
|
log_info "Reloading HAProxy..."
|
|
/etc/init.d/haproxy reload 2>/dev/null || true
|
|
fi
|