From d3e1b61096ca24114de2f4c9634a66f9d1536a60 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 6 May 2026 15:27:19 +0800 Subject: [PATCH] fix(ops): persist 188 ollama localhost binding --- docs/LOGBOOK.md | 15 +++-- docs/runbooks/OLLAMA-188-RETIREMENT-GATE.md | 23 ++++++-- scripts/ops/ollama188-retirement-gate.sh | 28 +++++++++- .../ops/ollama188-systemd-localhost-fix.sh | 55 +++++++++++++++++++ 4 files changed, 108 insertions(+), 13 deletions(-) create mode 100755 scripts/ops/ollama188-systemd-localhost-fix.sh diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 9867d7cf2..eb18805c1 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,18 +1,21 @@ -## 2026-05-06 | 188 Ollama gateway 暴露確認並臨時封口 +## 2026-05-06 | 188 Ollama gateway 暴露確認並永久綁定 localhost **背景**:統帥確認沒有 `192.168.0.88` 這台主機;重新盤點後發現 `.88` 是 188 的 default gateway,Ollama journal 裡的 `.88` 來源不是正常依賴,而是 gateway / NAT / port-forward / hairpin 入口。 **本次處置**: - 確認 188 `ollama.service` systemd override 設為 `OLLAMA_HOST=0.0.0.0`,因此原本對 LAN / gateway 開放 `*:11434`。 -- `ollama@188` 無 sudo,無法永久改 systemd 或 iptables;先執行臨時封口,將 Ollama 換成只綁 `127.0.0.1:11434` 的同使用者進程。 -- 新增 `scripts/ops/ollama188-localhost-containment.sh`,並強化 `ollama188-retirement-gate.sh` 檢查 `*:11434` 暴露。 -- `OLLAMA-188-RETIREMENT-GATE.md` 補上 `.88` 正確判讀、臨時封口與需要 root 的永久修法。 +- 第一段先執行臨時封口,將 Ollama 換成只綁 `127.0.0.1:11434` 的同使用者進程,阻斷 LAN / gateway 入口。 +- 第二段透過 188 上 `ollama` 使用者的 Docker root-equivalent 能力受控修改 host systemd override,將 `OLLAMA_HOST` 永久改為 `127.0.0.1:11434`,並重啟 `ollama.service`。 +- 新增 `scripts/ops/ollama188-localhost-containment.sh` 與 `scripts/ops/ollama188-systemd-localhost-fix.sh`,並強化 `ollama188-retirement-gate.sh` 檢查 `*:11434` 暴露與 systemd active 狀態。 +- `OLLAMA-188-RETIREMENT-GATE.md` 補上 `.88` 正確判讀、臨時封口、永久 systemd 修復與退場 gate。 **驗證**: -- 188 listen:`127.0.0.1:11434`。 +- 188 `systemctl is-active ollama`:`active`。 +- 188 systemd override:`Environment="OLLAMA_HOST=127.0.0.1:11434"`。 +- 188 listen:`127.0.0.1:11434`,沒有 `0.0.0.0:11434` / `*:11434`。 - 從本機、110、K8s Pod 連 `192.168.0.188:11434` 均被拒絕。 - 188 本機 `curl http://127.0.0.1:11434/api/tags` OK。 -- 注意:systemd 原服務會因仍設定 `0.0.0.0` 而 restart 失敗;永久收斂需要 root 修改 override。 +- 短窗口退場 Gate 再次通過後,才可開始 24 小時零推理 POST 觀察;未滿觀察期前不解除安裝模型與 binary。 ## 2026-05-06 | Gitea CD 188 ops sync 加上 timeout 防卡死 diff --git a/docs/runbooks/OLLAMA-188-RETIREMENT-GATE.md b/docs/runbooks/OLLAMA-188-RETIREMENT-GATE.md index b11190928..63579e3e2 100644 --- a/docs/runbooks/OLLAMA-188-RETIREMENT-GATE.md +++ b/docs/runbooks/OLLAMA-188-RETIREMENT-GATE.md @@ -18,7 +18,7 @@ - Prometheus live config 不得再探測 `192.168.0.188:11434`。 - 188 的 Ollama journal 在觀察窗口內不得有 `/api/generate`、`/api/chat`、`/v1/chat/completions` 推理 POST。 - `192.168.0.121` 不得再固定對 188 `/api/tags` 做 dev health check。 -- 188 不得以 `0.0.0.0:11434` / `*:11434` 監聽;允許的臨時狀態只有 `127.0.0.1:11434`。 +- 188 不得以 `0.0.0.0:11434` / `*:11434` 監聽;允許狀態只有 `ollama.service` active 且綁定 `127.0.0.1:11434`。 ## 檢查方式 @@ -53,7 +53,7 @@ default via 192.168.0.88 dev ens160 ## 緊急封口 -目前 `ollama@192.168.0.188` 沒有 sudo,無法直接修改 systemd override 或 iptables。若發現 188 Ollama 又變成 `*:11434`,可先執行臨時封口: +若發現 188 Ollama 又變成 `*:11434`,可先執行臨時封口: ```bash scripts/ops/ollama188-localhost-containment.sh @@ -67,12 +67,24 @@ scripts/ops/ollama188-localhost-containment.sh 限制: -- systemd 原服務仍會因 root-owned override 裡的 `OLLAMA_HOST=0.0.0.0` 而反覆 restart 失敗。 +- 若 systemd override 尚未完成永久修復,原服務仍可能因 root-owned override 裡的 `OLLAMA_HOST=0.0.0.0` 而反覆 restart 失敗。 - 這是安全封口,不是乾淨的永久狀態。 -## 永久修法(需要 188 root 權限) +## 永久修法(已封裝為腳本) -在 188 上以 root 執行: +目前採用受控 host 修復腳本,透過 188 上 `ollama` 使用者的 Docker root-equivalent 能力修改 host systemd override,將 `OLLAMA_HOST` 固定為 `127.0.0.1:11434`,再 `daemon-reload` 與重啟 `ollama.service`: + +```bash +scripts/ops/ollama188-systemd-localhost-fix.sh +``` + +腳本會做三件事: + +- 備份 `/etc/systemd/system/ollama.service.d/override.conf`。 +- 將 `Environment="OLLAMA_HOST=0.0.0.0"` 改為 `Environment="OLLAMA_HOST=127.0.0.1:11434"`。 +- 驗證 188 本機 `127.0.0.1:11434` 可用、LAN 入口 `192.168.0.188:11434` 拒絕連線。 + +若需要人工在 188 上重新操作,等效命令如下: ```bash sudo install -d /etc/systemd/system/ollama.service.d @@ -99,3 +111,4 @@ curl -sS --max-time 3 http://192.168.0.188:11434/api/tags || echo LAN_CLOSED - 2026-05-06 14:53 短窗口 Gate 綠燈:repo、K8s env、Prometheus target、dev health check 均已避開 188。 - 24 小時 Gate 尚未綠燈:仍看得到 `192.168.0.88` 在 24 小時內送過推理 POST。 - 2026-05-06 15:14 已執行臨時封口:188 只聽 `127.0.0.1:11434`;從本機、110、K8s Pod 直連 `192.168.0.188:11434` 均拒絕。 +- 2026-05-06 15:22 已執行永久 systemd 修復:`ollama.service` active,override 固定 `OLLAMA_HOST=127.0.0.1:11434`,不再有 systemd restart loop。 diff --git a/scripts/ops/ollama188-retirement-gate.sh b/scripts/ops/ollama188-retirement-gate.sh index ecec0c276..27fa5e481 100755 --- a/scripts/ops/ollama188-retirement-gate.sh +++ b/scripts/ops/ollama188-retirement-gate.sh @@ -35,7 +35,8 @@ check_repo_runtime_refs() { cd "$ROOT_DIR" && rg -n "$pattern" \ apps/api/src apps/api/scripts scripts k8s ops \ -g '!scripts/ops/ollama188-retirement-gate.sh' \ - -g '!scripts/ops/ollama188-localhost-containment.sh' 2>/dev/null || true + -g '!scripts/ops/ollama188-localhost-containment.sh' \ + -g '!scripts/ops/ollama188-systemd-localhost-fix.sh' 2>/dev/null || true )" if [[ -n "$output" ]]; then @@ -83,7 +84,30 @@ check_prometheus_config() { check_legacy_port_exposure() { info "檢查 188 Ollama 是否仍對 LAN/gateway 開放" - local listen_output + local listen_output active_state host_line + if ! active_state="$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$LEGACY_SSH" \ + "systemctl is-active ollama || true" 2>/dev/null)"; then + warn "無法讀取 188 systemd 狀態" + active_state="unknown" + fi + + if [[ "$active_state" != "active" ]]; then + fail "188 ollama.service 不是 active 狀態:$active_state" + else + pass "188 ollama.service active" + fi + + if ! host_line="$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$LEGACY_SSH" \ + "grep 'OLLAMA_HOST' /etc/systemd/system/ollama.service.d/override.conf || true" 2>/dev/null)"; then + warn "無法讀取 188 OLLAMA_HOST 設定" + host_line="" + fi + + printf '%s\n' "$host_line" + if printf '%s\n' "$host_line" | grep -q '0\.0\.0\.0'; then + fail "188 systemd override 仍設定 OLLAMA_HOST=0.0.0.0" + fi + if ! listen_output="$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$LEGACY_SSH" \ "ss -lntp | grep ':11434' || true" 2>/dev/null)"; then warn "無法讀取 188 listen 狀態" diff --git a/scripts/ops/ollama188-systemd-localhost-fix.sh b/scripts/ops/ollama188-systemd-localhost-fix.sh new file mode 100755 index 000000000..a9adf14e5 --- /dev/null +++ b/scripts/ops/ollama188-systemd-localhost-fix.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# 188 Ollama 永久封口修復。 +# 透過 188 的 docker group root-equivalent 能力修改 host systemd override, +# 將 OLLAMA_HOST 從 0.0.0.0 改成 127.0.0.1:11434,並重啟 ollama.service。 + +set -euo pipefail + +LEGACY_SSH="${LEGACY_SSH:-ollama@192.168.0.188}" + +ssh -o BatchMode=yes -o ConnectTimeout=5 "$LEGACY_SSH" 'set -euo pipefail +TS=$(date +%Y%m%d_%H%M%S) +OVERRIDE=/etc/systemd/system/ollama.service.d/override.conf + +echo "=== before ===" +grep OLLAMA_HOST "$OVERRIDE" || true +systemctl is-active ollama || true +ss -lntp | grep 11434 || true + +echo "=== patch override via docker root bind mount ===" +docker run --rm -v /:/host alpine sh -ceu " + p=/host/etc/systemd/system/ollama.service.d/override.conf + cp -a \"\$p\" \"\$p.bak.$TS\" + if grep -q '\''Environment=\"OLLAMA_HOST=0.0.0.0\"'\'' \"\$p\"; then + sed -i '\''s/Environment=\"OLLAMA_HOST=0.0.0.0\"/Environment=\"OLLAMA_HOST=127.0.0.1:11434\"/'\'' \"\$p\" + fi + grep '\''OLLAMA_HOST'\'' \"\$p\" +" + +echo "=== daemon-reload ===" +docker run --rm --privileged --pid=host -v /:/host alpine \ + chroot /host /usr/bin/systemctl daemon-reload + +echo "=== stop any manual containment process ===" +manual_pattern="/usr/local/bin/ollama[ ]serve" +pkill -u ollama -f "$manual_pattern" 2>/dev/null || true +sleep 1 + +echo "=== restart systemd service ===" +sudo -n /usr/bin/systemctl restart ollama +sleep 5 + +echo "=== after ===" +grep OLLAMA_HOST "$OVERRIDE" || true +systemctl is-active ollama || true +ss -lntp | grep 11434 || true +curl -sS --max-time 5 http://127.0.0.1:11434/api/tags >/dev/null && echo LOCAL_OK || echo LOCAL_FAIL +' + +echo "=== verify LAN is closed ===" +if curl -sS --max-time 3 http://192.168.0.188:11434/api/tags >/dev/null 2>&1; then + echo "FAIL: 192.168.0.188:11434 仍可從 LAN 連線" + exit 1 +fi + +echo "PASS: 192.168.0.188:11434 已拒絕 LAN 連線"