diff --git a/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl b/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl index d268c401..c8ded01d 100644 --- a/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl +++ b/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl @@ -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"