# HAProxy configuration for Taurus Production Environment # Managed by Ansible - Red Panda Approved # # SSL: Let's Encrypt certificate for helu.ca subdomains # HTTP backends: Casdoor (talos), Gitea (xenia), SearXNG (xenia) # TCP backend: Gitea SSH (xenia) global log /dev/log local0 log /dev/log local1 notice stats timeout 30s # Ubuntu systemd service handles user/group and daemonization # Default SSL material locations ca-base /etc/ssl/certs crt-base /etc/ssl/private # SSL/TLS configuration 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-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets defaults log global mode http option httplog option dontlognull # Log format with timing information for latency analysis log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r" timeout connect 5s timeout client 50s timeout server 50s # Stats page with Prometheus metrics listen stats bind *:{{ haproxy_stats_port }} mode http stats enable stats uri /metrics stats refresh 15s stats show-legends stats show-node # Prometheus metrics endpoint http-request use-service prometheus-exporter if { path /metrics } # HTTP to HTTPS redirect frontend http_frontend bind *:{{ haproxy_http_port }} mode http option httplog # Redirect all HTTP to HTTPS http-request redirect scheme https code 301 # HTTPS frontend with dynamic routing frontend https_frontend bind *:{{ haproxy_https_port }} ssl crt {{ haproxy_cert_path }} alpn h2,http/1.1 mode http option httplog option forwardfor # Security headers http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains" http-response set-header X-Frame-Options "SAMEORIGIN" http-response set-header X-Content-Type-Options "nosniff" http-response set-header X-XSS-Protection "1; mode=block" # ------------------------------------------------------------------------- # Rate limiting via stick-tables # ------------------------------------------------------------------------- # General rate limit: 1000 req/min per source IP stick-table type ip size 100k expire 1m store http_req_rate(1m) http-request track-sc0 src # Auth endpoint rate limit: 20 req/min per source IP acl is_auth_endpoint path_beg /api/login /api/signup /api/get-captcha /login/oauth/authorize /api/login/oauth/access_token acl host_id hdr_beg(host) -i id.{{ haproxy_domain }} # Use backend stick-table for auth endpoint tracking http-request track-sc1 src table st_casdoor_auth if host_id is_auth_endpoint # Deny if general rate exceeded http-request deny deny_status 429 if { sc_http_req_rate(0) gt 1000 } # Deny if auth endpoint rate exceeded http-request deny deny_status 429 if host_id is_auth_endpoint { sc_http_req_rate(1,st_casdoor_auth) gt 20 } {% for backend in haproxy_backends %} {% if backend.subdomain %} # ACL for {{ backend.subdomain }}.{{ haproxy_domain }} (matches with or without port) acl host_{{ backend.subdomain }} hdr_beg(host) -i {{ backend.subdomain }}.{{ haproxy_domain }} {% if backend.redirect_root is defined %} # Redirect root path to {{ backend.redirect_root }} (avoids redirect loop by matching exact path) http-request redirect location {{ backend.redirect_root }} code 302 if host_{{ backend.subdomain }} { path / } {% endif %} use_backend backend_{{ backend.subdomain }} if host_{{ backend.subdomain }} {% else %} # Default backend for root domain default_backend backend_root {% endif %} {% endfor %} # Backend definitions {% for backend in haproxy_backends %} {% if backend.subdomain %} backend backend_{{ backend.subdomain }} {% else %} backend backend_root {% endif %} mode http balance roundrobin option httpchk http-check send meth GET uri {{ backend.health_path }} ver HTTP/1.1 hdr Host {{ backend.health_host | default(backend.backend_host) }} http-check expect status 200 {% if backend.timeout_server is defined %} timeout server {{ backend.timeout_server }} {% endif %} server {{ backend.subdomain or 'root' }}_1 {{ backend.backend_host }}:{{ backend.backend_port }} check {% endfor %} # Stick-table for auth endpoint rate limiting (referenced by frontend) backend st_casdoor_auth stick-table type ip size 100k expire 1m store http_req_rate(1m) # ============================================================================= # TCP Frontends/Backends (non-HTTP protocols) # ============================================================================= {% for tcp_backend in haproxy_tcp_backends | default([]) %} # TCP passthrough: {{ tcp_backend.name }} frontend {{ tcp_backend.name }}_frontend bind *:{{ tcp_backend.listen_port }} mode tcp option tcplog timeout client 1h default_backend {{ tcp_backend.name }}_backend backend {{ tcp_backend.name }}_backend mode tcp option tcp-check timeout server 1h server {{ tcp_backend.name }}_1 {{ tcp_backend.backend_host }}:{{ tcp_backend.backend_port }} check {% endfor %}