57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
"""
|
|||
|
|
首次启动引导:为 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()}")
|