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

3.9 KiB
Raw Permalink Blame History

管理區存取限制(/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 原始檔;改檔請直接在遠端編輯。

目前規則

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照下面流程在本機跑

# 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 容器

驗證

兩個方向都要測,缺一不可:

# 負向:從非白名單 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。

出錯時的回退

ssh hospital_web 'sudo cp /opt/php/html/admin/.htaccess.bak.YYYYMMDD /opt/php/html/admin/.htaccess'

如果 /admin/ 出 500幾乎可以斷定是 .htaccess 語法錯誤最常見regex 含 literal space。直接回退舊版即可。