DeepSeek Harness公网部署:Nginx反向代理与多层鉴权完整实战
搞过大模型 API 集成的人都有这种体验:本地写个脚本调用 DeepSeek 接口,三分钟就能跑通;但一旦想把整套东西放到云服务器上,让同事甚至办公室外也能访问,要处理的问题立刻变成一长串——环境怎么装、服务怎么常驻、端口怎么暴露、怎么防止被各种公网扫描器盯上。这篇文章记录的就是我实际部署 DeepSeek Harness 的完整流程,重点放在“公网鉴权访问”这个最容易踩坑的环节:从服务器选型、环境初始化、应用部署,到用 nginx 做反向代理、上 TLS 证书、加多层访问鉴权,最终交付一个跑在公网上、只有你和你允许的人能打开的工具箱。
1. 项目定位与方案选型:算清楚再动手
1.1 DeepSeek Harness 到底解决什么问题
先把工具本身的边界说清楚。DeepSeek Harness 并不是模型本身,而是一层“模型调用与评测管线”。平时我们要深度调用 DeepSeek 系模型,通常习惯写一次性脚本:代码里塞 API Key,用 requests 发一条请求,打印结果完事。但实际项目里这套玩法很快就会撞到天花板——你有一批测试用例要定期跑,你需要记录每次请求的时延、Token 消耗、成功率,你需要把结果沉淀到一个统一面板供团队查看,你还可能需要给内部系统提供一个稳定、带鉴权的访问入口。
Harness 就是干这个的:它把你的调用逻辑、评测任务、结果记录和对外接口统一封装起来。它的典型使用场景是团队内部做模型效果回归测试,或者做一个多场景 Prompt 的自动评测。再比如说,你想给其他服务提供一个统一的模型访问网关,也需要这种具备鉴权、日志、审计能力的工具层。它关注的是“稳定、可重复、可审计”,而不是单次调用的“快”。
1.2 为什么一定要部署在云服务器上
有人会问,我的开发机或者家里的小主机也能跑,为什么非要上云?如果你只是自己玩,确实无所谓;但一旦涉及团队协作、公网访问和历史数据留存,云服务器几乎是唯一靠谱的选择。原因有几个:
- 可访问性:云主机天然有独立公网 IP,Harness 部署上去就直接变成可访问的服务。你不需要处理家用宽带的出口 IP、运营商封锁之类的麻烦事。
- 稳定性:云服务器即便重启,也能通过开机自启把服务拉起来。家用设备断电、断网误伤服务的案例太多了。
- 资源隔离:评测任务通常有比较集中的 CPU 和网络开销,放到独立服务器上不会和你日常办公环境互相抢资源。
- 资产安全:API Key、评测数据、日志这些敏感资产留在一个受控的服务器上,比散落在个人电脑里更安全,也更容易做备份和审计。
当然代价是每个月要产生一点云资源费用。我个人的建议是:如果只是做评测跑批、公网访问量不大,一台 2 核 4G 的入门云主机就够了,完全不需要配 GPU。这里要特别说清楚:DeepSeek Harness 默认的角色是“调用 DeepSeek 官方 API”,真正的模型推理发生在模型服务商的集群里,你的服务器只负责组请求、调度任务、存结果,所以 CPU 型云主机完全能胜任。只有在极少数场景下,你希望 Harness 在同一台机器上拉起本地推理模型做私有化评测,才需要考虑带 GPU 的实例。
1.3 整体架构:一层一层拆开
部署之前,先把最终要达成的架构在脑子里过一遍。整条链路是这样的:
客户端(浏览器 / 其他服务) -> 域名解析 -> 云服务器安全组 -> nginx 反向代理(TLS + Basic Auth) -> DeepSeek Harness 应用服务(自校验 Token) -> 模型 API / 评测任务存储
用文字讲清楚每一层是干什么的:
- 客户端通过 HTTPS 访问你的域名,传输层是加密的,中间人拿不到明文内容;
- 云平台安全组是第一道闸门,只放行可信来源 IP 访问,这是网络层最粗粒度的过滤;
- nginx 是公网入口,负责终止 TLS,同时用 HTTP Basic Auth 拦住绝大多数没有凭证的扫描流量;
- Harness 应用层再校验一次自定义 Token,这是给程序化调用准备的第二把钥匙;
- Harness 内部拿着你自己的模型 API Key 去请求模型服务,并把评测结果写入本地数据库。
很多第一次上手的人会问:为什么搞这么多层?道理很简单,公网是一个丛林,任何单点防护都可能失效。TLS 防的是传输过程被窃听,Basic Auth 防的是陌生人随手打开你的面板,安全组防的是来源网络不可信,应用层 Token 防的是有人绕过浏览器界面直接打底层接口。你不是在给某一个场景加锁,而是在给一整条链路加锁。只要还有一层在工作,攻击者就必须继续突破。
2. 服务器初始化:开箱之后先别急着装东西
2.1 云主机选型和安全组规则
先聊服务器本身。Harness 对机器的硬件要求不高,但有几个软性条件必须核实清楚,否则后面会非常被动。
- 操作系统:优先选 Ubuntu 22.04 LTS 或 Debian 12。原因是包管理稳定,Python 版本能满足 Harness 要求,就算遇到问题,社区资料也多。
- 磁盘:至少 40G。别看 Harness 本身很小,日志、测试集、数据库、模型评测报告会慢慢长出来。磁盘预留少了,跑三个月你就得半夜起来清理。
- 网络:确认云厂商给你分配的是独立公网 IP,而不是 NAT 型的临时公网。后面绑域名、签发 TLS 证书都依赖它。
- 带宽:日常评测任务对带宽要求不高,1M 上行足够跑 API JSON 请求。如果要在面板上在线查看大体积报告,建议选 5M 以上。
安全组配置是很多人容易忽略的重点。原则是“最小放行”,只开发必要端口:
22/TCP:SSH 管理,来源限定为你自己的办公出口 IP;80/TCP:HTTP,用于 TLS 证书签发和访问重定向;443/TCP:HTTPS,用户真正访问 Harness 的端口;8900/TCP:Harness 应用端口,这条规则绝对不要出现在公网放行里。
这里的核心逻辑在于:安全组是云平台上做的网络层过滤,哪怕 Harness 应用本身出了安全漏洞,外部流量也到不了 8900 端口。很多资料只教你关主机防火墙,却漏了安全组这一层。其实前者是主机内的最后一道防线,后者才是云环境的第一道防线,两层都要做。
2.2 系统更新与普通用户创建
拿到服务器后先登录,第一步是把系统更新到最新,然后创建一个日常运行的普通用户。任何服务都不应该用 root 身份常驻,这不是偏执,而是最基本的运维习惯。
我用的流程是这样的:
- 登录后先确认系统版本和架构:
lsb_release -a uname -m正常情况下应该是 Ubuntu 22.04 和 x86_64。
- 更新系统:
apt update && apt upgrade -y- 创建用户并加入 sudo 组:
adduser app usermod -aG sudo app- 配置 SSH 公钥登录。如果是新机器,直接把公钥写到
~app/.ssh/authorized_keys。这一步做完,后续就可以禁用 root 密码登录了。我自己一般只在云控制台里留 root 作为应急入口,平时全用app用户操作。
这里有一个很关键的权限细节:后面所有 Harness 相关目录都放在/opt/harness,属主设置为app:app,但包含密钥的配置文件会把权限收紧到600。这样即使某个目录被扫描到,没有属主权限的文件也无法被其他系统用户读取。
2.3 安装基础软件与 Python 环境
DeepSeek Harness 是基于 Python 的应用,所以先装好 Python 3.10+、pip、venv,以及后续要用的 Nginx 和证书签发工具:
apt install -y python3 python3-venv python3-pip nginx certbot python3-certbot-nginx装完后检查版本:
python3 --versionDebian/Ubuntu 自带 Python 版本一般都能满足 3.10+ 的要求。如果系统自带版本过低,建议用 apt 装对应版本,而不是到官网盲下源码包。用 venv 而不是直接把依赖装到系统 Python,是为了避免不同项目间的第三方包互相污染。之后你在升级 Harness 时,只需要重建 venv 就行。
我部署时固定了一套依赖安装命令:
python3 -m venv /opt/harness/venv /opt/harness/venv/bin/pip install -U pip /opt/harness/venv/bin/pip install -r /opt/harness/requirements.txt注意,不要用 root 跑这个安装,一律用app用户。
3. DeepSeek Harness 源码落地与首次启动
3.1 获取源码和目录规划
假设你已经从内部渠道或开源社区拿到了 Harness 源码包,把它放到合适的位置。我习惯用/opt/harness,这个路径对所有用户都易读,也符合服务部署惯例。先把目录结构规划出来:
/opt/harness/ ├── app/ # 应用代码 ├── data/ # 数据库文件、评测结果 ├── logs/ # 运行日志 ├── scripts/ # 备份、启动脚本 ├── .env # 环境变量配置(权限 600) ├── requirements.txt # Python 依赖 └── run_server.py # 启动入口代码放进去的方式比较简单:
sudo mkdir -p /opt/harness sudo chown app:app /opt/harness su - app cd /opt/harness git clone <你的仓库地址> app请注意,data和logs目录后续权限都要设置为app:app。.env文件不要在代码仓库里提交,必须手动创建,这是安全底线。如果你是从压缩包部署,解压后同样要注意文件归属关系,避免某些文件以 root 属主存在,导致服务账号读不了。
3.2 核心配置文件逐个解释
接下来是 Harness 能跑起来最关键的一步:配置。项目运行基本靠.env提供参数。我贴一份经过实际验证的模板,并逐个解释为什么这样设:
HARNESS_HOST=127.0.0.1 HARNESS_PORT=8900 DATABASE_URL=sqlite:////opt/harness/data/harness.db LOG_LEVEL=INFO LOG_BODY=false MODEL_API_KEY=sk-你的模型APIKey MODEL_API_BASE=https://api.your-provider.com/v1 MODEL_NAME=deepseek-chat REQUEST_TIMEOUT=60 ADMIN_TOKEN=一串足够复杂的长随机字符串 ALLOWED_ORIGINS=https://harness.example.com几个关键点值得展开:
HARNESS_HOST必须写127.0.0.1,而不是0.0.0.0。原因很简单:不要让应用服务直接暴露在公网网卡上,统一交给 nginx 去代理。这样即使你误配了防火墙规则,应用本身也不会对外监听。
DATABASE_URL在最小部署里用 SQLite,文件放在/opt/harness/data下。好处是备份简单——拷走一个文件就是全量备份。当你评测任务并发很高、需要多实例部署时,再把这个连接串切到 PostgreSQL,应用代码不需要动。
MODEL_API_KEY是模型服务的密钥,要确保永远不要以明文形式写进代码仓库。有些人图方便把它写进启动脚本,结果在进程列表里直接被其他用户看到,这是非常常见的安全事故。
ADMIN_TOKEN是应用层的访问令牌,它最好是 32 字节以上的随机字符串,不要用类似123456或者生日日期这类东西。
LOG_BODY=false非常重要:评测工具有时候会在日志里记录完整请求响应内容,这些内容里可能包含用户敏感数据和 Token。默认开启的话,密钥容易被悄悄写进日志文件。我这里默认关掉,只有明确排查问题时才短暂打开。
3.3 初始化数据库并做本地冒烟测试
配置写完之后,先初始化数据库,再启动服务做验证。不同版本的 Harness 初始化命令可能不同,通常带一个管理命令。以我部署的这个版本为例:
cd /opt/harness/app /opt/harness/venv/bin/python manage.py migrate执行成功之后,/opt/harness/data/下应该生成一个harness.db文件。接着启动服务:
cd /opt/harness/app /opt/harness/venv/bin/python run_server.py如果一切正常,终端会看到类似 “Uvicorn running on http://127.0.0.1:8900” 的输出。此时在本机验证接口:
curl -H "X-Api-Token: $ADMIN_TOKEN" http://127.0.0.1:8900/api/health返回{"status":"ok"}之类就说明应用本身没问题。这里先用 curl 验证有一个隐藏好处:你可以在没有公网暴露的情况下,确认 Harness 应用层鉴权工作正常。否则后续所有问题混在一起,很难定位到底是 nginx 挂了还是应用本身挂了。
冒烟测试通过后,按 Ctrl+C 先停掉。我们接下来要做的是把它变成常驻服务,再由公网代理接进来。
4. 公网鉴权访问:nginx + TLS + 多层防护落地
4.1 为什么必须用 nginx 反向代理
有人会问:应用既然监听 127.0.0.1,为什么还要装 nginx?直接把HARNESS_HOST改成0.0.0.0,再到安全组放行 8900 不就能访问了?能,但那是反面教材。
理由有这么几条:
- TLS 卸载:HTTP 证书续期、HTTPS 解密都由 Nginx 统一处理,应用不需要关心加密协议细节;
- 集中访问控制:可以在 Nginx 这一层做 Basic Auth、IP 限制、请求限速,应用收到的基本都是相对干净的流量;
- 端口规范:对外只开放 80/443,而不是一个非标准的 8900 端口。很多扫描器默认就会扫常见非标端口;
- 扩展方便:后续如果要给面板挂静态报告、做路径转发,改一行 Nginx 配置就行,不用重启应用。
如果你的部署环境里没有域名,只有裸 IP,也不是不能做,但 TLS 证书部分会比较麻烦,要么选择 IP 证书,要么接受浏览器“证书不可信”的提示。所以我强烈建议在云厂商那里注册一个域名并解析到服务器 IP,成本很低,而且能免费申请可信证书。配置里用的harness.example.com是示意,实际操作时换成你自己的域名。
4.2 Nginx 配置文件完整示例
下面是我在部署中实际使用的 Nginx 配置,逻辑很简单:任何明文 HTTP 都 301 到 HTTPS,真正的站点只跑在 443:
server { listen 80; server_name harness.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name harness.example.com; ssl_certificate /etc/letsencrypt/live/harness.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/harness.example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; auth_basic "Harness Restricted Area"; auth_basic_user_file /etc/nginx/.htpasswd; location / { proxy_pass http://127.0.0.1:8900; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } client_max_body_size 20m; }注意proxy_set_header为什么重要:Harness 的请求日志在记录来源 IP 时,读的是X-Forwarded-For或X-Real-IP头。如果你不配置这些头,所有请求在应用看来都来自127.0.0.1,来源审计就废了。X-Forwarded-Proto则告诉应用“客户端是通过 HTTPS 来的”,对某些回调地址生成逻辑很关键。
把配置放到/etc/nginx/sites-available/harness,然后建软链到sites-enabled:
sudo ln -s /etc/nginx/sites-available/harness /etc/nginx/sites-enabled/harness sudo nginx -t sudo systemctl reload nginx4.3 申请免费 TLS 证书并配置自动续期
证书我优先用 certbot。签发前确认域名解析已指向当前服务器 IP,然后执行:
sudo certbot certonly --nginx -d harness.example.com这条命令会读取 Nginx 配置,用 HTTP-01 方式完成域名的所有权验证。证书签发后,certbot 会把证书和私钥写到/etc/letsencrypt/live/harness.example.com/。Nginx 配置里引用的就是这个路径。
证书有效期一般是 90 天,所以自动续期一定要配好。先手动验证一次:
sudo certbot renew --dry-run然后添加一个 cron 任务,每天执行一次,防止意外漏续:
echo "0 3 * * * root certbot renew --quiet --deploy-hook 'systemctl reload nginx'" | sudo tee /etc/cron.d/certbot-renew这里我特别想强调一个细节:certbot 续期默认只续“接近过期”的证书,--deploy-hook里的 reload 是为了让 Nginx 加载新证书。如果你前一天刚签发,第二天跑 dry-run 成功也是正常的,不用慌。真正需要人工介入的是“续期失败且连续多天没修好”,这时候去检查 80 端口是否被占用、域名解析是否被改。
4.4 HTTP Basic Auth 与应用层 Token 双保险
TLS 解决了“传输加密”的问题,但任何知道 URL 的人都可以发起请求,所以必须加访问控制。我在这里采用双保险:Nginx 层做 HTTP Basic Auth,应用层再做自定义 Token 校验。
先创建 Basic Auth 用户:
sudo apt install -y apache2-utils sudo htpasswd -c /etc/nginx/.htpasswd admin这里会让你输入密码。-c是创建文件的参数。如果文件已经存在,第二次添加用户一定不能再加-c,否则会把原来的用户全清掉。这个坑我自己踩过,所以专门记在这里。
为什么浏览器场景用 Basic Auth 就够了?因为浏览器首访时会弹出原生的登录框,之后一段时间内会自动带上认证头,体验很顺。而它的弱点也很明显:如果只把它作为唯一防线,工具类客户端发起请求时需要传用户名密码,这个凭证一旦被泄露到日志里,就能畅通无阻。所以应用层再设一道 Token:
Harness 应用里通常会有一个根路径中间件,代码逻辑大致如下:
from fastapi import Request, HTTPException async def verify_shared_token(request: Request): token = request.headers.get("X-Api-Token") if token != settings.ADMIN_TOKEN: raise HTTPException(status_code=401, detail="invalid token")实际部署时并不需要你手写这个逻辑,Harness 一般提供了启用选项,比如REQUIRE_API_TOKEN=true。开启后,浏览器访问面板会先过 Nginx Basic Auth,再加载页面;而脚本调用则需要携带X-Api-Token自定义头。这两个机制叠加,相当于“人”和“程序”各拿一把钥匙。
关于 Basic Auth 有一个容易混淆的点:它必须和 HTTPS 搭配使用。如果站点走的是 HTTP 明文,那么你在浏览器里输入的用户名密码会以 Base64 编码形式直接暴露在网络中,这跟明文传密码没什么区别。这也是为什么上文里强制 301 到 HTTPS,任何省略这一步的部署都有严重风险。
4.5 安全组与主机防火墙的最终收紧
到这里应用已经能通过公网 HTTPS 访问了,但安全策略还没完。先把主机防火墙 UFW 规则收紧:
sudo ufw default deny incoming sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable然后在云控制台的安全组里,把入方向规则配置为:
22:仅你的管理出口 IP;80:通常需要放开到任意来源,因为证书签发商在验证域名所有权时可能从多个网络节点发起请求,然后靠 Nginx 的 301 重定向把访问导到 HTTPS;443:建议限制来源为企业出口 IP。如果确实需要随时随地访问,就开放到0.0.0.0/0,但必须确保 TLS、Basic Auth、应用层 Token 全部开启,并且日志里完整记录来源 IP。
我个人的选择是:如果团队成员都有固定的办公出口 IP,443 也会做来源限制;如果没有固定 IP,就必须依赖 TLS、Basic Auth、应用层 Token 这几层保护。注意,安全组里绝对不要放行 8900。
5. 进程守护与日常运维
5.1 systemd 服务:把前台进程变成常驻服务
很多人部署完后喜欢用nohup python run_server.py &让进程跑在后台。这在临时测试阶段没问题,但一旦服务器重启,进程不会自动恢复;进程意外退出也无人拉起;日志还会散落在终端会话里,时间一长非常被动。用 systemd 接管是标准答案。
创建/etc/systemd/system/harness.service:
[Unit] Description=DeepSeek Harness API Service After=network.target [Service] User=app Group=app WorkingDirectory=/opt/harness/app EnvironmentFile=/opt/harness/.env ExecStart=/opt/harness/venv/bin/python run_server.py Restart=always RestartSec=5 NoNewPrivileges=true ProtectSystem=full [Install] WantedBy=multi-user.target解释几个参数的意义:
EnvironmentFile:让 systemd 加载.env,这样应用和 systemd 看到的环境变量完全一致,避免在多个地方重复维护密钥;Restart=always:进程异常退出后 5 秒自动拉起。评测服务在偶发网络错误下可能退出,这个选项是刚需;ProtectSystem=full:对系统盘做只读保护,防止应用代码里误写系统文件。允许写的路径只有代码中明确指定的目录。
启用并启动:
sudo systemctl daemon-reload sudo systemctl enable harness sudo systemctl start harness sudo systemctl status harness之后看进程状态,就不只是裸的python run_server.py,而是被 systemd 托管的单元。查看日志也比较规范:
journalctl -u harness -f5.2 日志轮转与磁盘规划
Harness 如果把大量日志写进 journald,时间长了会占据不小的系统日志空间。我建议让应用把日志写到/opt/harness/logs/harness.log,然后用 logrotate 接管轮转。
创建/etc/logrotate.d/harness:
/opt/harness/logs/*.log { daily rotate 14 compress delaycompress missingok notifempty copytruncate }copytruncate是一个容易忽略的选项:它先把日志文件复制出来做轮转,再清空原文件。好处是正在写入的进程不需要重启日志句柄。如果改成create模式,可能需要重启 Harness 才能重新打开文件,这在生产环境里会引入额外故障。
另外,建议给/opt/harness所在的云盘留出至少 20% 的余量。日志和评测数据集一旦增长超过磁盘容量,服务可能直接挂掉,而且排查起来往往时间已经很晚。
5.3 数据备份与版本升级
评测工具里面最值钱的东西有三样:评测数据集、历史评测结果、环境配置。换句话说就是harness.db和.env这两个文件。备份方式可以很轻量:
#!/usr/bin/env bash BACKUP_DIR=/opt/harness/backups DATE=$(date +%Y%m%d%H%M%S) mkdir -p $BACKUP_DIR /opt/harness/venv/bin/python /opt/harness/app/manage.py dumpdata > $BACKUP_DIR/harness_$DATE.json 2>/dev/null cp /opt/harness/.env $BACKUP_DIR/env_$DATE.bak find $BACKUP_DIR -name "*.bak" -mtime +30 -delete如果用的是 SQLite,更直接的做法是服务停止写入后拷贝数据库文件。不过对于大多数轻量部署,dumpdata输出 JSON 更容易恢复。把这个脚本放进 crontab:
0 3 * * * app /opt/harness/scripts/backup.sh >> /opt/harness/logs/backup.log 2>&1升级版本时我的标准流程是:备份当前harness.db和.env-> 拉取新源码 -> 按 release note 执行迁移命令 -> 重启服务 -> 跑一次健康检查。整个操作尽量安排在维护窗口,动作越少越好。
6. 公网访问高频故障与排障实录
6.1 常见问题速查表
我把自己和周边朋友部署这套东西时踩过的问题整理成一张表,按“症状 -> 可能原因 -> 处理方式”的思路排查会快很多:
| 症状 | 可能原因 | 处理方式 |
|---|---|---|
| 浏览器访问显示 502 Bad Gateway | nginx 反代端口与应用监听端口不一致,或 harness 进程没起来 | 执行systemctl status harness;在本机curl http://127.0.0.1:8900/api/health验证 |
| 浏览器一直弹 Basic Auth 登录框,密码对了也进不去 | .htpasswd文件权限过宽或生成工具配置不对 | 检查nginx -T;确认用htpasswd -B生成;文件属主保证 nginx worker 进程可读 |
| 页面能开,但调用接口时 401 | 应用层 Token 校验失败;ADMIN_TOKEN与请求头不一致 | 检查 curl 请求是否带上X-Api-Token;确认.env里无多余空格和换行 |
| 日志里看到大量陌生 IP 的请求 | 安全组放开了 443 全网段,Basic Auth 强度不足 | 升级密码强度;限制安全组来源 IP;部署 fail2ban 对 Nginx 日志做暴力尝试封禁 |
| HTTPS 证书失效,浏览器提示不安全 | certbot 续期失败,通常是 80 端口被其他服务占用或 DNS 记录变了 | 手动执行certbot renew --force-renewal;确认 80 端口监听的是 nginx;检查域名解析 |
| Harness 服务重启后丢失运行状态 | systemd 没有 enable,或工作目录不对 | 执行systemctl enable harness;确认WorkingDirectory指向含run_server.py的目录 |
| 磁盘满了,服务无法写数据库 | 日志或备份目录太大 | 检查du -sh /opt/harness/*;触发 logrotate;清理过期备份 |
| 面板上看到的上游模型请求很慢 | REQUEST_TIMEOUT太短导致模型长时间生成时超时;也可能是模型服务端限流 | 调大REQUEST_TIMEOUT;检查模型服务商控制台配额 |
这个表不是万能的,但能覆盖大多数场景。我的排障顺序永远是:先看服务进程是否在 -> 再看本机 curl 是否通 -> 再看 nginx 日志 -> 最后查防火墙和安全组。倒着排查会把简单问题复杂化。
6.2 几个特别值得提醒的细节
第一个细节:.env文件被 systemd 读取时,不能有 DOS 换行符。如果文件里带着\r\n,ADMIN_TOKEN后面可能悄悄多了一个不可见字符,导致你请求时明明带了完全正确的 Token,也因为多一个换行而校验不过。这个坑的隐蔽之处在于初次部署时很难注意到,因为你在终端里看到的字符串完全一样。
第二个细节:如果你同时启用了 Basic Auth 和应用层 Token,不要试图在浏览器地址栏直接访问底层 API 调试接口。浏览器会先通过 Basic Auth 拦截,但不会自动带上X-Api-Token,所以你永远会得到 401。调试接口要用 Postman 或 curl 这种可以自定义请求头的工具。
第三个细节:云控制台安全组规则的生效逻辑。不同云厂商的规则实现有差异,有的是按优先级排列,有的是白名单模式。不管哪种,只要存在一条允许0.0.0.0/0访问 443 的规则,就不要指望“后面再写一条拒绝”能兜底。正确做法是:要么统一改成来源限定,要么干脆不要加自己都看不懂的规则。
再讲一个日志审计的小技巧。把 Harness 的访问日志和 nginx 访问日志一起保留,排查“谁在什么时候访问过面板”时会很有帮助。但要确认日志里记录的是真实客户端 IP,而不是代理 IP。如果 nginx 没配proxy_set_header X-Real-IP,你在应用日志里看到的 IP 全是127.0.0.1,那等于没有审计。配置好之后,日志才能成为可追溯的依据。
最后说一个容易被忽视的检查项:云厂商如果提供基础监控告警,建议把 CPU、内存、磁盘水位和公网带宽利用率的告警阈值都打开。我遇到过日志把磁盘写满、服务表面还在跑、但数据库实际上已经是只读状态的场景。如果没有磁盘告警,等发现的时候评测任务已经丢了一段数据。这类基础监控通常几分钟就能配好,但对长期运行来说非常关键。
这个部署流程走下来,其实没有多高深的技术,难点全在一个“稳”字:环境要稳、鉴权要稳、日志要稳、备份要稳。我个人的体会是,公网访问这件事,宁可多绕一层 nginx,也不要图省事直接暴露应用端口。把上面这套体系搭好后,日常维护基本不用再为安全问题操心,剩下的精力才能真正投入到评测任务本身中去。