feat(haproxy): Add webroot mode for ACME - no HAProxy restart needed
Certificate issuance now uses webroot mode instead of standalone: - HAProxy routes /.well-known/acme-challenge/ to local ACME webserver - Added acme_challenge backend on port 8402 - Uses busybox httpd to serve challenge files - No HAProxy restart required during certificate requests - Config auto-regenerates before cert request to ensure ACME backend This eliminates downtime during certificate issuance and allows multiple concurrent certificate requests. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
bc5bd8d8ce
commit
67cd2854a1
@ -409,6 +409,11 @@ _generate_frontends() {
|
||||
frontend http-in
|
||||
bind *:$http_port
|
||||
mode http
|
||||
|
||||
# ACME challenge routing (no HAProxy restart needed for cert issuance)
|
||||
acl is_acme_challenge path_beg /.well-known/acme-challenge/
|
||||
use_backend acme_challenge if is_acme_challenge
|
||||
|
||||
EOF
|
||||
|
||||
# Add HTTPS redirect rules for vhosts with ssl_redirect
|
||||
@ -484,6 +489,16 @@ _add_vhost_acl() {
|
||||
_generate_backends() {
|
||||
config_load haproxy
|
||||
|
||||
# ACME challenge backend (for certificate issuance without HAProxy restart)
|
||||
# Serves /.well-known/acme-challenge/ from /var/www/acme-challenge/
|
||||
cat << EOF
|
||||
|
||||
backend acme_challenge
|
||||
mode http
|
||||
server acme_webroot 192.168.255.1:8402 check
|
||||
|
||||
EOF
|
||||
|
||||
# Track which backends are generated
|
||||
_generated_backends=""
|
||||
|
||||
@ -770,6 +785,10 @@ cmd_cert_add() {
|
||||
local domain="$1"
|
||||
[ -z "$domain" ] && { log_error "Domain required"; return 1; }
|
||||
|
||||
# Ensure HAProxy config has ACME backend (for webroot mode)
|
||||
log_info "Ensuring HAProxy config is up to date..."
|
||||
generate_config
|
||||
|
||||
local email=$(uci_get acme.email)
|
||||
local staging=$(uci_get acme.staging)
|
||||
local key_type_raw=$(uci_get acme.key_type) || key_type_raw="ec-256"
|
||||
@ -820,21 +839,34 @@ cmd_cert_add() {
|
||||
"$ACME_SH" --register-account -m "$email" --server letsencrypt $staging_flag --home "$LE_WORKING_DIR" || true
|
||||
fi
|
||||
|
||||
# Check if HAProxy is using the port
|
||||
local haproxy_was_running=0
|
||||
if lxc_running; then
|
||||
log_info "Temporarily stopping HAProxy for certificate issuance..."
|
||||
haproxy_was_running=1
|
||||
/etc/init.d/haproxy stop 2>/dev/null || true
|
||||
# Setup webroot for ACME challenges (HAProxy routes /.well-known/acme-challenge/ here)
|
||||
local ACME_WEBROOT="/var/www/acme-challenge"
|
||||
ensure_dir "$ACME_WEBROOT/.well-known/acme-challenge"
|
||||
chmod 755 "$ACME_WEBROOT" "$ACME_WEBROOT/.well-known" "$ACME_WEBROOT/.well-known/acme-challenge"
|
||||
|
||||
# Start simple webserver for ACME challenges (if not already running)
|
||||
local ACME_PORT=8402
|
||||
if ! netstat -tln 2>/dev/null | grep -q ":$ACME_PORT "; then
|
||||
log_info "Starting ACME challenge webserver on port $ACME_PORT..."
|
||||
# Use busybox httpd (available on OpenWrt)
|
||||
start-stop-daemon -S -b -x /usr/sbin/httpd -- -p $ACME_PORT -h "$ACME_WEBROOT" -f 2>/dev/null || \
|
||||
busybox httpd -p $ACME_PORT -h "$ACME_WEBROOT" &
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# Ensure HAProxy is running with ACME backend
|
||||
if ! lxc_running; then
|
||||
log_info "Starting HAProxy..."
|
||||
/etc/init.d/haproxy start 2>/dev/null || true
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# Issue certificate using standalone mode
|
||||
log_info "Issuing certificate (standalone mode on port $http_port)..."
|
||||
# Issue certificate using webroot mode (NO HAProxy restart needed!)
|
||||
log_info "Issuing certificate (webroot mode - HAProxy stays running)..."
|
||||
local acme_result=0
|
||||
"$ACME_SH" --issue -d "$domain" \
|
||||
--server letsencrypt \
|
||||
--standalone --httpport "$http_port" \
|
||||
--webroot "$ACME_WEBROOT" \
|
||||
--keylength "$key_type" \
|
||||
$staging_flag \
|
||||
--home "$LE_WORKING_DIR" || acme_result=$?
|
||||
@ -856,19 +888,19 @@ cmd_cert_add() {
|
||||
chmod 600 "$CERTS_PATH/$domain.pem"
|
||||
|
||||
# Clean up intermediate files - HAProxy only needs the .pem file
|
||||
# Keeping these causes issues when HAProxy loads certs from directory
|
||||
rm -f "$CERTS_PATH/$domain.crt" "$CERTS_PATH/$domain.key" "$CERTS_PATH/$domain.fullchain.pem" "$CERTS_PATH/$domain.crt.key" 2>/dev/null
|
||||
fi
|
||||
|
||||
# Restart HAProxy if it was running
|
||||
if [ "$haproxy_was_running" = "1" ]; then
|
||||
log_info "Restarting HAProxy..."
|
||||
/etc/init.d/haproxy start 2>/dev/null || true
|
||||
# Reload HAProxy to pick up new cert
|
||||
log_info "Reloading HAProxy to use new certificate..."
|
||||
/etc/init.d/haproxy reload 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Check if certificate was created
|
||||
if [ ! -f "$CERTS_PATH/$domain.pem" ]; then
|
||||
log_error "Certificate issuance failed. Ensure port $http_port is accessible from internet and domain points to this IP."
|
||||
log_error "Certificate issuance failed. Check:"
|
||||
log_error " 1. Domain $domain points to this server's public IP"
|
||||
log_error " 2. Port 80 is accessible from internet"
|
||||
log_error " 3. HAProxy is running with ACME backend (haproxyctl generate)"
|
||||
return 1
|
||||
fi
|
||||
log_info "Certificate ready: $CERTS_PATH/$domain.pem"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user