Allow one server-audited onboarding polish request without credits and make the certificate gate temporarily reversible while preserving application authentication.
管理端 mTLS 部署
account.osglab.com 在同一个 TLS server 中同时承载移动端 API 和管理端。
由于 TLS 握手发生在 HTTP 路径匹配之前,配置必须使用 server 级
ssl_verify_client optional:普通客户端不提供证书时仍可正常访问。OpenResty 会把实际
证书验证结果传给 Ktor;ADMIN_MTLS_REQUIRED=true(默认值)时,/admin、/admin/、
/v1/admin 和其子路径要求验证成功。
临时关闭
在 1Panel/Compose 环境中显式设置并重启应用:
ADMIN_MTLS_REQUIRED=false
关闭后,管理端无需客户端证书,但登录仍要求用户名、密码和 TOTP,其他会话、CSRF、RBAC、
失败锁定、限流与审计规则保持不变。恢复时将该值改回 true 并重启应用。不要为了临时关闭
而删除客户端 CA、证书或轮换记录。
CA 与证书
- 为管理客户端创建独立私有 CA,不要复用公网服务端证书 CA 或其他内部 CA。
- CA 私钥离线保存;不要放入仓库、OpenResty 主机或容器镜像。
- 管理客户端证书使用短有效期和唯一密钥,并限制为 TLS Client Authentication
(
clientAuth) 用途。 - 仅将 CA 证书链(不含任何私钥)部署到:
/www/server/openresty/conf/mtls/admin-client-ca.pem - CA 文件由 OpenResty 运行用户只读,目录不可由应用进程或非特权用户写入。
- 更新 CA 文件后先运行
openresty -t,成功后再平滑重载。若需要立即吊销证书, 应另外配置并维护ssl_crl;当前配置只依据证书链和有效期验证。
不要把客户端证书、客户端私钥、CA 私钥或生产证书标识提交到仓库。 计划轮换与紧急处置步骤见 ROTATION.md。
上游信任边界
OpenResty 在管理路径用 $ssl_client_verify 覆盖并转发证书验证结果。有效证书对应:
X-OSG-mTLS-Verified: SUCCESS
无证书时该值为 NONE。客户端传入的同名头会被覆盖;其他路径会删除该头。Ktor 只能把
OpenResty 写入的这个头作为“边缘已验证”信号,不能信任客户端提供的证书相关头,也不能用
DN、CN 或证书正文做隐式授权。
后端端口必须继续只监听 127.0.0.1:18080,否则攻击者可绕过边缘伪造该头。
mTLS 只证明客户端持有受信证书,管理接口仍应执行应用层身份认证、授权和审计。
一次性管理员 Bootstrap
首次部署前运行 ./gradlew generateAdminCredentials,将生成的 runtime 文件仅临时写入
1Panel/Compose 环境,并同时设置:
ADMIN_ENABLED=true
ADMIN_BOOTSTRAP_ENABLED=true
确认初始管理员已创建且可以登录后,必须将 ADMIN_BOOTSTRAP_ENABLED 改回 false,
并从 1Panel、Compose 环境和部署文件中永久删除
ADMIN_BOOTSTRAP_OPERATOR_ID、ADMIN_BOOTSTRAP_USERNAME、
ADMIN_BOOTSTRAP_PASSWORD_HASH、ADMIN_BOOTSTRAP_TOTP_SECRET_BASE32。
日常运行只保留 ADMIN_ENABLED=true。重启后再次验证登录,确保服务不再依赖 Bootstrap
秘密。
验证
ADMIN_MTLS_REQUIRED=true 时,将测试域名解析到目标边缘后执行:
# 无证书:管理路径必须是 404。
curl -i https://account.osglab.com/admin
curl -i https://account.osglab.com/v1/admin
# 有效管理证书:请求应到达 Ktor,状态码由管理接口决定。
curl -i --cert admin-client.pem --key admin-client-key.pem \
https://account.osglab.com/v1/admin
# 无证书的普通移动端 API:响应应与变更前一致。
curl -i https://account.osglab.com/health
# 即使客户端伪造信任头,无证书访问管理路径仍必须是 404。
curl -i -H 'X-OSG-mTLS-Verified: SUCCESS' \
https://account.osglab.com/v1/admin
还应使用由非管理 CA 签发或已过期的客户端证书确认返回 404,并在 Ktor 测试端点确认:
管理请求只收到固定值 SUCCESS,普通 API 不收到 X-OSG-mTLS-Verified。
ADMIN_MTLS_REQUIRED=false 时,无证书访问 /admin/ 应返回管理页面,
/v1/admin/auth/session 应返回匿名会话状态。伪造 X-OSG-mTLS-Verified: SUCCESS 不会
改变结果,因为 OpenResty 会将其覆盖为实际验证状态。
部署后可在受信设备运行不含登录凭据的自动验收:
ADMIN_CLIENT_CERT=/secure/path/admin-client.pem \
ADMIN_CLIENT_KEY=/secure/path/admin-client-key.pem \
bash deploy/verify-admin.sh
脚本验证公开健康检查、无证书隐藏、伪造边缘头拦截、有效证书访问,以及 HSTS/CSP 安全响应头;它不会读取或传输管理员密码和 TOTP 密钥。