Files
nginx-proxy-manager/NOTES.md

6.2 KiB
Raw Permalink Blame History

操作筆記

README 是現況快照(會過期);這份是做法(重複用得到)。

網域架構:兩套,別搞混

網域 DNS 對外 IP 意義
lotimmy.com Cloudflare proxied(橘雲) 104.21.26.142 / 172.67.136.127 流量經 Cloudflare 再回源
timmy.edu.kg Cloudflare DNS only(灰雲) 125.229.110.50(直指自家 IP 流量直達Cloudflare 只做解析

Zone IDtimmy.edu.kg = 76ae4a4445b95c6e2a36708fcc095058

新子網域一律照該 zone 現有記錄的 proxied 狀態複製,不要混用。沒有 wildcard 記錄,每個子網域都要單獨加。

新增一個對外服務

順序不能顛倒 —— 沒有 DNSLet's Encrypt 的 HTTP-01 challenge 一定失敗。

# 1. Cloudflare 加 A 記錄proxied 依 zone 慣例)
curl -X POST -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
  -d '{"type":"A","name":"<sub>","content":"125.229.110.50","ttl":1,"proxied":false}' \
  "https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records"

# 2. 等生效
dig +short <sub>.timmy.edu.kg @1.1.1.1

# 3. 取 NPM token
TOK=$(curl -s -X POST -H "Content-Type: application/json" \
  -d '{"identity":"<email>","secret":"<password>"}' \
  http://192.168.42.124:81/api/tokens | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")

# 4. 先簽憑證(回傳的 id 下一步要用)
curl -X POST -H "Authorization: Bearer $TOK" -H "Content-Type: application/json" \
  -d '{"provider":"letsencrypt","nice_name":"<fqdn>","domain_names":["<fqdn>"],"meta":{"dns_challenge":false}}' \
  http://192.168.42.124:81/api/nginx/certificates

# 5. 再建 proxy host
curl -X POST -H "Authorization: Bearer $TOK" -H "Content-Type: application/json" -d '{
  "domain_names":["<fqdn>"],"forward_scheme":"http","forward_host":"<ip>","forward_port":<port>,
  "certificate_id":<id>,"ssl_forced":true,"http2_support":true,"hsts_enabled":false,
  "allow_websocket_upgrade":true,"block_exploits":true,"caching_enabled":false,
  "access_list_id":0,"advanced_config":"","locations":[],"meta":{}}' \
  http://192.168.42.124:81/api/nginx/proxy-hosts

驗證:curl -o /dev/null -w "%{http_code}" 打 https 應得 200、http 應得 301。

NPM 2.15.1 API 的坑

  • meta 只吃這些欄位certificatecertificate_keydns_challengedns_providerdns_provider_credentialsletsencrypt_certificatepropagation_secondskey_type。 舊教學常見的 letsencrypt_email / letsencrypt_agree 會被拒data/meta must NOT have additional propertiesemail 改由全域設定帶。
  • schema 就在容器裡,不確定就直接查,不要猜:
    docker exec nginxproxymanager cat /app/schema/paths/nginx/certificates/post.json
    docker exec nginxproxymanager cat /app/schema/components/certificate-object.json
    
  • 目前 40 張憑證全部用 HTTP-01DB 裡沒有任何 DNS provider 憑證。
  • 查「有幾張用 DNS challenge」不要用 meta like '%dns_challenge%true%' —— 會誤中 "dns_challenge":false,"nginx_online":true。要寫 meta like '%"dns_challenge":true%'

刪除:直接操 DB 的正確步驟

NPM 不會監看 DB光改 DB 不會生效,conf 檔和 nginx reload 要自己補

cd /opt/nginx-proxy-manager/data
cp database.sqlite database.sqlite.bak-$(date +%F)          # 先備份,非選配

# 刪之前一定要查引用,確認不是使用中的服務
sqlite3 -separator '|' database.sqlite \
  "select id,domain_names,is_deleted,enabled from proxy_host where certificate_id=<id>;"

sqlite3 database.sqlite "update proxy_host set is_deleted=1 where id=<h>;
                         update certificate set is_deleted=1 where id=<c>;"
rm -f nginx/proxy_host/<h>.conf                              # 少這步 → 網站還活著

docker exec nginxproxymanager certbot delete --cert-name npm-<c> --non-interactive \
  --config-dir /etc/letsencrypt --work-dir /tmp/letsencrypt-lib --logs-dir /tmp/letsencrypt-log

docker exec nginxproxymanager nginx -t && docker exec nginxproxymanager nginx -s reload

慣例:憑證 id 為 Nletsencrypt/{live,archive}/npm-Nrenewal/npm-N.conf

驗證要帶 Host header 打 port 80,不要打 443 —— 443 沒給 SNI 會 curl: (35),看起來像壞掉其實是測法錯:

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1 -H "Host: <fqdn>"   # 已刪 → 404

每次都要有對照組(拿 web.lotimmy.com 之類還活著的比),否則分不出「刪對了」和「整台 nginx 掛了」。

找孤兒 conf

for f in nginx/proxy_host/*.conf; do id=$(basename $f .conf)
  [ "$(sqlite3 database.sqlite "select count(*) from proxy_host where id=$id and is_deleted=0;")" = 1 ] \
    || echo "孤兒: $f"; done

Cloudflare API

cfat_ 開頭的 token /user/tokens/verify 會回 Invalid API Token,但 token 是好的。別因為這個結論說 token 失效 —— 直接打實際端點測:

curl -H "Authorization: Bearer $CF_TOKEN" \
  "https://api.cloudflare.com/client/v4/zones?name=timmy.edu.kg"

Git 推送到自架 Gitea

git push http://user:token@host/... 會把含 token 的 URL 寫進 .git/config 的 upstream。推完要清

git config --unset branch.main.remote
git config --unset branch.main.merge
git fetch origin && git branch --set-upstream-to=origin/main main
grep -rn "<token前綴>" .git/ && echo "⚠️ 有殘留"

origin 本身存不含帳密的 URL。

反向代理後別忘了改應用端 base URL

代理設好只是流量進得來應用自己產生的連結clone URL、通知信、OAuth callback還是用它自己的設定。Gitea 是 app.ini[server] ROOT_URL,改完要重啟。這是目前未完成項

雜項

  • client_max_body_size 全域已是 2000m/etc/nginx/nginx.conf:39git push 大 repo 不會被擋,不用另外寫 advanced config。
  • certbot delete 只清該 lineage。DB 早就軟刪除、檔案還留著的舊 lineagenpm-4)不會自己消失。
  • dead_host*.lotimmy.com 會接住所有未定義子網域,所以刪掉 proxy host 後是 404 而不是連到別的站。