diff --git a/playbooks/roles/vhosts/HAProxy/README.md b/playbooks/roles/vhosts/HAProxy/README.md new file mode 100644 index 0000000..5443bca --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/README.md @@ -0,0 +1,58 @@ +# HAProxy Role + +This role provisions a thin, include-only HAProxy configuration tree under `/etc/haproxy/`. It follows a map-driven SNI routing strategy so that new hostnames can be added without touching the frontend logic. + +## Layout +``` +/etc/haproxy/ +├── haproxy.cfg # Main entry point (kept thin) +├── global.cfg # global + defaults +├── frontends/ +│ ├── fe_443.cfg +│ └── fe_stats.cfg +├── backends/ +│ ├── console/ +│ │ ├── bk_cn.cfg +│ │ └── bk_global.cfg +│ ├── xray/ +│ │ ├── bk_xray_jp.cfg +│ │ ├── bk_xray_sg.cfg +│ │ └── bk_xray_hk.cfg +│ └── fallback/ +│ └── bk_blackhole.cfg +├── maps/ +│ ├── sni.map +│ └── sni_backend.map +├── certs/ # For HTTP/TLS termination if needed +├── scripts/ +│ └── reload.sh +└── logs/ +``` + +### Main configuration (`haproxy.cfg`) +- Includes `global.cfg` for shared defaults. +- Includes all frontends and recursively includes business backends. +- Contains no business logic to keep reloads predictable. + +### Global/defaults (`global.cfg`) +Applies TCP defaults and keeps per-node tuning minimal. HK/JP can adjust `maxconn` per-node if needed. + +### Frontends +- `fe_443.cfg` performs TLS inspection and routes via the SNI map (`maps/sni_backend.map`), falling back to `bk_blackhole`. +- `fe_stats.cfg` exposes the HAProxy stats UI on `:8404`. + +### Maps +- `maps/sni_backend.map` maps SNI hosts to backends; adding a domain means adding a single line. +- `maps/sni.map` documents the expected format when TLS termination is required. + +### Backends +Business backends are grouped by domain family (console/xray/fallback) with TCP health checks and consistent timings. The fallback backend intentionally blackholes unmatched traffic. + +### Reload helper +`scripts/reload.sh` validates the configuration and reloads HAProxy gracefully (preferring `systemctl` when present). + +## Usage +1. Include the role in a play targeting your HAProxy hosts. +2. Update `maps/sni_backend.map` with the desired hostname-to-backend mapping. +3. Adjust backend server endpoints per site; keep the directory layout identical across regions. +4. Run the play to copy the configuration tree and trigger a graceful reload via the handler. diff --git a/playbooks/roles/vhosts/HAProxy/defaults/main.yml b/playbooks/roles/vhosts/HAProxy/defaults/main.yml new file mode 100644 index 0000000..e149261 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/defaults/main.yml @@ -0,0 +1,5 @@ +--- +haproxy_conf_dir: /etc/haproxy +haproxy_user: root +haproxy_group: root +haproxy_reload_cmd: /etc/haproxy/scripts/reload.sh diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/console/bk_cn.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/console/bk_cn.cfg new file mode 100644 index 0000000..8ae7991 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/console/bk_cn.cfg @@ -0,0 +1,7 @@ +backend bk_console_cn + balance roundrobin + option tcp-check + default-server inter 3s fall 3 rise 2 + + server cn1 10.10.1.1:8443 check + server cn2 10.10.1.2:8443 check diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/console/bk_global.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/console/bk_global.cfg new file mode 100644 index 0000000..63a8c59 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/console/bk_global.cfg @@ -0,0 +1,7 @@ +backend bk_console_global + balance roundrobin + option tcp-check + default-server inter 3s fall 3 rise 2 + + server g1 10.10.0.1:8443 check + server g2 10.10.0.2:8443 check diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/fallback/bk_blackhole.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/fallback/bk_blackhole.cfg new file mode 100644 index 0000000..706179c --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/fallback/bk_blackhole.cfg @@ -0,0 +1,2 @@ +backend bk_blackhole + server reject 127.0.0.1:1 diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/xray/bk_xray_hk.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/xray/bk_xray_hk.cfg new file mode 100644 index 0000000..74ce711 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/xray/bk_xray_hk.cfg @@ -0,0 +1,7 @@ +backend bk_xray_hk + balance roundrobin + option tcp-check + default-server inter 3s fall 3 rise 2 + + server hk1 10.22.0.1:1443 check send-proxy-v2 + server hk2 10.22.0.2:1443 check send-proxy-v2 diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/xray/bk_xray_jp.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/xray/bk_xray_jp.cfg new file mode 100644 index 0000000..d6d5c81 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/xray/bk_xray_jp.cfg @@ -0,0 +1,7 @@ +backend bk_xray_jp + balance roundrobin + option tcp-check + default-server inter 3s fall 3 rise 2 + + server jp1 10.20.0.1:1443 check send-proxy-v2 + server jp2 10.20.0.2:1443 check send-proxy-v2 diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/xray/bk_xray_sg.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/xray/bk_xray_sg.cfg new file mode 100644 index 0000000..78c391d --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/backends/xray/bk_xray_sg.cfg @@ -0,0 +1,7 @@ +backend bk_xray_sg + balance roundrobin + option tcp-check + default-server inter 3s fall 3 rise 2 + + server sg1 10.21.0.1:1443 check send-proxy-v2 + server sg2 10.21.0.2:1443 check send-proxy-v2 diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/certs/.gitkeep b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/certs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/frontends/fe_443.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/frontends/fe_443.cfg new file mode 100644 index 0000000..9ad2f96 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/frontends/fe_443.cfg @@ -0,0 +1,9 @@ +frontend fe_443 + bind :443 + mode tcp + + tcp-request inspect-delay 5s + tcp-request content accept if { req.ssl_hello_type 1 } + + use_backend %[req.ssl_sni,lower,map(/etc/haproxy/maps/sni_backend.map)] + default_backend bk_blackhole diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/frontends/fe_stats.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/frontends/fe_stats.cfg new file mode 100644 index 0000000..249edc3 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/frontends/fe_stats.cfg @@ -0,0 +1,5 @@ +listen stats + bind :8404 + stats enable + stats uri /stats + stats refresh 5s diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/global.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/global.cfg new file mode 100644 index 0000000..b7e5816 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/global.cfg @@ -0,0 +1,14 @@ +global + daemon + log /dev/log local0 + maxconn 20000 + nbthread 1 + +defaults + mode tcp + log global + option dontlognull + timeout connect 3s + timeout client 1m + timeout server 1m + timeout check 3s diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/haproxy.cfg b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/haproxy.cfg new file mode 100644 index 0000000..395b18f --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/haproxy.cfg @@ -0,0 +1,10 @@ +# Main HAProxy configuration file (intentionally thin) + +# 引入全局参数 +include /etc/haproxy/global.cfg + +# Frontends +include /etc/haproxy/frontends/*.cfg + +# Backends +include /etc/haproxy/backends/**/*.cfg diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/logs/.gitkeep b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/maps/sni.map b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/maps/sni.map new file mode 100644 index 0000000..9ff76a9 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/maps/sni.map @@ -0,0 +1,3 @@ +# Map SNI to certificate filenames when terminating TLS locally. +# +# example.com example-com.pem diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/maps/sni_backend.map b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/maps/sni_backend.map new file mode 100644 index 0000000..a86c3d6 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/maps/sni_backend.map @@ -0,0 +1,5 @@ +cn-console.sv.plus bk_console_cn +global-console.sv.plus bk_console_global +xray-jp.svc.plus bk_xray_jp +xray-sg.svc.plus bk_xray_sg +xray-hk.svc.plus bk_xray_hk diff --git a/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/scripts/reload.sh b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/scripts/reload.sh new file mode 100644 index 0000000..da2a75d --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/files/etc/haproxy/scripts/reload.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +CONFIG="/etc/haproxy/haproxy.cfg" +PIDFILE="/var/run/haproxy.pid" +HAPROXY_BIN="${HAPROXY_BIN:-/usr/sbin/haproxy}" +SYSTEMCTL_BIN="${SYSTEMCTL_BIN:-/bin/systemctl}" + +# Validate configuration before applying +if ! "${HAPROXY_BIN}" -c -f "${CONFIG}"; then + echo "HAProxy configuration validation failed" >&2 + exit 1 +fi + +# Reload HAProxy gracefully +if command -v "${SYSTEMCTL_BIN}" >/dev/null 2>&1; then + exec "${SYSTEMCTL_BIN}" reload haproxy +else + if [[ -r "${PIDFILE}" ]]; then + exec "${HAPROXY_BIN}" -f "${CONFIG}" -sf "$(cat "${PIDFILE}")" + else + exec "${HAPROXY_BIN}" -f "${CONFIG}" + fi +fi diff --git a/playbooks/roles/vhosts/HAProxy/handlers/main.yml b/playbooks/roles/vhosts/HAProxy/handlers/main.yml new file mode 100644 index 0000000..b609bda --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/handlers/main.yml @@ -0,0 +1,4 @@ +--- +- name: reload haproxy + ansible.builtin.command: "{{ haproxy_reload_cmd }}" + listen: reload haproxy diff --git a/playbooks/roles/vhosts/HAProxy/meta/main.yml b/playbooks/roles/vhosts/HAProxy/meta/main.yml new file mode 100644 index 0000000..a384687 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + role_name: haproxy + author: gitops + description: "HAProxy role with thin main config, map-driven SNI routing, and organized backends." + min_ansible_version: "2.10" + platforms: + - name: EL + versions: [8, 9] + - name: Debian + versions: [11, 12] + - name: Ubuntu + versions: [20.04, 22.04] + +dependencies: [] diff --git a/playbooks/roles/vhosts/HAProxy/tasks/main.yml b/playbooks/roles/vhosts/HAProxy/tasks/main.yml new file mode 100644 index 0000000..c0cd6c4 --- /dev/null +++ b/playbooks/roles/vhosts/HAProxy/tasks/main.yml @@ -0,0 +1,51 @@ +--- +- name: Ensure HAProxy base directory exists + ansible.builtin.file: + path: "{{ haproxy_conf_dir }}" + state: directory + owner: "{{ haproxy_user }}" + group: "{{ haproxy_group }}" + mode: "0755" + +- name: Ensure HAProxy subdirectories exist + ansible.builtin.file: + path: "{{ haproxy_conf_dir }}/{{ item }}" + state: directory + owner: "{{ haproxy_user }}" + group: "{{ haproxy_group }}" + mode: "0755" + loop: + - frontends + - backends + - backends/console + - backends/xray + - backends/fallback + - maps + - certs + - scripts + - logs + +- name: Deploy HAProxy configuration files + ansible.builtin.copy: + src: "etc/haproxy/{{ item.src }}" + dest: "{{ haproxy_conf_dir }}/{{ item.dest | default(item.src) }}" + owner: "{{ haproxy_user }}" + group: "{{ haproxy_group }}" + mode: "{{ item.mode | default('0644') }}" + loop: + - { src: "haproxy.cfg" } + - { src: "global.cfg" } + - { src: "frontends/fe_443.cfg" } + - { src: "frontends/fe_stats.cfg" } + - { src: "backends/console/bk_cn.cfg" } + - { src: "backends/console/bk_global.cfg" } + - { src: "backends/xray/bk_xray_jp.cfg" } + - { src: "backends/xray/bk_xray_sg.cfg" } + - { src: "backends/xray/bk_xray_hk.cfg" } + - { src: "backends/fallback/bk_blackhole.cfg" } + - { src: "maps/sni_backend.map" } + - { src: "maps/sni.map" } + - { src: "certs/.gitkeep" } + - { src: "logs/.gitkeep" } + - { src: "scripts/reload.sh", mode: "0755" } + notify: reload haproxy