Files
rustguac/haproxy.example.cfg
T
Dave Kempe 67101e27ce Initial public release
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 14:38:53 +11:00

132 lines
5.5 KiB
INI

# HAProxy example configuration for rustguac
#
# This config terminates TLS at HAProxy and proxies to rustguac on localhost.
# rustguac listens on 127.0.0.1:8089 with its own TLS (double encryption on
# loopback — belt and suspenders). Adjust "verify none" to "verify required
# ca-file /path/to/ca.pem" if using a real CA-signed cert for rustguac.
#
# WebSocket support requires tunnel timeout for long-lived connections.
#
# Knocknoc integration (https://knocknoc.io):
# knocknoc-agent dynamically adds/removes client IPs to HAProxy ACLs via
# the admin socket, providing zero-trust network access. Only users who
# have authenticated through Knocknoc can reach the login page.
# All other paths (OIDC flow, API, WebSocket, share links) pass through
# to rustguac which has its own auth layer.
global
log /dev/log local0
log /dev/log local1 notice
# Admin socket — knocknoc-agent uses this to manage dynamic ACLs.
# Ensure knocknoc-agent user is in the haproxy group:
# adduser knocknoc-agent haproxy
stats socket /run/haproxy/admin.sock mode 0660 level admin user haproxy group haproxy
user haproxy
group haproxy
daemon
# Modern TLS defaults
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-options ssl-min-ver TLSv1.2
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5s
timeout client 30s
timeout server 30s
# WebSocket/tunnel connections need a long timeout — sessions can last hours.
# This should be >= rustguac's session_max_duration_secs (default 8h).
timeout tunnel 8h
# Abort slow client sends (protects against slowloris)
timeout http-request 10s
timeout http-keep-alive 5s
frontend https
bind *:443 ssl crt /etc/ssl/private/rustguac.pem alpn h2,http/1.1
bind *:80
mode http
# Redirect HTTP → HTTPS
http-request redirect scheme https unless { ssl_fc }
# Forward client IP to rustguac (used by rate limiting and audit logging).
# Delete any incoming X-Forwarded-For first to prevent spoofing, then let
# HAProxy add the real client IP as a fresh single-valued header.
http-request del-header X-Forwarded-For
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request set-header X-Forwarded-Proto https if { ssl_fc }
#
# rustguac must trust HAProxy's source IP to honour X-Forwarded-For.
# Add this to your rustguac config.toml:
# trusted_proxies = ["127.0.0.1/32"]
# HSTS header (HAProxy layer — rustguac also sets this when TLS is enabled)
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains" if { ssl_fc }
# ── Knocknoc dynamic IP allowlist ────────────────────────────────────
#
# knocknoc-agent populates this ACL via the admin socket. The ACL ID (600)
# must match what you configure in Knocknoc admin (Knocs > Identity Aware
# Proxy > ACL ID). Pick any unused ID.
#
# Verify currently allowed IPs:
# echo "show acl #600" | socat stdio /run/haproxy/admin.sock
acl knoc_rustguac src -u 600
# ── Route classification ─────────────────────────────────────────────
# Match requests to this rustguac instance by hostname
acl is_rustguac hdr(host) -i console.example.com
# Internal/server IPs that always bypass Knocknoc.
# IMPORTANT: Include the server's own public IP if hairpin NAT is possible,
# and 127.0.0.0/8 for local health checks.
acl is_internal src 127.0.0.0/8
# Front page — gate behind Knocknoc to hide the login UI from scanners.
# Note: only gate `/` (the login page). Don't gate the API, OIDC flow,
# WebSocket, or share links — rustguac has its own auth for those, and
# gating them at HAProxy breaks OIDC callbacks and share link access.
acl is_root path /
# ── Routing rules ────────────────────────────────────────────────────
# Front page: require Knocknoc or internal IP
use_backend rustguac if is_rustguac is_root knoc_rustguac
use_backend rustguac if is_rustguac is_root is_internal
use_backend denied if is_rustguac is_root
# All other paths pass through to rustguac (has its own auth)
use_backend rustguac if is_rustguac
default_backend denied
backend rustguac
mode http
balance roundrobin
# Health check against the unauthenticated /api/health endpoint.
option httpchk GET /api/health
http-check expect status 200
# rustguac on loopback with TLS. Use "verify none" for self-signed certs,
# or "verify required ca-file /opt/rustguac/tls/cert.pem" for strict checking.
server rustguac 127.0.0.1:8089 ssl verify none check inter 30s
backend denied
mode http
# Custom 403 page. The .http file includes full HTTP headers + HTML body.
errorfile 403 /etc/haproxy/errors/403.http
http-request deny deny_status 403