#!/usr/bin/env bash set -euo pipefail DOMAIN="${1:-vtuber.wooo.work}" TARGET_IP="${2:-114.32.151.246}" DNS_RESOLVER="${3:-8.8.8.8}" STRICT_MODE="${4:-1}" strict_mode="${CHECK_STRICT_MODE:-${STRICT_MODE}}" fail_count=0 resolved_count=0 need_cmd() { if ! command -v "$1" >/dev/null 2>&1; then echo "❌ 缺少指令:$1" exit 1 fi } need_cmd dig need_cmd openssl need_cmd curl echo "===== DNS 解析檢查 =====" echo "網域:${DOMAIN}" echo "預期 A 紀錄指向:${TARGET_IP}" echo "採用解析器:${DNS_RESOLVER}" echo "嚴格檢查:${strict_mode}" a_records="$(dig +short A "${DOMAIN}" @"${DNS_RESOLVER}" | awk 'NF {print $1}' | sort -u)" aaaa_records="$(dig +short AAAA "${DOMAIN}" @"${DNS_RESOLVER}" | awk 'NF {print $1}' | sort -u)" if [ -z "${a_records}" ] && [ -z "${aaaa_records}" ]; then echo "⚠️ 無法解析到 A/AAAA;請檢查 DNS zone 是否有建立 ${DOMAIN} 的 A/AAAA 紀錄。" fail_count=$((fail_count + 1)) else echo "A 紀錄:${a_records:-(未設定)}" echo "AAAA 紀錄:${aaaa_records:-(未設定)}" if printf '%s\n' "${a_records}" | grep -Fxq "${TARGET_IP}"; then echo "✅ A 紀錄包含 ${TARGET_IP}" resolved_count=$((resolved_count + 1)) else echo "⚠️ ${DOMAIN} 的 A 紀錄未看到 ${TARGET_IP}" echo " 請檢查 DNS:A Record (${DOMAIN}) = ${TARGET_IP}" echo " 修正指引:在 DNS 面板把 vtuber.wooo.work 的 A Record 指到 ${TARGET_IP}" fail_count=$((fail_count + 1)) fi fi if command -v host >/dev/null 2>&1; then host "${DOMAIN}" | sed -n '1,4p' || true else echo "(已略過 host 查詢,未安裝 host 指令)" fi echo echo "===== HTTPS 憑證檢查 =====" cert_pem="$(echo | openssl s_client -connect "${DOMAIN}:443" -servername "${DOMAIN}" 2>/dev/null | openssl x509 2>/dev/null || true)" if [ -z "${cert_pem}" ]; then echo "❌ 無法抓到 TLS 憑證(連線或憑證有問題)" fail_count=$((fail_count + 1)) else subject="$(printf '%s\n' "${cert_pem}" | openssl x509 -noout -subject)" issuer="$(printf '%s\n' "${cert_pem}" | openssl x509 -noout -issuer)" start_date="$(printf '%s\n' "${cert_pem}" | openssl x509 -noout -startdate)" end_date="$(printf '%s\n' "${cert_pem}" | openssl x509 -noout -enddate)" san="$(printf '%s\n' "${cert_pem}" | openssl x509 -noout -ext subjectAltName 2>/dev/null || true)" echo "Subject: ${subject}" echo "Issuer: ${issuer}" echo "起始: ${start_date}" echo "到期: ${end_date}" echo "SAN: ${san:-(未提供)}" if printf '%s\n' "${san}" | grep -q "DNS:${DOMAIN}"; then echo "✅ 憑證 SAN 包含 ${DOMAIN}" else echo "⚠️ 憑證 SAN 未明確列出 ${DOMAIN}" fail_count=$((fail_count + 1)) fi if printf '%s\n' "${cert_pem}" | openssl x509 -checkend $((7*24*60*60)) >/dev/null 2>&1; then echo "✅ 憑證未於 7 日內到期" else echo "⚠️ 憑證疑似 7 日內到期" fail_count=$((fail_count + 1)) fi fi echo echo "===== 路徑可達性檢查 =====" check_url() { local label="$1" local url="$2" local expect_http="${3:-200}" local output shift 3 output="$(curl -ksS -m 12 -o /dev/null -w 'HTTP %{http_code} | total=%{time_total}s | remote=%{remote_ip}' "$@" "$url" || true)" local http_code http_code="$(printf '%s\n' "${output}" | awk '{print $2}')" if [ -z "${output}" ] || [ "${http_code}" = "000" ] || [ -z "${http_code}" ]; then echo "❌ ${label}:連線失敗" fail_count=$((fail_count + 1)) elif [ "${http_code}" != "${expect_http}" ]; then echo "❌ ${label}:HTTP ${http_code}(預期 ${expect_http})" fail_count=$((fail_count + 1)) else echo "✅ ${label}:${output}" fi } check_url "域名直連" "https://${DOMAIN}/live/demo" 200 check_url "域名繁中直連" "https://${DOMAIN}/zh-TW/live/demo" 200 check_url "SNI 直定向到 ${TARGET_IP}" "https://${DOMAIN}/live/demo" \ --resolve "${DOMAIN}:443:${TARGET_IP}" 200 check_url "IP + Host 直走 (防止 host 漏配)" "http://${TARGET_IP}/live/demo" \ -H "Host: ${DOMAIN}" 200 check_url "純 IP 訪問(不帶 Host)" "http://${TARGET_IP}/live/demo" 200 echo if [ "${fail_count}" -eq 0 ]; then echo "總結:✅ 全部檢查通過。" else echo "總結:⚠️ ${fail_count} 項提醒,需先補上對應修正。" fi if [ "${strict_mode}" != "0" ] && [ "${fail_count}" -gt 0 ]; then echo "部署門檻:❌ 因嚴格模式,非 0 值結果將阻止後續流程。" exit 1 fi if [ "${fail_count}" -gt 0 ] && [ "${resolved_count}" -eq 0 ]; then echo "DNS 門檻:❌ 外網無法直接導向目標主機,這通常代表流量仍在舊站。" fi exit 0