# nginx 真实 IP 还原 — 生产部署(小白友好版) > 术语速查:**nginx** = 你这台服务器的"门卫",负责把用户请求分发给后端 / 把静态文件返回给浏览器 > **配置** = nginx 的工作规则,改配置 = 改门卫的工作方式 --- ## 我们要做啥(整体目标) **一句话目标**:`https://itsupport.servyou.com.cn/itadmin/` 之前返回 403(被门卫拦了),原因是门卫把"代理服务器 IP"当成了"用户 IP",而代理 IP 不在白名单里。这次我们改门卫的规则,让它从请求头里读"真实用户 IP"。 **一共 7 个动作**: | # | 动作 | 大概多久 | 风险 | |---|---|---|---| | 1 | PuTTY 连上服务器 | 1 分钟 | ⚪ 无风险 | | 2 | 备份当前配置 | 几秒 | 🟢 备份原文件,可还原 | | 3 | 写入 13 行新规则 | 几秒 | 🟡 改配置,但有备份 | | 4 | 确认写入正确 | 几秒 | ⚪ 只读不写 | | 5 | 检查配置语法 | 几秒 | ⚪ 只读不写 | | 6 | 让 nginx 重新读规则 | 1 秒 | 🟡 短暂重载,服务不中断 | | 7 | 浏览器看效果 | 几秒 | ⚪ 只读 | **总耗时**:第一次大概 5-10 分钟;熟练了 2 分钟 **整体风险**:🟢 **低** — 每一步都给了"回滚"按钮,改坏了随时能恢复 --- ## PuTTY 是啥?在哪儿打开? **PuTTY** = 一个 SSH 客户端软件,作用是让你从你的 Windows 电脑远程连到公司的 Linux 服务器 **打开方式**: - 按 `Win 键` → 输入 `putty` → 回车 - 或者开始菜单 → 找到 PuTTY 图标 打开后会看到一个灰底配置界面,我们要填 4 项: ``` ┌──────────────────────────────────────┐ │ Host Name (or IP address) │ ← 填: 10.212.189.210 │ Port │ ← 填: 2222 │ Connection type │ ← 选: SSH(默认就是) │ Saved Sessions │ ← 填: wecom-bastion(起个名) └──────────────────────────────────────┘ 点 Save 保存 → 点 Open 开始连接 ``` 连接后会黑底白字,提示 `login as:` → 输入 `sxn` 回车 → 提示 `password:` → 输入你的堡垒机密码(输入时屏幕不显示,正常,输完回车就行) **注意**:输错密码不会锁账号,直接重新输 --- ## 动作 1:PuTTY 连服务器(⚪ 无风险) > **为啥要连服务器?**:改配置必须在服务器上操作,你 Windows 这边只是"遥控器" 连上堡垒机后,黑底白字会显示一个类似 `sxn@jump-host:~$` 的提示符,说明你已经到堡垒机了。 **决策树**: ``` 你现在看到了堡垒机提示符(类似 sxn@jump-host:~$) ├─ 是 → 在 PuTTY 里继续输入下面命令 └─ 否 → 截图发给我,卡哪儿了 ``` 贴下面的命令(右键 = 粘贴,Enter = 执行): ```bash # 从堡垒机跳到真正的生产服务器 ssh sxn@10.90.5.110 ``` 回车后可能要输密码(堡垒机和目标机密码可能不同,试一下你之前用过的那个) **✅ 成功长这样**: ```text sxn@prod-server:~$ ``` **❌ 失败常见**: - `Permission denied` → 密码错了,重输 - `Connection timed out` → 网络问题,可能 VPN 没连 - 卡住不动 → 可能需要输 `yes` 确认服务器指纹,看到 `(yes/no/[fingerprint])?` 就输 `yes` 回车 --- ## 动作 2:备份当前配置(🟢 低风险,改坏了能还原) > **为啥要备份?**:运维铁律 — **改任何东西之前先备份**,这样改坏了能用备份还原,不会把生产搞挂 ```bash # 进入 nginx 配置所在目录 cd /opt/wecom-it-desk/nginx # 复制一份当前配置,文件名带当前时间(分),方便区分 sudo cp nginx.conf nginx.conf.bak-$(date +%H%M) # 列出所有备份文件,确认刚才那行成功 ls -la nginx.conf.bak-* ``` **为啥用 `$(date +%H%M)`?**:这个写法会自动拼上当前时间(比如 1430 表示 14:30),每次备份文件名都不一样,不会覆盖之前的备份 **✅ 成功长这样**: ```text -rw-r--r-- 1 root root 4821 Jun 15 14:30 nginx.conf.bak-1430 ``` **❌ 失败常见**: - `cp: cannot stat 'nginx.conf'` → 当前不在 nginx 目录,先 `cd /opt/wecom-it-desk/nginx` 进去 - `Permission denied` → 缺 `sudo`,命令前面加 `sudo` 重试 --- ## 动作 3:写入 13 行新规则(🟡 中风险,但有备份兜底) > **写入啥?**:13 行 nginx 配置,告诉 nginx"从请求头 X-Forwarded-For 里读真实用户 IP" > > **为啥要这样做?**:用户通过公司 WAF/堡垒机访问,WAF 会把真实 IP 放在 `X-Forwarded-For` 请求头里,但 nginx 默认只看直连 IP,所以才误判 403 **重要**:把下面**从 `cat > /tmp/patch.py` 到 `PYEOF`** 的**整段**一次性粘贴进 PuTTY(右键 = 粘贴)。整段会作为一条命令执行。 ```bash # 创建一个 python 脚本到 /tmp/patch.py cat > /tmp/patch.py << 'PYEOF' fp = '/opt/wecom-it-desk/nginx/nginx.conf' with open(fp) as f: c = f.read() patch = ''' # ------------------------------------------------------------------ # 真实 IP 还原(2026-06-15 v0.5.1 修复) # ------------------------------------------------------------------ set_real_ip_from 10.0.0.0/8; set_real_ip_from 172.16.0.0/12; set_real_ip_from 192.168.0.0/16; set_real_ip_from 10.212.0.0/16; real_ip_header X-Forwarded-For; real_ip_recursive on; ''' old = 'error_log /var/log/nginx/error.log warn;' new = old + patch new_c = c.replace(old, new, 1) with open(fp, 'w') as f: f.write(new_c) print('patched, +{} bytes'.format(len(new_c) - len(c))) PYEOF # 运行这个 python 脚本,它会自动把上面那 13 行插入到 nginx.conf sudo python3 /tmp/patch.py ``` **术语解释**: - `cat > /tmp/patch.py` → 创建一个文件,内容是后面所有内容 - `<< 'PYEOF' ... PYEOF` → 这种写法叫 **heredoc**(直译"这里是文档"),作用是把多行文字原样写入文件 - `sudo` → 以管理员身份运行(改系统文件需要权限) **✅ 成功长这样**: ```text patched, +492 bytes ``` **❌ 失败常见**: - `Permission denied` → 缺 `sudo`,或者 nginx.conf 不存在 - `NameError: name 'fp' is not defined` → heredoc 没贴完整,最末尾的 `PYEOF` 没贴上 - 没任何输出 → python 没运行,看光标有没有新行,可能没回车 --- ## 动作 4:确认写入正确(⚪ 无风险,只读) > **为啥要确认?**:虽然脚本说写入了,但**人眼看到才真的算**。这步只读不写,放心跑 ```bash # 在 nginx.conf 里搜索"真实 IP 还原"关键字,并显示后面 13 行 sudo grep -A 13 "真实 IP 还原" /opt/wecom-it-desk/nginx/nginx.conf ``` **✅ 成功长这样**(应该看到完整 13 行): ```nginx # 真实 IP 还原(2026-06-15 v0.5.1 修复) # ------------------------------------------------------------------ set_real_ip_from 10.0.0.0/8; set_real_ip_from 172.16.0.0/12; set_real_ip_from 192.168.0.0/16; set_real_ip_from 10.212.0.0/16; real_ip_header X-Forwarded-For; real_ip_recursive on; ``` **❌ 失败**: - 啥也没输出 → 写入失败,回到动作 3 重做 - 只输出一两行 → heredoc 没贴全,需要回滚后重来 --- ## 动作 5:检查配置语法(⚪ 无风险,只读不执行) > **为啥要检查?**:这个命令 nginx 会"假装"按新配置启动,只检查语法,不会真的重启。**通过 = 配置写得对,放心用;不通过 = 写得有问题,继续走会出问题** ```bash # 在 nginx 容器(就是跑 nginx 服务的那个小 Linux)内,做配置语法检查 docker compose exec nginx nginx -t ``` **术语解释**: - `docker compose` → 管理这台服务器上所有"容器"的命令 - `exec` → "钻进"某个容器里执行命令 - `nginx -t` → nginx 自带的"语法检查"工具(全称 `--test`) **✅ 成功长这样**: ```text nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` **❌ 失败**(`test is successful` 没出现): - `unexpected "}"` / `unknown directive` → 写错字了,回去动作 4 看看哪里对不上 - **直接停下,不要继续** → 复制错误信息贴回给我 --- ## 动作 6:让 nginx 重新读规则(🟡 中风险,但服务不中断) > **"重新读"是啥意思?**:nginx 现在用的还是旧配置,我们让 nginx 不用重启(不会断服务)就把新配置加载进来。这个动作叫"热加载"或 "reload" > > **会断网吗?**:不会,reload 是无缝的,用户那边无感知 ```bash # 通知 nginx 容器内的 master 进程重新读配置 docker compose exec nginx nginx -s reload ``` **术语解释**:`-s reload` = 发信号(英文 signal)给 nginx,告诉它"重读配置" **✅ 成功长这样**(没报错即成功): ```text 2026/06/15 14:35:12 [notice] 1#1: signal process started ``` **❌ 失败**: - `nginx: [error]` 开头 → 配置没通过,回去动作 5 看哪里没对 - 啥也没输出 → 命令没执行,看光标位置 --- ## 动作 7:浏览器看效果(⚪ 无风险) **为啥这步是浏览器而不是 curl?**:curl 看响应头,浏览器看真实页面。**人眼看到才作数** **操作步骤**: 1. 打开浏览器 2. **开隐身模式**(`Ctrl + Shift + N`,Chrome / Edge 都是这个快捷键) - **为啥要隐身?**:隐身模式不读本地缓存,看到的就是 nginx **当下**返回的 3. 地址栏输入 `https://itsupport.servyou.com.cn/itadmin/` 4. 按回车 **✅ 成功长这样**: - 页面正常显示 - 按 `F12` 打开开发者工具 → `Network` 选项卡 → 顶部那一行状态码是 **200**(不是 403) **❌ 失败**: - 仍然是 403 → 见下面"如果还是 403"段 - 502 / 504 → nginx 后面那个服务挂了,贴错误给我 - 页面打不开(连接被拒) → DNS 没配,联系 IT 运维 --- ## 如果还是 403 — 看 WAF 出口 IP(诊断) > **啥是 WAF?**:公司部署在 nginx 前面的"统一入口",所有用户请求先经过 WAF 再到 nginx。WAF 自己的 IP 不一定在你写的 4 段内网里,所以还得加 ```bash # 看 nginx 最后 20 条访问日志,找 $remote_addr 是不是 WAF 的 IP docker compose exec nginx tail -20 /var/log/nginx/access.log ``` **日志长这样**: ```text 10.80.5.123 - - [15/Jun/2026:14:35:45 +0800] "GET /itadmin/ HTTP/1.1" 403 ... ^^^^^^^ 这就是 $remote_addr ``` 把那个 IP 数字(比如 `10.80.5.123`)贴回给我,我会: 1. 给你追加一行 `set_real_ip_from 10.80.5.123;` 2. 让你重跑动作 5 + 动作 6 --- ## 如果改坏了 — 回滚(啥时候都能用) > **啥时候用?**:任何一个动作出问题,你都可以直接回滚到动作 2 备份的版本 ```bash # 列出所有备份,挑最近的一个 ls -la /opt/wecom-it-desk/nginx/nginx.conf.bak-* ``` ```bash # 用最近那个备份覆盖当前配置(把 1430 换成上面列出的真实时间) sudo cp /opt/wecom-it-desk/nginx/nginx.conf.bak-1430 /opt/wecom-it-desk/nginx/nginx.conf ``` ```bash # 重新加载回滚后的配置 docker compose exec nginx nginx -s reload ``` 回滚后页面应该回到改之前的状态(403 回来),说明回滚成功 --- ## 一张图看懂流程 ``` PuTTY 连服务器 │ ▼ 备份原配置 │ ▼ 写入 13 行新规则 │ ▼ 确认写入正确 ──→ ❌ 不对 ──→ 重做写入 / 回滚 │ ✅ ▼ 检查配置语法 ──→ ❌ 语法错 ──→ 复制错误贴回给我,不要继续 │ ✅ ▼ 重载 nginx ─────→ ❌ 报错 ──→ 检查容器状态 / 找 Claude │ ✅ ▼ 浏览器看效果 ──→ ❌ 还是 403 ──→ 看 WAF 出口 IP,贴给 Claude │ ✅ ▼ 🎉 完成 ``` --- ## 我建议你第一次做 **第一次建议**:动作 1 → 动作 2 → **停一下,截图发给我** → 我确认备份成功 → 你再继续动作 3 之后 **熟练了以后**:一口气跑完动作 1-7,中间不打断 --- ## 关联 - 评审报告:`review-p0-security-2026-06-14.md` P0-3 - 待办:`ip-whitelist-trust-proxies-todo.md` — v1.0 前必须收窄 4 段 → 4 个 IP - 本地配置:`deploy-server/nginx/nginx.conf`(已包含 patch,下次重打包自动带) - 服务器 IP 变更:`project-production-server-ip-2026-06-15.md` — 10.80.0.136 已下线,用 10.90.5.110 - 客户端约束:`feedback-putty-not-openssh.md` — 用 PuTTY,不用 `ssh -J` - 命令行规范:`feedback-cmd-step-by-step.md` — 每行一条 + 中文注释 - 小白引导规范:`feedback-beginner-friendly-guide.md` — 讲清目标+风险、术语解释、出错兜底