# HAProxy configuration for Ouranos Titania # Managed by Ansible - Red Panda Approved global log 127.0.0.1:{{ haproxy_syslog_port }} local0 stats timeout 30s # 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 frontend - redirect all traffic to HTTPS frontend http_frontend bind *:{{ haproxy_http_port }} mode http option httplog http-request redirect scheme https code 301 # HTTPS frontend with dynamic routing frontend https_frontend bind *:{{ haproxy_https_port }} ssl crt {{ haproxy_cert_path }} mode http option httplog option forwardfor # Forward original protocol and host for reverse-proxied services http-request set-header X-Forwarded-Proto https http-request set-header X-Forwarded-Port %[dst_port] # 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" {% 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 {% if backend.ssl_backend | default(false) %} option httpchk http-check send meth GET uri {{ backend.health_path }} hdr Host {{ backend.subdomain }}.{{ haproxy_domain }} {% else %} option httpchk GET {{ backend.health_path }} {% endif %} 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{% if backend.ssl_backend | default(false) %} ssl verify none{% endif %} {% endfor %} {% 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 default_backend {{ tcp_backend.name }}_backend backend {{ tcp_backend.name }}_backend mode tcp server {{ tcp_backend.name }}_1 {{ tcp_backend.backend_host }}:{{ tcp_backend.backend_port }} check {% endfor %}