fix(haproxy): Combine fullchain + key for HAProxy certificates
HAProxy requires certificate files to contain both the fullchain (cert + intermediate CA) and the private key concatenated together. Changes: - haproxyctl: Fix cert_add to create combined .pem files - haproxy-sync-certs: New script to sync ACME certs to HAProxy format - haproxy.sh: ACME deploy hook for HAProxy - init.d: Sync certs before starting HAProxy - Makefile: Install new scripts, add cron job for cert sync This fixes the "No Private Key found" error when HAProxy tries to load certificates that only contain the fullchain without the key. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
785ba9eb4c
commit
fed7bd43c1
@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=secubox-app-haproxy
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=13
|
||||
PKG_RELEASE:=14
|
||||
|
||||
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
|
||||
PKG_LICENSE:=MIT
|
||||
@ -50,11 +50,28 @@ define Package/secubox-app-haproxy/install
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) ./files/usr/sbin/haproxyctl $(1)/usr/sbin/haproxyctl
|
||||
$(INSTALL_BIN) ./files/usr/sbin/haproxy-sync-certs $(1)/usr/sbin/haproxy-sync-certs
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib/acme/deploy
|
||||
$(INSTALL_BIN) ./files/usr/lib/acme/deploy/haproxy.sh $(1)/usr/lib/acme/deploy/haproxy.sh
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/haproxy/templates
|
||||
$(INSTALL_DATA) ./files/usr/share/haproxy/templates/* $(1)/usr/share/haproxy/templates/
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/haproxy/certs
|
||||
|
||||
# Add cron job for certificate sync after ACME renewals
|
||||
$(INSTALL_DIR) $(1)/etc/cron.d
|
||||
echo "# Sync ACME certs to HAProxy after renewals" > $(1)/etc/cron.d/haproxy-certs
|
||||
echo "15 3 * * * root /usr/sbin/haproxy-sync-certs >/dev/null 2>&1" >> $(1)/etc/cron.d/haproxy-certs
|
||||
endef
|
||||
|
||||
define Package/secubox-app-haproxy/postinst
|
||||
#!/bin/sh
|
||||
[ -n "$${IPKG_INSTROOT}" ] && exit 0
|
||||
# Sync existing ACME certificates on install
|
||||
/usr/sbin/haproxy-sync-certs 2>/dev/null || true
|
||||
exit 0
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,secubox-app-haproxy))
|
||||
|
||||
@ -16,6 +16,9 @@ start_service() {
|
||||
|
||||
[ "$enabled" = "1" ] || return 0
|
||||
|
||||
# Sync ACME certificates to HAProxy format before starting
|
||||
/usr/sbin/haproxy-sync-certs 2>/dev/null || true
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG" service-run
|
||||
procd_set_param respawn 3600 5 0
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
#!/bin/sh
|
||||
# ACME deploy hook for HAProxy
|
||||
# Combines fullchain + private key into single .pem file
|
||||
# Usage: Called by acme.sh after certificate issuance/renewal
|
||||
|
||||
HAPROXY_CERTS_DIR="/srv/haproxy/certs"
|
||||
|
||||
# acme.sh passes these environment variables:
|
||||
# DOMAIN - the domain name
|
||||
# CERT_PATH - path to the domain certificate
|
||||
# KEY_PATH - path to the domain private key
|
||||
# CA_PATH - path to the intermediate CA certificate
|
||||
# FULLCHAIN_PATH - path to the full chain certificate
|
||||
# CERT_KEY_PATH - same as KEY_PATH
|
||||
|
||||
deploy() {
|
||||
local domain="$1"
|
||||
local key_path="$2"
|
||||
local cert_path="$3"
|
||||
local ca_path="$4"
|
||||
local fullchain_path="$5"
|
||||
|
||||
[ -z "$domain" ] && { echo "Error: domain required"; return 1; }
|
||||
|
||||
mkdir -p "$HAPROXY_CERTS_DIR"
|
||||
|
||||
# Use fullchain if available, otherwise use cert + ca
|
||||
local combined_cert=""
|
||||
if [ -n "$fullchain_path" ] && [ -f "$fullchain_path" ]; then
|
||||
combined_cert="$fullchain_path"
|
||||
elif [ -n "$cert_path" ] && [ -f "$cert_path" ]; then
|
||||
combined_cert="$cert_path"
|
||||
else
|
||||
echo "Error: No certificate file found for $domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$key_path" ] || [ ! -f "$key_path" ]; then
|
||||
echo "Error: No key file found for $domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Combine fullchain + private key for HAProxy
|
||||
echo "Deploying certificate for $domain to HAProxy..."
|
||||
cat "$combined_cert" "$key_path" > "$HAPROXY_CERTS_DIR/$domain.pem"
|
||||
chmod 600 "$HAPROXY_CERTS_DIR/$domain.pem"
|
||||
|
||||
echo "Certificate deployed: $HAPROXY_CERTS_DIR/$domain.pem"
|
||||
|
||||
# Reload HAProxy if running
|
||||
if [ -x /etc/init.d/haproxy ]; then
|
||||
/etc/init.d/haproxy reload 2>/dev/null || true
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Entry point for acme.sh deploy hook
|
||||
deploy "$Le_Domain" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"
|
||||
@ -0,0 +1,47 @@
|
||||
#!/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"
|
||||
|
||||
# Reload HAProxy if running
|
||||
if pgrep -x 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
|
||||
@ -630,8 +630,13 @@ cmd_cert_add() {
|
||||
--home "$LE_WORKING_DIR" \
|
||||
--cert-file "$CERTS_PATH/$domain.crt" \
|
||||
--key-file "$CERTS_PATH/$domain.key" \
|
||||
--fullchain-file "$CERTS_PATH/$domain.pem" \
|
||||
--fullchain-file "$CERTS_PATH/$domain.fullchain.pem" \
|
||||
--reloadcmd "/etc/init.d/haproxy reload" 2>/dev/null || true
|
||||
|
||||
# HAProxy needs combined file: fullchain + private key
|
||||
log_info "Creating combined PEM for HAProxy..."
|
||||
cat "$CERTS_PATH/$domain.fullchain.pem" "$CERTS_PATH/$domain.key" > "$CERTS_PATH/$domain.pem"
|
||||
chmod 600 "$CERTS_PATH/$domain.pem"
|
||||
fi
|
||||
|
||||
# Restart HAProxy if it was running
|
||||
|
||||
Loading…
Reference in New Issue
Block a user