feat(nginx): add ssl_cert_source/existing paths for HTTPS
Previously the SSL block always generated a self-signed cert at a
hardcoded /etc/nginx/ssl/<server_name>.{crt,key}. That worked for
test/internal use but is unusable for production HTTPS (Let's Encrypt
/ commercial certs): users couldn't point nginx at their own files
without editing the script after generation.
Add three fields (defaults preserve prior behavior):
ssl_cert_source select self_signed|existing
self_signed (default) - existing behavior,
openssl req generates the cert at
/etc/nginx/ssl/<server_name>.{crt,key}.
existing - skip openssl entirely,
use user-supplied ssl_cert_path /
ssl_key_path.
ssl_cert_path text absolute path to fullchain/cert (existing only)
ssl_key_path text absolute path to private key (existing only)
Validation (raised at render time, before script generation):
- ssl_cert_source=existing requires both paths
- both paths must start with '/' (absolute)
Runtime behavior when existing files aren't readable (e.g. certbot
hasn't run yet): script emits a clear WARN with a hint to deploy the
cert first, and continues. nginx -t will fail in that case but the
operator can re-run the script after the cert is in place, or run
'nginx -s reload' directly.
Verified:
- self_signed: full pipeline OK, real openssl produces a valid cert,
real nginx -t passes.
- existing + Let's Encrypt style paths pointing at real certs:
nginx -t passes; no openssl invocation in the script.
- existing + cert files absent: WARN emitted on stderr, script exits 0,
nginx -t skipped (would fail by design — operator runs certbot then
reloads).
- existing with missing path: ValueError at render time.
- existing with relative path: ValueError at render time.
- All 50 generators still pass 'bash -n'.
This commit is contained in:
@@ -389,8 +389,18 @@ class Nginx(Generator):
|
|||||||
Field("root_path", "网站根目录", "text", default="/var/www/html",
|
Field("root_path", "网站根目录", "text", default="/var/www/html",
|
||||||
help="静态文件根目录,留空使用默认。"),
|
help="静态文件根目录,留空使用默认。"),
|
||||||
Field("enable_ssl", "启用 HTTPS", "checkbox", default="no",
|
Field("enable_ssl", "启用 HTTPS", "checkbox", default="no",
|
||||||
help="启用后会在 443 监听并生成自签名证书。"),
|
help="启用后会在 443 监听并生成 SSL 配置。"),
|
||||||
Field("ssl_port", "HTTPS 端口", "number", default="443", min_=1, max_=65535),
|
Field("ssl_port", "HTTPS 端口", "number", default="443", min_=1, max_=65535),
|
||||||
|
Field("ssl_cert_source", "SSL 证书来源", "select", default="self_signed",
|
||||||
|
options=["self_signed", "existing"],
|
||||||
|
help="self_signed=脚本生成自签证书(浏览器告警,适合测试);"
|
||||||
|
"existing=使用已有证书(Let's Encrypt/商业证书)"),
|
||||||
|
Field("ssl_cert_path", "证书路径", "text", default="",
|
||||||
|
placeholder="/etc/letsencrypt/live/example.com/fullchain.pem",
|
||||||
|
help="ssl_cert_source=existing 时必填。绝对路径。"),
|
||||||
|
Field("ssl_key_path", "私钥路径", "text", default="",
|
||||||
|
placeholder="/etc/letsencrypt/live/example.com/privkey.pem",
|
||||||
|
help="ssl_cert_source=existing 时必填。绝对路径。"),
|
||||||
Field("enable_proxy", "启用反向代理", "checkbox", default="no"),
|
Field("enable_proxy", "启用反向代理", "checkbox", default="no"),
|
||||||
Field("proxy_pass", "Proxy Pass 地址", "text", default="http://127.0.0.1:8080",
|
Field("proxy_pass", "Proxy Pass 地址", "text", default="http://127.0.0.1:8080",
|
||||||
placeholder="http://127.0.0.1:8080",
|
placeholder="http://127.0.0.1:8080",
|
||||||
@@ -407,11 +417,27 @@ class Nginx(Generator):
|
|||||||
root = p.get("root_path", "/var/www/html")
|
root = p.get("root_path", "/var/www/html")
|
||||||
ssl = bool_str(p.get("enable_ssl"))
|
ssl = bool_str(p.get("enable_ssl"))
|
||||||
ssl_port = str(p.get("ssl_port", "443"))
|
ssl_port = str(p.get("ssl_port", "443"))
|
||||||
|
ssl_src = p.get("ssl_cert_source", "self_signed")
|
||||||
|
ssl_cert_path = p.get("ssl_cert_path", "").strip()
|
||||||
|
ssl_key_path = p.get("ssl_key_path", "").strip()
|
||||||
proxy = bool_str(p.get("enable_proxy"))
|
proxy = bool_str(p.get("enable_proxy"))
|
||||||
proxy_pass = p.get("proxy_pass", "http://127.0.0.1:8080")
|
proxy_pass = p.get("proxy_pass", "http://127.0.0.1:8080")
|
||||||
cmbs = p.get("client_max_body_size", "50m")
|
cmbs = p.get("client_max_body_size", "50m")
|
||||||
wp = p.get("worker_processes", "auto")
|
wp = p.get("worker_processes", "auto")
|
||||||
|
|
||||||
|
# SSL cert resolution
|
||||||
|
if ssl:
|
||||||
|
if ssl_src == "existing":
|
||||||
|
if not ssl_cert_path or not ssl_key_path:
|
||||||
|
raise ValueError(
|
||||||
|
"ssl_cert_source=existing 时必须同时填写 ssl_cert_path "
|
||||||
|
"和 ssl_key_path"
|
||||||
|
)
|
||||||
|
if not ssl_cert_path.startswith("/") or not ssl_key_path.startswith("/"):
|
||||||
|
raise ValueError(
|
||||||
|
"ssl_cert_path / ssl_key_path 必须是绝对路径(以 / 开头)"
|
||||||
|
)
|
||||||
|
|
||||||
out = [bash_header(self.title)]
|
out = [bash_header(self.title)]
|
||||||
out.append('log "Installing nginx..."')
|
out.append('log "Installing nginx..."')
|
||||||
out.append('PKG_INSTALL nginx openssl curl')
|
out.append('PKG_INSTALL nginx openssl curl')
|
||||||
@@ -462,18 +488,32 @@ class Nginx(Generator):
|
|||||||
out.append(f"cat > /etc/nginx/conf.d/{sn}.conf <<'CONF_EOF'\n{siteconf}CONF_EOF")
|
out.append(f"cat > /etc/nginx/conf.d/{sn}.conf <<'CONF_EOF'\n{siteconf}CONF_EOF")
|
||||||
out.append('rm -f /etc/nginx/sites-enabled/default')
|
out.append('rm -f /etc/nginx/sites-enabled/default')
|
||||||
if ssl:
|
if ssl:
|
||||||
out.append('log "Generating self-signed certificate..."')
|
if ssl_src == "self_signed":
|
||||||
out.append(f'openssl req -x509 -nodes -newkey rsa:2048 -days 365 \\\n'
|
# Default path: generate a self-signed cert at /etc/nginx/ssl/.
|
||||||
f' -keyout /etc/nginx/ssl/{sn}.key \\\n'
|
out.append('log "Generating self-signed certificate..."')
|
||||||
f' -out /etc/nginx/ssl/{sn}.crt \\\n'
|
out.append(f'openssl req -x509 -nodes -newkey rsa:2048 -days 365 \\\n'
|
||||||
f' -subj "/CN={sn}" 2>/dev/null')
|
f' -keyout /etc/nginx/ssl/{sn}.key \\\n'
|
||||||
|
f' -out /etc/nginx/ssl/{sn}.crt \\\n'
|
||||||
|
f' -subj "/CN={sn}" 2>/dev/null')
|
||||||
|
cert_path = f'/etc/nginx/ssl/{sn}.crt'
|
||||||
|
key_path = f'/etc/nginx/ssl/{sn}.key'
|
||||||
|
else: # existing
|
||||||
|
# Use user-provided paths. We do NOT overwrite their files; if
|
||||||
|
# the cert/key aren't readable, log a clear warning so they
|
||||||
|
# know to deploy the cert (e.g. run certbot) first.
|
||||||
|
out.append('log "Using user-provided SSL certificate..."')
|
||||||
|
out.append(f'for f in {ssl_cert_path} {ssl_key_path}; do\n'
|
||||||
|
f' [ -r "$f" ] || warn "SSL file not readable: $f (deploy your cert first, e.g. certbot)"\n'
|
||||||
|
f'done')
|
||||||
|
cert_path = ssl_cert_path
|
||||||
|
key_path = ssl_key_path
|
||||||
out.append(f"cat > /etc/nginx/conf.d/{sn}-ssl.conf <<'CONF_EOF'\n"
|
out.append(f"cat > /etc/nginx/conf.d/{sn}-ssl.conf <<'CONF_EOF'\n"
|
||||||
'server {\n'
|
'server {\n'
|
||||||
f' listen {ssl_port} ssl;\n'
|
f' listen {ssl_port} ssl;\n'
|
||||||
f' listen [::]:{ssl_port} ssl;\n'
|
f' listen [::]:{ssl_port} ssl;\n'
|
||||||
f' server_name {sn};\n'
|
f' server_name {sn};\n'
|
||||||
f' ssl_certificate /etc/nginx/ssl/{sn}.crt;\n'
|
f' ssl_certificate {cert_path};\n'
|
||||||
f' ssl_certificate_key /etc/nginx/ssl/{sn}.key;\n'
|
f' ssl_certificate_key {key_path};\n'
|
||||||
f' root {root};\n'
|
f' root {root};\n'
|
||||||
f' client_max_body_size {cmbs};\n'
|
f' client_max_body_size {cmbs};\n'
|
||||||
' index index.html;\n'
|
' index index.html;\n'
|
||||||
|
|||||||
Reference in New Issue
Block a user