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:
2026-04-08 17:57:09 +00:00
parent df1ee5e778
commit 0f21380fd0
8 changed files with 56 additions and 354 deletions

View File

@@ -1,6 +1,6 @@
// Prospero Alloy Configuration
// Red Panda Approved 🐼
// Services: PPLG stack (Grafana, Prometheus, Loki, Alertmanager, PgAdmin, HAProxy, OAuth2-Proxy)
// Services: PPLG stack (Grafana, Prometheus, Loki, Alertmanager, PgAdmin, OAuth2-Proxy)
logging {
level = "{{alloy_log_level}}"
@@ -19,20 +19,6 @@ loki.source.file "system_logs" {
forward_to = [loki.write.default.receiver]
}
// PPLG HAProxy syslog receiver (HAProxy syslog → Alloy → Loki)
loki.source.syslog "pplg_haproxy" {
listener {
address = "127.0.0.1:{{pplg_haproxy_syslog_port}}"
protocol = "tcp"
labels = {
job = "pplg-haproxy",
hostname = "{{inventory_hostname}}",
environment = "{{deployment_environment}}",
}
}
forward_to = [loki.write.default.receiver]
}
// Journal relabeling - assign dedicated job labels per systemd unit
loki.relabel "journal" {
forward_to = []

View File

@@ -1,6 +1,6 @@
---
# Prospero Configuration - PPLG Observability & Admin Stack
# Services: pplg (PgAdmin, Prometheus, Loki, Grafana + HAProxy + OAuth2-Proxy)
# Services: pplg (PgAdmin, Prometheus, Loki, Grafana + OAuth2-Proxy)
ansible_user: robert
@@ -12,17 +12,10 @@ services:
alloy_log_level: "warn"
# ============================================================================
# PPLG HAProxy Configuration
# PPLG Domain (TLS termination handled by Titania HAProxy)
# ============================================================================
pplg_haproxy_user: haproxy
pplg_haproxy_group: haproxy
pplg_haproxy_uid: 800
pplg_haproxy_gid: 800
pplg_haproxy_domain: "ouranos.helu.ca"
pplg_haproxy_cert_path: /etc/haproxy/certs/ouranos.pem
pplg_haproxy_stats_port: 8404
pplg_haproxy_syslog_port: 51405
pplg_domain: "ouranos.helu.ca"
# ============================================================================
# Grafana

View File

@@ -89,31 +89,26 @@ haproxy_backends:
backend_host: "prospero.incus"
backend_port: 5050
health_path: "/misc/ping"
ssl_backend: true
- subdomain: "grafana"
backend_host: "prospero.incus"
backend_port: 3000
health_path: "/api/health"
ssl_backend: true
- subdomain: "prometheus"
backend_host: "prospero.incus"
backend_port: 9090
backend_port: 9091 # OAuth2-Proxy sidecar (skips auth for /api/v1/write and /ping)
health_path: "/ping"
ssl_backend: true
- subdomain: "loki"
backend_host: "prospero.incus"
backend_port: 3100
health_path: "/ready"
ssl_backend: true
- subdomain: "alertmanager"
backend_host: "prospero.incus"
backend_port: 9093
health_path: "/-/healthy"
ssl_backend: true
- subdomain: "gitea"
backend_host: "rosalind.incus"

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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