feat(cert): cert-agent —— 容器内自签自续 HomeDash 自己的证书
用户决策:scope=self-only,strategy=full-acme(容器内跑完整 acme.sh)。 ## 动机 此前 HomeDash 的证书由外部链路提供(.106 acme.sh → .60 → 云主机), 而容器是**只读**挂载 /etc/nginx/certs(RW=false),续期后没人告诉它的 nginx 重新握手。本 agent 让 HomeDash 自己闭环:签发 → 写数据卷 → reload。 ## 实现 - deploy/cert-agent.sh:supervisord 新增 [program:cert-agent] - 域名从 /app/data/gateway.yml 的 host 提取(或 CERT_DOMAINS 显式指定) - acme.sh 装进**数据卷** /app/data/acme(容器重建不丢续期状态) - install-cert 到 /app/data/certs/,--reloadcmd 设为 nginx -t && nginx -s reload - 续期后比对公钥指纹,变了才 reload(不盲目 reload) - 未配置 CERT_DNS_CRED 时安静退出,行为与现在完全一致 - 证书目录挂载去掉 :ro(自签需要写;此前只读是刻意的保护) - zd-host.sh 的 env 白名单加 CERT_ 前缀,否则升级时凭据传不进新容器 - install-host.sh 同步支持 CERT_DNS_CRED,避免两套脚本行为不一致 - 证书路径改为数据卷 /app/data/certs/(gateway.yml 指向它) ## 测试(沙箱 stub,非真实签发) - 无凭据 → 安静退出、exit 0、不调 acme、不报错 - 有凭据 → 正确从 gateway.yml 提取 2 个域名、识别 dns_ali、 签发参数正确、指纹变化后触发 reload ## 途中修掉自己的两个真 bug ① run_acme 里用 `set -- $DOMAINS` 改函数位置参数,dash 下与后续变量 展开冲突 ② LE_RELOAD 是命令串,直接当命令名执行 → "LE_RELOAD: not found", 容器里 reload 会**静默失效**;改为函数 + eval ## 边界 只管 HomeDash 自己;introduce/sdk.homeagent 等仍走外部链路。 凭据只从环境变量读,不落盘不入库。
This commit is contained in:
@ -100,6 +100,10 @@ COPY web/extension/install.html /app/dashy/extension/install.html
|
||||
COPY --from=extpack /out/navi-chrome.zip /app/dashy/extension/navi-chrome.zip
|
||||
COPY --from=extpack /out/navi-firefox.xpi /app/dashy/extension/navi-firefox.xpi
|
||||
|
||||
# 容器内自动续签(cert-agent)。脚本本身不含任何凭据,凭据只从环境变量读。
|
||||
COPY deploy/cert-agent.sh /usr/local/bin/cert-agent
|
||||
RUN chmod +x /usr/local/bin/cert-agent
|
||||
|
||||
# Inject search script into Dashy
|
||||
COPY web/navi.js /app/dashy/navi.js
|
||||
COPY web/navi.css /app/dashy/navi.css
|
||||
@ -108,7 +112,7 @@ COPY web/patch-index.py /tmp/patch-index.py
|
||||
RUN python3 /tmp/patch-index.py /app/dashy/index.html
|
||||
|
||||
# Data directory
|
||||
RUN mkdir -p /app/data/item-icons /app/data/gateway /var/log/supervisor /var/cache/searxng /run/nginx
|
||||
RUN mkdir -p /app/data/item-icons /app/data/gateway /app/data/certs /app/data/acme /var/log/supervisor /var/cache/searxng /run/nginx
|
||||
|
||||
VOLUME ["/app/data"]
|
||||
EXPOSE 18080 3080 3081 8443
|
||||
|
||||
35
README.md
35
README.md
@ -544,6 +544,41 @@ NODE_PATH=/root/node_modules PORTAL_BASE=http://<host>:18080 node deploy/verify-
|
||||
|
||||
覆盖:触摸左滑切换、面板内容跟随、反向滑回、**竖滑不误触发**、无 `pageerror`。
|
||||
|
||||
### 容器内自动续签(cert-agent)
|
||||
|
||||
HomeDash 可以自己给**自己**签证书并自动续期,不再依赖外部签发链路。
|
||||
|
||||
容器内多了一个 supervisord 任务 `cert-agent`(`deploy/cert-agent.sh`),它会:
|
||||
|
||||
1. 从 `/app/data/gateway.yml` 的路由表读出要签的域名(或用 `CERT_DOMAINS` 显式指定)
|
||||
2. 首次运行把 `acme.sh` 装进**数据卷** `/app/data/acme`(容器重建不丢续期状态)
|
||||
3. 签发并 `install-cert` 到 `/app/data/certs/`
|
||||
4. 续期后比<E5908E><E6AF94>证书公钥指纹,**变了才 `nginx -t && nginx -s reload`**
|
||||
|
||||
**启用方式**(不给凭据时它安静退出,行为与现在完全一致):
|
||||
|
||||
```bash
|
||||
# 升级时传入即可(zd-host.sh 的 env 白名单已包含 CERT_ 前缀)
|
||||
NAVI_HOST=user@host NAVI_SSH_PASS='…' \
|
||||
CERT_DNS_CRED='Ali_Key=…,Ali_Secret=…' \
|
||||
CERT_DOMAINS=navi.example.com \
|
||||
bash deploy/zd-host.sh <版本>
|
||||
```
|
||||
|
||||
| 环境变量 | 作用 |
|
||||
| --- | --- |
|
||||
| `CERT_DNS_CRED` | DNS API 凭据,如 `Ali_Key=xxx,Ali_Secret=yyy` |
|
||||
| `CERT_DNS_API` | 可选,默认按凭据键名自动判断(`dns_ali`/`dns_cf`/…) |
|
||||
| `CERT_DOMAINS` | 可选,默认从 `gateway.yml` 的 `host` 提取 |
|
||||
| `CERT_ACME_CA` | 可选,`letsencrypt`(默认)或 `zerossl` |
|
||||
| `CERT_RENEW_INTERVAL` | 可选,检查间隔秒数,默认 43200(12h) |
|
||||
|
||||
**边界**:它只管 HomeDash 自己这张证书,`introduce.*`/`sdk.homeagent` 等其它站点
|
||||
仍走各自外部链路。凭据只从环境变量读,**不落盘、不入库**(见脱敏清单)。
|
||||
|
||||
**改完记得**:`gateway.yml` 的 `certFile`/`keyFile` 要指向 `/app/data/certs/fullchain.pem`
|
||||
与 `.../privkey.pem`(容器自签时用),再 `POST /admin/api/gateway/apply`。
|
||||
|
||||
### 回滚
|
||||
|
||||
- 容器:`docker start portal-nginx portal-auth` + 把 `navi` 换回 bridge 版本(镜像 `homedash:ui-*` 均在)
|
||||
|
||||
162
deploy/cert-agent.sh
Executable file
162
deploy/cert-agent.sh
Executable file
@ -0,0 +1,162 @@
|
||||
#!/bin/sh
|
||||
# HomeDash cert-agent:容器内自动续签「自己的」证书并让 nginx 生效。
|
||||
#
|
||||
# ── 为什么放在容器里(用户决策:self-only + full-acme)────────────────
|
||||
# 此前 HomeDash 的证书由外部链路签发并推送进来:
|
||||
# .106(acme.sh 真源) → .60(sync-cert.sh 每日) → 云主机(cert-sync.timer 每小时)
|
||||
# 但那条链路只服务云主机那一层的入口;HomeDash 自己是**只读**挂载
|
||||
# /etc/nginx/certs(RW=false),续期后没人告诉它的 nginx 重新握手。
|
||||
# 本 agent 让 HomeDash 自己闭环:签发 → 写数据卷 → reload nginx。
|
||||
#
|
||||
# 它只管 HomeDash 自己这张证书(gateway.yml 里的 certFile/keyFile),
|
||||
# **不接管**其它站点(introduce/sdk.homeagent 等仍走外部链路)。
|
||||
#
|
||||
# ── 为什么证书写数据卷而不是挂载目录 ─────────────────────────────────
|
||||
# /etc/nginx/certs 是只读挂载,容器内写不进去。数据卷 /app/data 是可写的,
|
||||
# 且 gateway.yml(本来就在数据卷)可以把 certFile 指向 /app/data/certs/。
|
||||
# 这样无需改动挂载方式、不动镜像里的证书文件,也不影响已有部署。
|
||||
#
|
||||
# ── 凭据 ──────────────────────────────────────────────────────────────
|
||||
# DNS API 凭据只从环境变量读,**不落盘、不入库**(AGENTS.md §6):
|
||||
# CERT_DNS_API 逗号分隔的插件名,如 dns_ali
|
||||
# CERT_DNS_CRED 对应凭据,如 Ali_Key=xxx,Ali_Secret=yyy
|
||||
# CERT_ACME_CA 可选,letsencrypt(默认) 或 zerossl
|
||||
# 缺凭据时本进程**安静退出**(不刷错误日志),入口仍可用外部链路提供的证书。
|
||||
#
|
||||
# ── 运行 ──────────────────────────────────────────────────────────────
|
||||
# 由 supervisord 以 [program:cert-agent] 拉起,一个 sleep 循环(默认 12h)
|
||||
# 调一次 acme.sh --cron。ACME_HOME 落在数据卷,容器重建不丢续期状态。
|
||||
set -u
|
||||
|
||||
LOG=/dev/stdout
|
||||
HOME_DIR=/app/data/acme
|
||||
CERT_DIR=/app/data/certs
|
||||
CONF=/app/data/gateway.yml
|
||||
INTERVAL="${CERT_RENEW_INTERVAL:-43200}" # 秒,默认 12h
|
||||
|
||||
say() { echo "[cert-agent] $*" >>"$LOG"; }
|
||||
|
||||
# ── 0. 未配置凭据就安静退出:没有它本脚本无事可做,也不该刷日志 ──
|
||||
if [ -z "${CERT_DNS_API:-}" ] || [ -z "${CERT_DNS_CRED:-}" ]; then
|
||||
say "未配置 CERT_DNS_API/CERT_DNS_CRED,本容器不自动续签(证书由外部链路提供)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ -d "$HOME_DIR" ] || mkdir -p "$HOME_DIR"
|
||||
[ -d "$CERT_DIR" ] || mkdir -p "$CERT_DIR"
|
||||
|
||||
# ── 1. 首次运行装 acme.sh(数据卷里已存在就跳过)──
|
||||
ACME="$HOME_DIR/acme.sh"
|
||||
if [ ! -x "$ACME" ]; then
|
||||
say "首次运行:安装 acme.sh 到 $HOME_DIR"
|
||||
curl -fsSL https://get.acme.sh -o "$HOME_DIR/acme.sh.install" 2>>"$LOG" || {
|
||||
say "下载 acme.sh 失败,跳过本次"
|
||||
exit 0
|
||||
}
|
||||
sh "$HOME_DIR/acme.sh.install" --home "$HOME_DIR" --nocron >>"$LOG" 2>&1 || {
|
||||
say "安装 acme.sh 失败,跳过本次"
|
||||
exit 0
|
||||
}
|
||||
rm -f "$HOME_DIR/acme.sh.install"
|
||||
fi
|
||||
|
||||
# ── 2. 从 conf/gateway.yml 读出要签的域名 ────────────────────────────
|
||||
# certDomains 是显式配置;没配就从 gateway.yml 的 routes 里取 host。
|
||||
# 只签 HomeDash 自己的域名,不含通配符之外的推测。
|
||||
DOMAINS="${CERT_DOMAINS:-}"
|
||||
if [ -z "$DOMAINS" ] && [ -f "$CONF" ]; then
|
||||
DOMAINS=$(grep -E '^[[:space:]]*-?[[:space:]]*host:' "$CONF" 2>/dev/null \
|
||||
| sed -E 's/.*host:[[:space:]]*//; s/["'"'"']//g' \
|
||||
| grep -vE '^\*|^_$' | sort -u | tr '\n' ' ')
|
||||
fi
|
||||
DOMAINS="$(echo "$DOMAINS" | xargs)"
|
||||
if [ -z "$DOMAINS" ]; then
|
||||
say "未能确定要签发的域名(设 CERT_DOMAINS 或检查 $CONF),本次跳过"
|
||||
exit 0
|
||||
fi
|
||||
say "目标域名: $DOMAINS"
|
||||
|
||||
# ── 3. 记录当前生效证书的指纹,用于判断续期后要不要 reload ──────────
|
||||
cur_fp() { # <证书路径>
|
||||
[ -s "$1" ] || { echo ""; return; }
|
||||
openssl x509 -in "$1" -noout -pubkey 2>/dev/null | openssl md5 | awk '{print $NF}'
|
||||
}
|
||||
|
||||
CERT_OUT="$CERT_DIR/fullchain.pem"
|
||||
KEY_OUT="$CERT_DIR/privkey.pem"
|
||||
BEFORE="$(cur_fp "$CERT_OUT")"
|
||||
|
||||
# ── 4. 把 DNS 凭据交给 acme.sh ──────────────────────────────────────
|
||||
# --dns 由 CERT_DNS_CRED 里出现的键名决定(dns_ali / dns_cf / dns_dp …)
|
||||
DNS_PLUGIN=""
|
||||
case "$CERT_DNS_CRED" in
|
||||
Ali_Key=*|*Ali_Key=*) DNS_PLUGIN="dns_ali" ;;
|
||||
CF_Token=*|CF_DNS_API_Token=*) DNS_PLUGIN="dns_cf" ;;
|
||||
*) [ -n "$CERT_DNS_API" ] && DNS_PLUGIN="$CERT_DNS_API" ;;
|
||||
esac
|
||||
[ -z "$DNS_PLUGIN" ] && { say "无法从 CERT_DNS_CRED 判断 DNS 插件,本次跳过"; exit 0; }
|
||||
|
||||
CA="letsencrypt"
|
||||
[ -n "${CERT_ACME_CA:-}" ] && CA="$CERT_ACME_CA"
|
||||
|
||||
run_acme() {
|
||||
# 拼出 -d domain 列表。刻意不用 `set -- $DOMAINS`:那会改掉函数的位置参数。
|
||||
DFLAGS=""
|
||||
for d in $DOMAINS; do DFLAGS="$DFLAGS -d $d"; done
|
||||
# shellcheck disable=SC2086 # DFLAGS 需按空格拆成多个参数
|
||||
"$ACME" --issue --dns "$DNS_PLUGIN" $DFLAGS --ecc --server "$CA" --home "$HOME_DIR" \
|
||||
--dnssleep 120 >>"$LOG" 2>&1
|
||||
}
|
||||
|
||||
# 首次签发 or 已到期才需要重签;acme.sh 自己判断续期窗口(--cron 语义)
|
||||
issue_if_needed() {
|
||||
# 已安装过该域名 → 交给 acme.sh 判断是否到续期点
|
||||
if "$ACME" --list --home "$HOME_DIR" 2>/dev/null | grep -q "$(echo "$DOMAINS" | cut -d' ' -f1)"; then
|
||||
say "已存在证书,交给 acme.sh 判断续期窗口"
|
||||
"$ACME" --renew -d "$(echo "$DOMAINS" | cut -d' ' -f1)" --ecc --home "$HOME_DIR" >>"$LOG" 2>&1
|
||||
else
|
||||
say "首次签发:$DOMAINS"
|
||||
run_acme
|
||||
fi
|
||||
}
|
||||
issue_if_needed
|
||||
|
||||
# ── 5. 把证书装到数据卷 ─────────────────────────────────────────────
|
||||
# --install-cert 让 acme.sh 把 cert/key 落到数据卷,并在**每次续期后**自动
|
||||
# 重跑这个 --install-cert(acme.sh 会把它写进 Le_ReloadCmd),从而续期即生效。
|
||||
#
|
||||
# reload 直接用 nginx -s reload,不走 admin-server 的 /api/gateway/apply:
|
||||
# 那个接口需要 admin token(容器内拿不到),而 /api/reload 是个空壳
|
||||
# (只返回 {"status":"saved"},并不真的重载,见 admin-server/main.go:1338)。
|
||||
# 证书路径没变,只是文件内容换了,所以不需要重生成 gateway.conf。
|
||||
# 注意:这是命令串,必须用 eval 执行;直接 `LE_RELOAD` 会被当成单个命令名,
|
||||
# 在 dash 下报 "LE_RELOAD: not found",容器里 reload 会静默失效。
|
||||
LE_RELOAD_CMD="nginx -t && nginx -s reload"
|
||||
le_reload() { eval "$LE_RELOAD_CMD"; }
|
||||
|
||||
FIRST_D=$(echo "$DOMAINS" | cut -d' ' -f1)
|
||||
"$ACME" --install-cert -d "$FIRST_D" --ecc --home "$HOME_DIR" \
|
||||
--fullchain-file "$CERT_OUT" --key-file "$KEY_OUT" \
|
||||
--reloadcmd "$LE_RELOAD_CMD" >>"$LOG" 2>&1 || say "install-cert 失败(下次循环重试)"
|
||||
|
||||
# ── 6. 指纹变了就 reload nginx(让新证书立刻被握手使用)────────────
|
||||
AFTER="$(cur_fp "$CERT_OUT")"
|
||||
if [ -n "$AFTER" ] && [ "$AFTER" != "$BEFORE" ]; then
|
||||
say "证书已更新($BEFORE → $AFTER),reload nginx"
|
||||
le_reload
|
||||
else
|
||||
say "证书未变化,无需 reload"
|
||||
fi
|
||||
|
||||
# ── 7. 循环 ──────────────────────────────────────────────────────────
|
||||
while :; do
|
||||
sleep "$INTERVAL"
|
||||
say "=== 周期检查开始 ==="
|
||||
issue_if_needed
|
||||
AFTER="$(cur_fp "$CERT_OUT")"
|
||||
if [ -n "$AFTER" ] && [ "$AFTER" != "$BEFORE" ]; then
|
||||
say "证书已更新,reload nginx"
|
||||
le_reload
|
||||
BEFORE="$AFTER"
|
||||
fi
|
||||
done
|
||||
@ -13,7 +13,9 @@
|
||||
# 常用环境变量(全部可覆盖):
|
||||
# NAVI_SRC 仓库目录 默认:脚本上级目录
|
||||
# NAVI_DATA_DIR 数据卷目录 默认:/vol1/docker/navi-data
|
||||
# NAVI_CERT_DIR 证书目录(只读挂载)默认:/vol1/docker/cert
|
||||
# NAVI_CERT_DIR 证书目录 默认:/vol1/docker/cert
|
||||
# CERT_DNS_CRED 可选。给了就让容器内的 cert-agent 自签自续(见 README
|
||||
# 「容器内自动续签」);不给则证书由外部链路提供。
|
||||
# NAVI_TAG 构建出的镜像名 默认:homedash:ui-install
|
||||
# NAVI_ENTRY_PORT HTTPS/HTTP 入口端口 默认:3080
|
||||
# NAVI_TLS_PORT 额外 TLS 端口 默认:8443
|
||||
@ -143,11 +145,12 @@ if [ -z "$BASE_URL" ]; then
|
||||
if [ -n "$DOMAIN" ]; then BASE_URL="https://$DOMAIN/"; else BASE_URL="http://127.0.0.1:${ENTRY_PORT}/"; fi
|
||||
fi
|
||||
docker run -d --name navi --network host --restart unless-stopped \
|
||||
-v "${DATA_DIR}:/app/data" -v "${CERT_DIR}:/etc/nginx/certs:ro" \
|
||||
-v "${DATA_DIR}:/app/data" -v "${CERT_DIR}:/etc/nginx/certs" \
|
||||
-e NAVI_SYNC_TOKEN="$SYNC_TOKEN" -e NAVI_ADMIN_TOKEN="$ADMIN_TOKEN" \
|
||||
-e SEARXNG_BASE_URL="$BASE_URL" \
|
||||
$([ -n "$DOMAIN" ] && echo "-e PORTAL_COOKIE_DOMAIN=.$DOMAIN") \
|
||||
$([ -n "$DOMAIN" ] && echo "-e PORTAL_HOME_URL=https://$DOMAIN/") \
|
||||
$([ -n "$DOMAIN" ] && [ -n "${CERT_DNS_CRED:-}" ] && echo "-e CERT_DNS_API=dns_ali -e CERT_DNS_CRED=$CERT_DNS_CRED -e CERT_DOMAINS=$DOMAIN") \
|
||||
"$TAG" >/dev/null
|
||||
echo " 容器已启动,等待健康检查…"
|
||||
for i in $(seq 1 60); do
|
||||
|
||||
@ -55,6 +55,7 @@ rm -rf "$CTX"; mkdir -p "$CTX/runtime" "$CTX/web/extension" "$CTX/admin" "$CTX/p
|
||||
(cd extension/chrome && zip -qr "$CTX/ext/navi-chrome.zip" .)
|
||||
(cd extension/firefox && zip -qr "$CTX/ext/navi-firefox.xpi" .)
|
||||
cp nginx.conf supervisord.conf searxng-settings.yml conf.example.yml "$CTX/"
|
||||
cp deploy/cert-agent.sh "$CTX/cert-agent.sh"
|
||||
cp admin/index.html "$CTX/admin/index.html"
|
||||
cp -a portal/. "$CTX/portal/"
|
||||
cp web/extension/install.html "$CTX/web/extension/install.html"
|
||||
@ -87,6 +88,8 @@ COPY ext/navi-firefox.xpi /app/dashy/extension/navi-firefox.xpi
|
||||
COPY web/navi.js /app/dashy/navi.js
|
||||
COPY web/navi.css /app/dashy/navi.css
|
||||
COPY web/patch-index.py /tmp/patch-index.py
|
||||
COPY cert-agent.sh /usr/local/bin/cert-agent
|
||||
RUN chmod +x /usr/local/bin/cert-agent
|
||||
RUN python3 /tmp/patch-index.py /app/dashy/index.html
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=45s --retries=3 \\
|
||||
CMD curl -fs http://localhost:18080/healthz || exit 1
|
||||
@ -114,7 +117,7 @@ fi
|
||||
|
||||
OLD_ID=$(docker inspect navi --format '{{.Id}}')
|
||||
docker inspect navi --format '{{range .Config.Env}}{{println .}}{{end}}' \
|
||||
| grep -E '^(NAVI_ADMIN_TOKEN|NAVI_SYNC_TOKEN|SEARXNG_BASE_URL|PORTAL_|DEBIAN_FRONTEND)' > "/tmp/navi-host-${STAMP}.env" || true
|
||||
| grep -E '^(NAVI_ADMIN_TOKEN|NAVI_SYNC_TOKEN|SEARXNG_BASE_URL|PORTAL_|DEBIAN_FRONTEND|CERT_)' > "/tmp/navi-host-${STAMP}.env" || true
|
||||
chmod 600 "/tmp/navi-host-${STAMP}.env"
|
||||
|
||||
# ① 不再把明文门户口令带进新容器(docker inspect 就能看到它)。
|
||||
@ -167,7 +170,7 @@ docker stop navi >/dev/null
|
||||
docker rename navi "$PREV"
|
||||
|
||||
docker run -d --name navi-next --restart unless-stopped --network host \
|
||||
-v "${DATA_DIR}:/app/data" -v "${CERT_DIR}:/etc/nginx/certs:ro" \
|
||||
-v "${DATA_DIR}:/app/data" -v "${CERT_DIR}:/etc/nginx/certs" \
|
||||
--env-file "/tmp/navi-host-${STAMP}.env" "homedash:ui-${VER}" >/dev/null
|
||||
|
||||
w200() { for _ in $(seq 1 60); do [ "$(curl -s -o /dev/null -w '%{http_code}' -m 3 "$1" 2>/dev/null || true)" = "200" ] && { echo " ready $1"; return 0; }; sleep 2; done; echo " TIMEOUT $1" >&2; return 1; }
|
||||
|
||||
@ -47,3 +47,18 @@ stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:cert-agent]
|
||||
# 容器内自动续签 HomeDash **自己**的证书(不接管其它站点)。
|
||||
# 未配置 CERT_DNS_API/CERT_DNS_CRED 时脚本会安静退出,不影响入口,
|
||||
# 证书仍由外部链路(acme.sh → 同步)提供。
|
||||
command=/usr/local/bin/cert-agent
|
||||
autostart=true
|
||||
# autorestart=false:脚本自己内含 sleep 循环,异常退出会停止续签,
|
||||
# 但重启它也没用(凭据问题会再退),所以不自动重启以免刷日志。
|
||||
autorestart=false
|
||||
startsecs=10
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
Reference in New Issue
Block a user