""" 首次启动引导:为 CertCenter 自身生成自签证书 当 BASE_URL 使用 HTTPS 但证书不存在时自动执行 """ import subprocess from pathlib import Path from urllib.parse import urlparse from datetime import datetime, timedelta def ensure_self_signed_cert(base_url: str, cert_dir: str): """ 如果 CertCenter 自身的域名没有证书,生成一份自签证书 证书存放在 cert_dir/{domain}/ 目录下 """ parsed = urlparse(base_url) if parsed.scheme != "https": return # HTTP 模式不需要证书 hostname = parsed.hostname if not hostname: return cert_path = Path(cert_dir) / hostname / "fullchain.pem" key_path = Path(cert_dir) / hostname / "private.key" if cert_path.exists() and key_path.exists(): return # 证书已存在 # 创建目录 cert_path.parent.mkdir(parents=True, exist_ok=True) print(f"[bootstrap] 为 {hostname} 生成自签证书...") # 使用 openssl 生成自签证书 try: subprocess.run( [ "openssl", "req", "-x509", "-newkey", "rsa:2048", "-keyout", str(key_path), "-out", str(cert_path), "-days", "365", "-nodes", "-subj", f"/CN={hostname}", "-addext", f"subjectAltName=DNS:{hostname}", ], capture_output=True, check=True, ) print(f"[bootstrap] 自签证书已生成: {cert_path}") print(f"[bootstrap] 客户端部署脚本将使用 curl -k 跳过 TLS 验证") except FileNotFoundError: print("[bootstrap] 未找到 openssl,跳过自签证书生成") print("[bootstrap] 请手动配置 HTTPS 证书,或使用 HTTP 模式") except subprocess.CalledProcessError as e: print(f"[bootstrap] 生成自签证书失败: {e.stderr.decode()}")