# -*- coding: utf-8 -*- r'''验证源站的 443 是否就绪(证书签完之后)。 背景:522 是因为 Cloudflare 的 SSL 模式是 Full/Full(strict), 回源走 443,而源站只有 80。现在证书签好了、nginx 也配了 443, 所以要确认: 1. 本机 443 能自测通(带正确 SNI) 2. 从**外面**能不能连上 443 —— 这一步取决于用户在 Lightsail 防火墙开没开 443 ''' from __future__ import annotations import socket import ssl import sys import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "_build")) from lssh import run # noqa: E402 DOMAIN = "yk.xxmxxs.top" IP = "13.54.30.196" def tcp(host: str, port: int, timeout: float = 8.0) -> bool: s = socket.socket() s.settimeout(timeout) try: s.connect((host, port)) return True except Exception: # noqa: BLE001 return False finally: s.close() def https(host: str, port: int = 443) -> tuple: """返回 (状态码文本, 证书主体)。""" ctx = ssl.create_default_context() try: with urllib.request.urlopen( "https://%s:%d/v1/health" % (host, port), timeout=15, context=ctx) as r: r.read(64) return str(r.status), "证书验证通过" except ssl.SSLCertVerificationError as exc: return "证书错误", str(exc)[:70] except Exception as exc: # noqa: BLE001 return "失败", "%s: %s" % (type(exc).__name__, str(exc)[:60]) def main() -> int: print("=" * 62) print(" 验证 443 是否就绪") print("=" * 62) print() print(" ── 1. 源站内部(从服务器自己看)──") # ⚠ 这里**不能用 % 格式化** —— 远端命令里有 curl 的 # %{http_code},% 格式化会把 %{ 当成格式符直接抛 # "unsupported format character '{'"。用 replace 更安全。 inner = ( "echo ' --- 监听 ---'; " "ss -tln 2>/dev/null | grep -E ':(80|443) ' | sed 's/^/ /'; " "echo ' --- 本机 https(带 SNI)---'; " "curl -s -o /dev/null -w ' %{http_code}\\n' --max-time 10 " "--resolve __D__:443:127.0.0.1 https://__D__/v1/health; " "echo ' --- 证书主体 ---'; " "sudo openssl x509 -in /etc/letsencrypt/live/__D__/fullchain.pem " "-noout -subject -dates 2>/dev/null | sed 's/^/ /'; " "echo ' --- nginx 里的 443 段 ---'; " "sudo grep -nE 'listen|ssl_certificate|return 301|server_name' " "/etc/nginx/sites-available/ykxmz | sed 's/^/ /'" ).replace("__D__", DOMAIN) out = run(inner, timeout=180, quiet=True) for ln in out[1].strip().splitlines(): print(ln) print() print(" ── 2. 从这台电脑直连源站(不经过 Cloudflare)──") for port in (80, 443): ok = tcp(IP, port) print(" %s:%-4d TCP %s" % (IP, port, "通" if ok else "不通 ❌")) code, note = https(IP) print(" https://%s/v1/health -> %s (%s)" % (IP, code, note)) print(" (这里会是证书错误,正常 —— 我们是用 IP 直连,") print(" 证书是签给域名的。这一步只看 TCP 通不通。)") print() print(" ── 3. 经 Cloudflare 的 HTTPS ──") code, note = https(DOMAIN) print(" https://%s/v1/health -> %s (%s)" % (DOMAIN, code, note)) print() print(" ── 4. 结论 ──") if tcp(IP, 443): print(" ✅ 443 从外面**通** —— Cloudflare 的 Full(strict) 能工作") if code == "200": print(" ✅ 全链路已经好了") else: print(" ⚠ 443 通了但 HTTPS 还不通,可能是 Cloudflare") print(" 还没切到 Full(strict),或者配置还在生效中") else: print(" ❌ 443 从外面**不通** —— 需要去 Lightsail 防火墙开 TCP 443") print(" (实例 → 联网 → 防火墙 → 添加规则 → 自定义 TCP 443)") return 0 if __name__ == "__main__": sys.exit(main())