refactor: remove HAProxy from Prospero, centralize TLS on Titania
Move TLS termination and reverse proxying entirely to Titania's HAProxy, eliminating the redundant HAProxy instance on Prospero. Backends now communicate over plain HTTP within the internal network. - Remove HAProxy container, config, certs, and syslog from Prospero - Remove ssl_backend flags from Titania backend definitions - Replace pplg_haproxy_* vars with single pplg_domain variable - Remove HAProxy syslog source from Alloy config - Update OAuth2-Proxy to listen on all interfaces for Titania access
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
---
|
||||
# PPLG - Consolidated Observability & Admin Stack for Prospero
|
||||
# PgAdmin, Prometheus, Loki, Grafana + HAProxy (TLS) + OAuth2-Proxy (Prometheus UI)
|
||||
# PgAdmin, Prometheus, Loki, Grafana + OAuth2-Proxy (Prometheus UI)
|
||||
# TLS termination handled by Titania HAProxy
|
||||
# Red Panda Approved
|
||||
|
||||
- name: Deploy PPLG Stack
|
||||
@@ -47,7 +48,6 @@
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- acl
|
||||
- haproxy
|
||||
- prometheus
|
||||
- loki
|
||||
- grafana
|
||||
@@ -372,83 +372,6 @@
|
||||
state: started
|
||||
daemon_reload: true
|
||||
|
||||
# ===========================================================================
|
||||
# SSL Certificate Distribution (from Titania)
|
||||
# ===========================================================================
|
||||
|
||||
- name: Create haproxy group
|
||||
ansible.builtin.group:
|
||||
name: "{{pplg_haproxy_group}}"
|
||||
gid: "{{pplg_haproxy_gid}}"
|
||||
system: true
|
||||
|
||||
- name: Create haproxy user
|
||||
ansible.builtin.user:
|
||||
name: "{{pplg_haproxy_user}}"
|
||||
comment: "PPLG HAProxy"
|
||||
group: "{{pplg_haproxy_group}}"
|
||||
uid: "{{pplg_haproxy_uid}}"
|
||||
system: true
|
||||
|
||||
- name: Create HAProxy directories
|
||||
ansible.builtin.file:
|
||||
path: "{{item}}"
|
||||
state: directory
|
||||
owner: "{{pplg_haproxy_user}}"
|
||||
group: "{{pplg_haproxy_group}}"
|
||||
mode: '750'
|
||||
loop:
|
||||
- /etc/haproxy
|
||||
- /etc/haproxy/certs
|
||||
|
||||
- name: Fetch wildcard certificate from Titania
|
||||
ansible.builtin.fetch:
|
||||
src: /etc/haproxy/certs/ouranos.pem
|
||||
dest: /tmp/ouranos-haproxy.pem
|
||||
flat: yes
|
||||
delegate_to: titania.incus
|
||||
when: "'titania.incus' in groups['ubuntu']"
|
||||
|
||||
- name: Deploy wildcard certificate
|
||||
ansible.builtin.copy:
|
||||
src: /tmp/ouranos-haproxy.pem
|
||||
dest: "{{pplg_haproxy_cert_path}}"
|
||||
owner: "{{pplg_haproxy_user}}"
|
||||
group: "{{pplg_haproxy_group}}"
|
||||
mode: '0640'
|
||||
when: "'titania.incus' in groups['ubuntu']"
|
||||
|
||||
- name: Generate self-signed wildcard certificate (fallback)
|
||||
command: >
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048
|
||||
-keyout {{pplg_haproxy_cert_path}}
|
||||
-out {{pplg_haproxy_cert_path}}
|
||||
-subj "/C=US/ST=State/L=City/O=Ouranos/CN=*.{{pplg_haproxy_domain}}"
|
||||
-addext "subjectAltName=DNS:*.{{pplg_haproxy_domain}},DNS:{{pplg_haproxy_domain}}"
|
||||
when: "'titania.incus' not in groups['ubuntu']"
|
||||
args:
|
||||
creates: "{{pplg_haproxy_cert_path}}"
|
||||
|
||||
# ===========================================================================
|
||||
# HAProxy (TLS Termination)
|
||||
# ===========================================================================
|
||||
|
||||
- name: Template HAProxy configuration
|
||||
ansible.builtin.template:
|
||||
src: pplg-haproxy.cfg.j2
|
||||
dest: /etc/haproxy/haproxy.cfg
|
||||
owner: "{{pplg_haproxy_user}}"
|
||||
group: "{{pplg_haproxy_group}}"
|
||||
mode: "640"
|
||||
validate: haproxy -c -f %s
|
||||
notify: restart haproxy
|
||||
|
||||
- name: Enable and start HAProxy service
|
||||
ansible.builtin.systemd:
|
||||
name: haproxy
|
||||
enabled: true
|
||||
state: started
|
||||
|
||||
# ===========================================================================
|
||||
# Handlers
|
||||
# ===========================================================================
|
||||
@@ -484,11 +407,6 @@
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: restart haproxy
|
||||
ansible.builtin.systemd:
|
||||
name: haproxy
|
||||
state: reloaded
|
||||
|
||||
- name: restart oauth2-proxy-prometheus
|
||||
ansible.builtin.systemd:
|
||||
name: oauth2-proxy-prometheus
|
||||
|
||||
@@ -10,7 +10,7 @@ client_id = "{{prometheus_oauth2_client_id}}"
|
||||
client_secret = "{{prometheus_oauth2_client_secret}}"
|
||||
|
||||
# Redirect URL after authentication
|
||||
redirect_url = "https://prometheus.{{pplg_haproxy_domain}}/oauth2/callback"
|
||||
redirect_url = "https://prometheus.{{pplg_domain}}/oauth2/callback"
|
||||
|
||||
# Upstream service (Prometheus)
|
||||
upstreams = [
|
||||
@@ -24,7 +24,7 @@ cookie_secure = true
|
||||
cookie_httponly = true
|
||||
cookie_expire = "168h"
|
||||
cookie_refresh = "1h"
|
||||
cookie_domains = ".{{pplg_haproxy_domain}}"
|
||||
cookie_domains = ".{{pplg_domain}}"
|
||||
session_store_type = "cookie"
|
||||
|
||||
# Authentication settings
|
||||
@@ -49,9 +49,10 @@ http_address = "0.0.0.0:{{prometheus_proxy_port}}"
|
||||
reverse_proxy = true
|
||||
real_client_ip_header = "X-Forwarded-For"
|
||||
|
||||
# Skip authentication for health check endpoints
|
||||
# Skip authentication for health check and machine-to-machine endpoints
|
||||
skip_auth_routes = [
|
||||
"^/ping$"
|
||||
"^/ping$",
|
||||
"^/api/v1/write$"
|
||||
]
|
||||
|
||||
# OIDC specific settings
|
||||
|
||||
@@ -9,7 +9,7 @@ User={{pgadmin_user}}
|
||||
Group={{pgadmin_group}}
|
||||
WorkingDirectory=/usr/pgadmin4/web
|
||||
ExecStart=/usr/pgadmin4/venv/bin/python3 -m gunicorn pgAdmin4:app \
|
||||
--bind 127.0.0.1:{{pgadmin_port}} \
|
||||
--bind 0.0.0.0:{{pgadmin_port}} \
|
||||
--workers 1 \
|
||||
--threads 4 \
|
||||
--timeout 120 \
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
# PPLG HAProxy - Internal TLS Termination for Prospero
|
||||
# Services: Grafana, PgAdmin, Prometheus (via OAuth2-Proxy), Loki, Alertmanager
|
||||
# Managed by Ansible - Red Panda Approved
|
||||
|
||||
global
|
||||
log 127.0.0.1:{{pplg_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 "%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 *:{{pplg_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 *:80
|
||||
mode http
|
||||
option httplog
|
||||
http-request redirect scheme https code 301
|
||||
|
||||
# HTTPS frontend with subdomain-based routing
|
||||
frontend https_frontend
|
||||
bind *:443 ssl crt {{pplg_haproxy_cert_path}}
|
||||
mode http
|
||||
option httplog
|
||||
option forwardfor
|
||||
|
||||
# Forward original protocol and host
|
||||
http-request set-header X-Forwarded-Proto https
|
||||
http-request set-header X-Forwarded-Port %[dst_port]
|
||||
http-request set-header X-Forwarded-Host %[req.hdr(Host)]
|
||||
|
||||
# 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"
|
||||
|
||||
# Subdomain ACLs
|
||||
acl host_grafana hdr_beg(host) -i grafana.{{pplg_haproxy_domain}}
|
||||
acl host_pgadmin hdr_beg(host) -i pgadmin.{{pplg_haproxy_domain}}
|
||||
acl host_prometheus hdr_beg(host) -i prometheus.{{pplg_haproxy_domain}}
|
||||
acl host_loki hdr_beg(host) -i loki.{{pplg_haproxy_domain}}
|
||||
acl host_alertmanager hdr_beg(host) -i alertmanager.{{pplg_haproxy_domain}}
|
||||
|
||||
# Prometheus write API - bypass OAuth2-Proxy (machine-to-machine)
|
||||
acl is_prometheus_write path_beg /api/v1/write
|
||||
|
||||
use_backend backend_grafana if host_grafana
|
||||
use_backend backend_pgadmin if host_pgadmin
|
||||
use_backend backend_prometheus_direct if host_prometheus is_prometheus_write
|
||||
use_backend backend_prometheus if host_prometheus
|
||||
use_backend backend_loki if host_loki
|
||||
use_backend backend_alertmanager if host_alertmanager
|
||||
|
||||
# Grafana - Native Casdoor OAuth SSO
|
||||
backend backend_grafana
|
||||
mode http
|
||||
balance roundrobin
|
||||
option httpchk GET /api/health
|
||||
http-check expect status 200
|
||||
server grafana_1 127.0.0.1:3000 check
|
||||
|
||||
# PgAdmin - Native Casdoor OAuth SSO
|
||||
backend backend_pgadmin
|
||||
mode http
|
||||
balance roundrobin
|
||||
option httpchk GET /misc/ping
|
||||
http-check expect status 200
|
||||
server pgadmin_1 127.0.0.1:{{pgadmin_port}} check
|
||||
|
||||
# Prometheus UI - via OAuth2-Proxy sidecar
|
||||
backend backend_prometheus
|
||||
mode http
|
||||
balance roundrobin
|
||||
option httpchk GET /ping
|
||||
http-check expect status 200
|
||||
server prometheus_1 127.0.0.1:{{prometheus_proxy_port}} check
|
||||
|
||||
# Prometheus Write API - direct (no auth, machine-to-machine)
|
||||
backend backend_prometheus_direct
|
||||
mode http
|
||||
balance roundrobin
|
||||
server prometheus_write_1 127.0.0.1:9090 check
|
||||
|
||||
# Loki - no auth (machine-to-machine log ingestion)
|
||||
backend backend_loki
|
||||
mode http
|
||||
balance roundrobin
|
||||
option httpchk GET /ready
|
||||
http-check expect status 200
|
||||
server loki_1 127.0.0.1:{{loki_port}} check
|
||||
|
||||
# Alertmanager - internal only
|
||||
backend backend_alertmanager
|
||||
mode http
|
||||
balance roundrobin
|
||||
option httpchk GET /-/healthy
|
||||
http-check expect status 200
|
||||
server alertmanager_1 127.0.0.1:{{alertmanager_port}} check
|
||||
Reference in New Issue
Block a user