Files
flights_web/deployment/setup-pve201.sh
T
gnezim 5273b3a7a6 setup-pve201: treat WAF 403 as warning, not fatal
The smoke test was getting 403 from the upstream WAF (rate-limit on
webzavod's egress IP). 403 doesn't indicate a tunnel/routing problem
— it confirms the egress IP IS the WAF-recognized one and is being
throttled. Don't abort the rest of setup over a transient throttle;
the only response that should hard-fail is HTTP 200 with HTML body
(WAF interstitial), which means the tunnel was bypassed.
2026-04-27 17:37:22 +03:00

152 lines
5.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# setup-pve201.sh — one-shot Phase B host setup. Run on pve-201 from the repo root.
#
# Usage (run on pve-201, in the repo root, on branch chore/tim-tunnel-routing):
# BASIC_AUTH_USER=front BASIC_AUTH_PASS=<pw> sudo -E bash deployment/setup-pve201.sh
#
# What it does (idempotent — safe to re-run):
# 1. Installs flights-tim-tunnel.service systemd unit and brings it up.
# 2. Smoke-tests the tunnel (curl to flights.test.aeroflot.ru via 127.0.0.1:8443).
# 3. Installs the new ui-dashboard.gnerim.ru nginx vhost + htpasswd dir.
# 4. Renders /etc/nginx/htpasswd/ui-dashboard from BASIC_AUTH_USER/PASS.
# 5. Reloads nginx after `nginx -t` passes.
#
# Each step prints a heading and exits non-zero on failure. Re-running after a
# fix continues where it failed (everything is overwrite-safe).
set -euo pipefail
if [ "$(id -u)" -ne 0 ]; then
echo "fatal: run as root (sudo -E bash $0)" >&2
exit 2
fi
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"
step() { printf '\n=== %s ===\n' "$*"; }
ok() { printf ' ok: %s\n' "$*"; }
fail() { printf ' FAIL: %s\n' "$*" >&2; exit 1; }
# ---------- 1. systemd unit ----------
step "1. flights-tim-tunnel.service"
UNIT_SRC="$REPO_ROOT/deployment/systemd/flights-tim-tunnel.service"
UNIT_DST="/etc/systemd/system/flights-tim-tunnel.service"
[ -f "$UNIT_SRC" ] || fail "missing $UNIT_SRC — wrong branch?"
if [ -f "$UNIT_DST" ] && cmp -s "$UNIT_SRC" "$UNIT_DST"; then
ok "$UNIT_DST already up-to-date"
else
cp "$UNIT_SRC" "$UNIT_DST"
ok "installed $UNIT_DST"
fi
systemctl daemon-reload
systemctl enable --now flights-tim-tunnel.service
sleep 2
systemctl is-active flights-tim-tunnel.service >/dev/null \
|| { systemctl status flights-tim-tunnel.service --no-pager; fail "tunnel unit not active"; }
ok "unit active"
# ---------- 2. tunnel smoke test ----------
step "2. tunnel smoke test"
ss -ltn | grep -qE '127\.0\.0\.1:8443\s' || fail "no listener on 127.0.0.1:8443"
ok "listener present"
SWAGGER_RC=$(curl -sS -k --max-time 10 -o /dev/null -w "%{http_code}" \
--resolve flights.test.aeroflot.ru:8443:127.0.0.1 \
https://flights.test.aeroflot.ru:8443/swagger/index.html)
case "$SWAGGER_RC" in
401) ok "swagger HTTP 401 (real backend, WAF passed)" ;;
403) ok "swagger HTTP 403 (WAF rate-limit — egress IP is correct, just throttled)" ;;
200) fail "swagger HTTP 200 — likely WAF interstitial (tunnel bypassed)" ;;
*) fail "swagger unexpected HTTP $SWAGGER_RC" ;;
esac
API_RC=$(curl -sS -k --max-time 10 -o /dev/null -w "%{http_code}" \
--resolve flights.test.aeroflot.ru:8443:127.0.0.1 \
https://flights.test.aeroflot.ru:8443/api/health)
case "$API_RC" in
200) ok "api/health HTTP 200" ;;
403) ok "api/health HTTP 403 (WAF rate-limit — transient, egress IP confirmed correct)" ;;
*) fail "api/health HTTP $API_RC" ;;
esac
# ---------- 3. nginx vhost ----------
step "3. nginx vhost"
VHOST_SRC="$REPO_ROOT/deployment/nginx/ui-dashboard.gnerim.ru.conf"
VHOST_DST="/etc/nginx/sites-available/ui-dashboard.gnerim.ru"
[ -f "$VHOST_SRC" ] || fail "missing $VHOST_SRC"
if [ -f "$VHOST_DST" ] && cmp -s "$VHOST_SRC" "$VHOST_DST"; then
ok "$VHOST_DST already up-to-date"
else
if [ -f "$VHOST_DST" ]; then
BAK="${VHOST_DST}.bak.$(date +%Y%m%d-%H%M%S)"
cp "$VHOST_DST" "$BAK"
ok "backed up old vhost to $BAK"
fi
cp "$VHOST_SRC" "$VHOST_DST"
ok "installed $VHOST_DST"
fi
ENABLED="/etc/nginx/sites-enabled/ui-dashboard.gnerim.ru"
if [ ! -L "$ENABLED" ]; then
ln -sf "$VHOST_DST" "$ENABLED"
ok "created sites-enabled symlink"
else
ok "sites-enabled symlink already present"
fi
mkdir -p /etc/nginx/htpasswd
ok "/etc/nginx/htpasswd ensured"
# Install proxy_cache zone declaration (must live in http {} context)
CACHE_CONF_SRC="$REPO_ROOT/deployment/nginx/conf.d/flights-api-cache.conf"
CACHE_CONF_DST="/etc/nginx/conf.d/flights-api-cache.conf"
if [ -f "$CACHE_CONF_DST" ] && cmp -s "$CACHE_CONF_SRC" "$CACHE_CONF_DST"; then
ok "$CACHE_CONF_DST already up-to-date"
else
cp "$CACHE_CONF_SRC" "$CACHE_CONF_DST"
ok "installed $CACHE_CONF_DST"
fi
# Cache directory — nginx auto-creates with proper perms on first start, but
# we pre-create with the right ownership so reload picks it up cleanly.
CACHE_DIR="/var/cache/nginx/flights-api"
NGINX_USER="$(awk '/^user / {gsub(";",""); print $2}' /etc/nginx/nginx.conf 2>/dev/null | head -1)"
NGINX_USER="${NGINX_USER:-www-data}"
mkdir -p "$CACHE_DIR"
chown -R "$NGINX_USER":"$NGINX_USER" "$CACHE_DIR"
ok "$CACHE_DIR ensured (owner: $NGINX_USER)"
# ---------- 4. htpasswd ----------
step "4. htpasswd"
: "${BASIC_AUTH_USER:?BASIC_AUTH_USER required (export it before sudo -E)}"
: "${BASIC_AUTH_PASS:?BASIC_AUTH_PASS required (export it before sudo -E)}"
HASH=$(openssl passwd -apr1 "$BASIC_AUTH_PASS")
HTPASSWD_PATH="/etc/nginx/htpasswd/ui-dashboard"
echo "${BASIC_AUTH_USER}:${HASH}" > "$HTPASSWD_PATH"
chmod 644 "$HTPASSWD_PATH"
ok "wrote $HTPASSWD_PATH"
# ---------- 5. nginx reload ----------
step "5. nginx -t + reload"
nginx -t
systemctl reload nginx
ok "nginx reloaded"
# ---------- summary ----------
step "done"
echo "Tunnel: $(systemctl is-active flights-tim-tunnel.service)"
echo "Nginx: $(systemctl is-active nginx)"
echo
echo "Try:"
echo " curl -u ${BASIC_AUTH_USER}:<pw> -I https://ui-dashboard.gnerim.ru/ # expect 502 until container is deployed (Workflow A)"
echo " curl -u ${BASIC_AUTH_USER}:<pw> -I https://ui-dashboard.gnerim.ru/api/health # expect 200 from real upstream"