Files
106_105_181_70/admin_access_note.md
Timmy f2fa685140 docs: 新增 admin_access_note — /admin/ IP 白名單機制紀錄
紀錄 .htaccess 改用 X-Forwarded-For + 末尾錨定的設計,以及兩個
不從程式碼能直接看出的雷:NPM 反向代理讓後端 REMOTE_ADDR 失效,
Apache RewriteCond 的 regex 不可含 literal space 否則 vhost 500。
順手把新檔加進 README 文件清單。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:47:46 +08:00

94 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 管理區存取限制(/admin/ IP 白名單)
`https://tpesupporter.tpech.gov.tw/admin/` 透過 Apache `.htaccess` 做 IP 白名單,不在白名單的 client 一律 302 導去 `/404`
## 檔案位置
| 路徑 | 說明 |
|---|---|
| `hospital_web:/opt/php/html/admin/.htaccess` | 實際生效檔案(容器內為 `/var/www/html/admin/.htaccess` |
| `hospital_web:/opt/php/html/admin/.htaccess.bak.YYYYMMDD` | 修改前備份命名慣例 |
> `php/` 子目錄是另一個獨立 git repo本 repo 不持有 .htaccess 原始檔;改檔請直接在遠端編輯。
## 目前規則
```apache
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-For} !(^|,\s?)61\.220\.124\.10[56]$
RewriteCond %{HTTP:X-Forwarded-For} !(^|,\s?)220\.135\.27\.129$
RewriteCond %{HTTP:X-Forwarded-For} !(^|,\s?)223\.137\.237\.10$
RewriteCond %{HTTP:X-Forwarded-For} !(^|,\s?)10\.209\.(159|160|161)\.[0-9]+$
RewriteRule .* https://tpesupporter.tpech.gov.tw/404 [R,L]
```
| IP / 網段 | 用途 |
|---|---|
| 61.220.124.105 / 106 | 辦公室出口 |
| 220.135.27.129 | jason_hsieh_home_srv 跳板機 |
| 223.137.237.10 | 備援辦公點 |
| 10.209.159161.x | 市府內部網段 |
## 兩個必須注意的雷
### 1. 不能用 `REMOTE_ADDR`,必須用 `X-Forwarded-For`
站台前面是 Nginx Proxy Manageropenresty反向代理後端 Apache 看到的 `REMOTE_ADDR` 永遠是 NPM 容器的 docker 內網 IP每個 client 都一樣)。直接用 `REMOTE_ADDR` 比對 → 不是把所有人都放行就是把所有人都擋掉。
NPM 預設行為:把真實 client IP **append 到 `X-Forwarded-For` 尾端**,所以末尾錨定 `$` 可以同時:
- 比對到真正的 client IP
- 防止 client 自己送假 header 偽造前綴繞過(任何前綴都會被 NPM 推到非末尾位置)
替代方案:在 Apache 啟用 `mod_remoteip` 並信任 NPM 來源 → `REMOTE_ADDR` 會被替換為真實 client IP這樣原本的 `REMOTE_ADDR` 規則才能用。目前**沒有**啟用,故採取 `X-Forwarded-For` 方案。
### 2. `RewriteCond` regex 不能放 literal space
Apache 用空白切 directive 欄位regex 裡寫 `(^|, ?)` 會被 parser 截斷 → vhost 整個壞掉,`/admin/` 會回 500。必須用 `\s?``[ ]?``\ `escape。本檔採 `\s?`
## 更新 SOP
新增 / 移除 IP照下面流程在本機跑
```bash
# 1. 備份(檔名用當天日期)
ssh hospital_web 'sudo cp -p /opt/php/html/admin/.htaccess /opt/php/html/admin/.htaccess.bak.YYYYMMDD'
# 2. 寫入新內容(編輯下面 here-doc 內的規則)
cat <<'HTACCESS' | ssh hospital_web 'sudo tee /opt/php/html/admin/.htaccess > /dev/null'
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-For} !(^|,\s?)61\.220\.124\.10[56]$
RewriteCond %{HTTP:X-Forwarded-For} !(^|,\s?)220\.135\.27\.129$
# ... 其餘規則 ...
RewriteRule .* https://tpesupporter.tpech.gov.tw/404 [R,L]
HTACCESS
# 3. 修正擁有者(檔案掛載自 host需保持原 uid
ssh hospital_web 'sudo chown tpech_1duan:tpech_1duan /opt/php/html/admin/.htaccess && sudo chmod 664 /opt/php/html/admin/.htaccess'
```
> Apache 每次請求都會重讀 `.htaccess`**不需要 restart 容器**。
## 驗證
兩個方向都要測,缺一不可:
```bash
# 負向:從非白名單 IP例如本機→ 預期 302
curl -sI https://tpesupporter.tpech.gov.tw/admin/ | head -5
# 正向:從白名單 IP例如 jason_hsieh_home_srv = 220.135.27.129)→ 預期 200
ssh jason_hsieh_home_srv 'curl -sI https://tpesupporter.tpech.gov.tw/admin/ | head -5'
```
> 從外部用 `-H "X-Forwarded-For: <白名單IP>"` **無法**模擬白名單 client因為 NPM 會把你的真實 IP append 在尾端覆蓋掉錨點 — 這是設計上正確的安全行為,不是 bug。
## 出錯時的回退
```bash
ssh hospital_web 'sudo cp /opt/php/html/admin/.htaccess.bak.YYYYMMDD /opt/php/html/admin/.htaccess'
```
如果 `/admin/` 出 500幾乎可以斷定是 `.htaccess` 語法錯誤最常見regex 含 literal space。直接回退舊版即可。