mirror of
https://github.com/sol1/rustguac.git
synced 2026-09-10 17:47:13 +00:00
67101e27ce
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
132 lines
5.5 KiB
INI
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
|