#!/usr/bin/env bash # HAProxy 配置与后端验证(OpenWrt 18080/18443,第三方 onecloud curl) # 核心:ansible/files/01-08-haproxy/*.cfg 语法正确;可选经 curl 验证运行时与后端 set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" CFG_DIR="${ROOT_DIR}/ansible/files/01-08-haproxy" MATRIX_FILE="${ROOT_DIR}/docs/00-02-验证矩阵.md" SSH_OPENWRT="${SSH_OPENWRT:-openwrt}" VERIFY_HOST="${VERIFY_HOST:-onecloud}" OPENWRT_IP="${OPENWRT_IP:-192.168.2.1}" HTTP_PORT="${HTTP_PORT:-18080}" HTTPS_PORT="${HTTPS_PORT:-18443}" DEPLOY_MATRIX="${DEPLOY_MATRIX:-none}" HTTPS_HOSTS="" # 逗号分隔,如 test01.jackadam.top,test02.jackadam.top UPDATE_MATRIX=1 CFG_ONLY=0 # 仅 haproxy -c 校验本目录 cfg,不跑 curl usage() { cat <<'EOF' 用法: ./scripts/01-08-verify-haproxy.sh [选项] 选项: --cfg-only 仅校验 ansible/files/01-08-haproxy/*.cfg 语法(OpenWrt 上 haproxy -c),不跑 curl --deploy-matrix 验证前一键部署矩阵(默认 none) --verify-host curl 执行主机,SSH 目标(默认 onecloud) --openwrt-ip OpenWrt/网关 IP(默认 192.168.2.1) --http-port HAProxy HTTP 端口(默认 18080) --https-port HAProxy HTTPS 端口(默认 18443) --https-hosts HTTPS 校验域名,M1~M4 对应(缺省时不校验 HTTPS) --update-matrix 验证通过后更新验证矩阵(默认启用) --no-update-matrix 不更新验证矩阵 -h, --help 显示帮助 前置:ssh openwrt 可用;完整验证还需 ssh onecloud;OpenWrt HAProxy 已按 18080/18443 配置(运行时验证) EOF } while [[ $# -gt 0 ]]; do case "$1" in --cfg-only) CFG_ONLY=1; shift ;; --deploy-matrix) DEPLOY_MATRIX="${2:-none}"; shift 2 ;; --verify-host) VERIFY_HOST="${2:-onecloud}"; shift 2 ;; --openwrt-ip) OPENWRT_IP="${2:-192.168.2.1}"; shift 2 ;; --http-port) HTTP_PORT="${2:-18080}"; shift 2 ;; --https-port) HTTPS_PORT="${2:-18443}"; shift 2 ;; --https-hosts) HTTPS_HOSTS="${2:-}"; shift 2 ;; --update-matrix) UPDATE_MATRIX=1; shift ;; --no-update-matrix) UPDATE_MATRIX=0; shift ;; -h|--help) usage; exit 0 ;; *) echo "[ERR] 未知参数: $1"; usage; exit 1 ;; esac done REMOTE_DIR="/tmp/haproxy-verify" if [[ $CFG_ONLY -eq 1 ]]; then echo "=== HAProxy cfg 语法校验(${SSH_OPENWRT},ansible/files/01-08-haproxy/*.cfg)===" else echo "=== HAProxy 验证(${SSH_OPENWRT} → ${VERIFY_HOST} curl ${OPENWRT_IP}:${HTTP_PORT}/${HTTPS_PORT})===" fi # 0. 按需部署矩阵(--cfg-only 时不部署) if [[ $CFG_ONLY -eq 1 ]]; then : elif [[ "$DEPLOY_MATRIX" == "http" ]]; then echo "[0] 部署 02-05 nginx 矩阵(http)..." (cd "$ROOT_DIR" && ansible-playbook -i ansible/inventory.ini ansible/playbooks/nginx-matrix-deploy.yml) || { echo "[ERR] nginx-matrix-deploy 失败" >&2; exit 1; } echo " [OK] HTTP 矩阵已部署" elif [[ "$DEPLOY_MATRIX" == "tls" ]]; then echo "[0] 部署 nginx 矩阵 TLS 版..." (cd "$ROOT_DIR" && ansible-playbook -i ansible/inventory.ini ansible/playbooks/nginx-matrix-tls-deploy.yml) || { echo "[ERR] nginx-matrix-tls-deploy 失败" >&2; exit 1; } echo " [OK] TLS 矩阵已部署" [[ -z "$HTTPS_HOSTS" ]] && HTTPS_HOSTS="test01.jackadam.top,test02.jackadam.top,test03.jackadam.top,test04.jackadam.top" fi if [[ ! -d "$CFG_DIR" ]]; then echo "[ERR] cfg 目录不存在: $CFG_DIR" >&2 exit 1 fi # 1. 拷贝 cfg 到 OpenWrt(-O 强制旧 SCP 协议,兼容无 sftp-server 的 OpenWrt) echo "[1/4] 拷贝 cfg 到 ${SSH_OPENWRT}:${REMOTE_DIR}..." ssh "$SSH_OPENWRT" "mkdir -p ${REMOTE_DIR}" scp -q -O "${CFG_DIR}"/*.cfg "${SSH_OPENWRT}:${REMOTE_DIR}/" 2>/dev/null || { echo " [INFO] scp -O 不可用,改用 ssh 管道传输..." for f in "${CFG_DIR}"/*.cfg; do bn=$(basename "$f") ssh "$SSH_OPENWRT" "cat > ${REMOTE_DIR}/${bn}" < "$f" done } # 2. 语法校验 echo "[2/4] 校验 cfg 语法..." SYNTAX_FAIL=0 for cfg in haproxy-no-check haproxy-http haproxy-tls haproxy-proxy-http-tls; do if ssh "$SSH_OPENWRT" "haproxy -c -f ${REMOTE_DIR}/${cfg}.cfg" 2>/dev/null; then echo " [OK] ${cfg}.cfg" else echo " [FAIL] ${cfg}.cfg" >&2 SYNTAX_FAIL=1 fi done if ssh "$SSH_OPENWRT" "haproxy -c -f ${REMOTE_DIR}/haproxy-https.cfg" 2>/dev/null; then echo " [OK] haproxy-https.cfg(语法;运行需 /etc/ssl/haproxy.pem)" else echo " [SKIP] haproxy-https.cfg(缺证书)" fi if [[ $SYNTAX_FAIL -eq 1 ]]; then echo "[ERR] 部分 cfg 语法校验失败" >&2 exit 1 fi if [[ $CFG_ONLY -eq 1 ]]; then echo echo "[PASS] 本目录 HAProxy cfg 语法校验通过(见 ansible/files/01-08-haproxy/README.md)" exit 0 fi # 3. SSH onecloud 执行 curl 验证 echo "[3/4] 经 ${VERIFY_HOST} 验证 HTTP(${OPENWRT_IP}:${HTTP_PORT})..." # HTTP:TLS 矩阵(有 --https-hosts)按 Host 验证;否则 02-05 路径 /demo-m1~m4 if [[ -n "$HTTPS_HOSTS" ]]; then # TLS 矩阵:按 Host 验证,test01~test04 对应 M1~M4 IFS=',' read -ra HOSTS <<< "$HTTPS_HOSTS" HTTP_FAIL=0 for i in "${!HOSTS[@]}"; do host="${HOSTS[$i]}" expect="M$((i+1))" code=$(ssh "$VERIFY_HOST" "curl -s -o /dev/null -w '%{http_code}' --max-time 5 'http://${host}:${HTTP_PORT}/' --resolve '${host}:${HTTP_PORT}:${OPENWRT_IP}' 2>/dev/null" || echo "000") body=$(ssh "$VERIFY_HOST" "curl -s --max-time 5 'http://${host}:${HTTP_PORT}/' --resolve '${host}:${HTTP_PORT}:${OPENWRT_IP}' 2>/dev/null" || echo "") if [[ "$code" != "200" ]]; then echo " [FAIL] http://${host}:${HTTP_PORT}/ 返回 ${code}" >&2 HTTP_FAIL=1 elif [[ "$body" != *"$expect"* ]]; then echo " [FAIL] http://${host}:${HTTP_PORT}/ body 不含 ${expect}" >&2 HTTP_FAIL=1 else echo " [OK] http://${host}:${HTTP_PORT}/ 200 含 ${expect}" fi done else # 02-05 路径型 DEMO_PATHS=(demo-m1:M1 demo-m2:M2 demo-m3:M3 demo-m4:M4) HTTP_FAIL=0 for item in "${DEMO_PATHS[@]}"; do path="${item%%:*}" expect="${item##*:}" code=$(ssh "$VERIFY_HOST" "curl -s -o /dev/null -w '%{http_code}' --max-time 5 'http://${OPENWRT_IP}:${HTTP_PORT}/${path}/' 2>/dev/null" || echo "000") body=$(ssh "$VERIFY_HOST" "curl -s --max-time 5 'http://${OPENWRT_IP}:${HTTP_PORT}/${path}/' 2>/dev/null" || echo "") if [[ "$code" != "200" ]]; then echo " [FAIL] /${path}/ 返回 ${code}" >&2 HTTP_FAIL=1 elif [[ "$body" != *"$expect"* ]]; then echo " [FAIL] /${path}/ body 不含 ${expect}" >&2 HTTP_FAIL=1 else echo " [OK] /${path}/ 200 含 ${expect}" fi done fi if [[ $HTTP_FAIL -eq 1 ]]; then echo "[ERR] HTTP 验证失败" >&2 exit 1 fi # 4. HTTPS 验证(需 --https-hosts,不带 -k 校验证书) if [[ -n "$HTTPS_HOSTS" ]]; then echo "[4/4] 经 ${VERIFY_HOST} 验证 HTTPS(域名 :${HTTPS_PORT},校验 ACME 证书)..." IFS=',' read -ra HOSTS <<< "$HTTPS_HOSTS" HTTPS_FAIL=0 for i in "${!HOSTS[@]}"; do host="${HOSTS[$i]}" expect="M$((i+1))" code=$(ssh "$VERIFY_HOST" "curl -s -o /dev/null -w '%{http_code}' --max-time 10 'https://${host}:${HTTPS_PORT}/' --resolve '${host}:${HTTPS_PORT}:${OPENWRT_IP}' 2>/dev/null" || echo "000") body=$(ssh "$VERIFY_HOST" "curl -s --max-time 10 'https://${host}:${HTTPS_PORT}/' --resolve '${host}:${HTTPS_PORT}:${OPENWRT_IP}' 2>/dev/null" || echo "") if [[ "$code" != "200" ]]; then echo " [FAIL] https://${host}:${HTTPS_PORT}/ 返回 ${code}" >&2 HTTPS_FAIL=1 elif [[ "$body" != *"$expect"* ]]; then echo " [FAIL] https://${host}:${HTTPS_PORT}/ body 不含 ${expect}" >&2 HTTPS_FAIL=1 else echo " [OK] https://${host}:${HTTPS_PORT}/ 200 含 ${expect}" fi done if [[ $HTTPS_FAIL -eq 1 ]]; then echo "[ERR] HTTPS 验证失败" >&2 exit 1 fi else echo "[4/4] 跳过 HTTPS(未指定 --https-hosts)" fi echo echo "[PASS] HAProxy 验证通过" # 5. 可选:更新验证矩阵 if [[ $UPDATE_MATRIX -eq 1 ]] && [[ -f "$MATRIX_FILE" ]]; then TODAY=$(date +%Y-%m-%d) echo "[INFO] 更新验证矩阵..." if command -v python3 >/dev/null 2>&1; then python3 "${ROOT_DIR}/scripts/01-08-update-verify-matrix.py" "$MATRIX_FILE" "$TODAY" || echo " [WARN] 验证矩阵未更新" else echo " [WARN] 未找到 python3,请手动更新 docs/00-02-验证矩阵.md" fi fi