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
|
frontend http-in
|
||||||
bind *:$http_port
|
bind *:$http_port
|
||||||
mode http
|
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
|
EOF
|
||||||
|
|
||||||
# Add HTTPS redirect rules for vhosts with ssl_redirect
|
# Add HTTPS redirect rules for vhosts with ssl_redirect
|
||||||
@ -484,6 +489,16 @@ _add_vhost_acl() {
|
|||||||
_generate_backends() {
|
_generate_backends() {
|
||||||
config_load haproxy
|
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
|
# Track which backends are generated
|
||||||
_generated_backends=""
|
_generated_backends=""
|
||||||
|
|
||||||
@ -770,6 +785,10 @@ cmd_cert_add() {
|
|||||||
local domain="$1"
|
local domain="$1"
|
||||||
[ -z "$domain" ] && { log_error "Domain required"; return 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 email=$(uci_get acme.email)
|
||||||
local staging=$(uci_get acme.staging)
|
local staging=$(uci_get acme.staging)
|
||||||
local key_type_raw=$(uci_get acme.key_type) || key_type_raw="ec-256"
|
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
|
"$ACME_SH" --register-account -m "$email" --server letsencrypt $staging_flag --home "$LE_WORKING_DIR" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if HAProxy is using the port
|
# Setup webroot for ACME challenges (HAProxy routes /.well-known/acme-challenge/ here)
|
||||||
local haproxy_was_running=0
|
local ACME_WEBROOT="/var/www/acme-challenge"
|
||||||
if lxc_running; then
|
ensure_dir "$ACME_WEBROOT/.well-known/acme-challenge"
|
||||||
log_info "Temporarily stopping HAProxy for certificate issuance..."
|
chmod 755 "$ACME_WEBROOT" "$ACME_WEBROOT/.well-known" "$ACME_WEBROOT/.well-known/acme-challenge"
|
||||||
haproxy_was_running=1
|
|
||||||
/etc/init.d/haproxy stop 2>/dev/null || true
|
# 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
|
sleep 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Issue certificate using standalone mode
|
# Issue certificate using webroot mode (NO HAProxy restart needed!)
|
||||||
log_info "Issuing certificate (standalone mode on port $http_port)..."
|
log_info "Issuing certificate (webroot mode - HAProxy stays running)..."
|
||||||
local acme_result=0
|
local acme_result=0
|
||||||
"$ACME_SH" --issue -d "$domain" \
|
"$ACME_SH" --issue -d "$domain" \
|
||||||
--server letsencrypt \
|
--server letsencrypt \
|
||||||
--standalone --httpport "$http_port" \
|
--webroot "$ACME_WEBROOT" \
|
||||||
--keylength "$key_type" \
|
--keylength "$key_type" \
|
||||||
$staging_flag \
|
$staging_flag \
|
||||||
--home "$LE_WORKING_DIR" || acme_result=$?
|
--home "$LE_WORKING_DIR" || acme_result=$?
|
||||||
@ -856,19 +888,19 @@ cmd_cert_add() {
|
|||||||
chmod 600 "$CERTS_PATH/$domain.pem"
|
chmod 600 "$CERTS_PATH/$domain.pem"
|
||||||
|
|
||||||
# Clean up intermediate files - HAProxy only needs the .pem file
|
# 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
|
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
|
# Reload HAProxy to pick up new cert
|
||||||
if [ "$haproxy_was_running" = "1" ]; then
|
log_info "Reloading HAProxy to use new certificate..."
|
||||||
log_info "Restarting HAProxy..."
|
/etc/init.d/haproxy reload 2>/dev/null || true
|
||||||
/etc/init.d/haproxy start 2>/dev/null || true
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if certificate was created
|
# Check if certificate was created
|
||||||
if [ ! -f "$CERTS_PATH/$domain.pem" ]; then
|
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
|
return 1
|
||||||
fi
|
fi
|
||||||
log_info "Certificate ready: $CERTS_PATH/$domain.pem"
|
log_info "Certificate ready: $CERTS_PATH/$domain.pem"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user