Files
nginx-proxy-manager/NOTES.md

133 lines
6.2 KiB
Markdown
Raw Permalink 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.
# 操作筆記
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 ID`timmy.edu.kg` = `76ae4a4445b95c6e2a36708fcc095058`
新子網域一律照該 zone 現有記錄的 proxied 狀態複製,不要混用。**沒有 wildcard 記錄**,每個子網域都要單獨加。
## 新增一個對外服務
順序不能顛倒 —— 沒有 DNSLet's Encrypt 的 HTTP-01 challenge 一定失敗。
```bash
# 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` 只吃這些欄位**`certificate``certificate_key``dns_challenge``dns_provider``dns_provider_credentials``letsencrypt_certificate``propagation_seconds``key_type`
舊教學常見的 `letsencrypt_email` / `letsencrypt_agree` **會被拒**`data/meta must NOT have additional properties`email 改由全域設定帶。
- schema 就在容器裡,不確定就直接查,不要猜:
```bash
docker exec nginxproxymanager cat /app/schema/paths/nginx/certificates/post.json
docker exec nginxproxymanager cat /app/schema/components/certificate-object.json
```
- 目前 40 張憑證**全部用 HTTP-01**DB 裡沒有任何 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 要自己補**。
```bash
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 為 `N` ↔ `letsencrypt/{live,archive}/npm-N` 與 `renewal/npm-N.conf`。
驗證要帶 Host header 打 **port 80**,不要打 443 —— 443 沒給 SNI 會 `curl: (35)`,看起來像壞掉其實是測法錯:
```bash
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1 -H "Host: <fqdn>" # 已刪 → 404
```
每次都要有**對照組**(拿 `web.lotimmy.com` 之類還活著的比),否則分不出「刪對了」和「整台 nginx 掛了」。
找孤兒 conf
```bash
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 失效 —— 直接打實際端點測:
```bash
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。推完要清
```bash
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:39`git push 大 repo 不會被擋,不用另外寫 advanced config。
- `certbot delete` 只清該 lineage。DB 早就軟刪除、檔案還留著的舊 lineage如 `npm-4`)不會自己消失。
- `dead_host` 的 `*.lotimmy.com` 會接住所有未定義子網域,所以刪掉 proxy host 後是 404 而不是連到別的站。