Ollama API 沒有密碼:開放 11434 的風險與修正
Ollama API 預設沒有驗證。任何能連線到 port 11434 的人,都能執行模型、下載 2 到 40 GB 的模型,本文依序說明三種修正方式。
Ollama API 沒有密碼
Ollama API 沒有驗證機制。您執行的伺服器中沒有使用者、密碼、金鑰檢查或任何允許清單。任何能對 port 11434 建立 TCP 連線的對象,都能列出您的模型、執行模型、下載新模型,以及刪除現有模型。
官方文件明確說明:「透過 http://localhost:11434 在本機存取 Ollama API 時,不需要驗證。」本機 一詞涵蓋了整個安全模型。Ollama 預設繫結至 127.0.0.1,因此在筆記型電腦上,loopback 介面就是存取控制。將該 listener 移至公開位址後,存取控制就會消失,因為沒有其他機制取代它。
這就是 VPS(virtual private server)環境中這項設定很重要的原因。預設設定是安全的。多數人進行的第一項變更,是開放 listener,讓第二台機器可以使用模型;但這項變更會一次移除所有保護。
開放連接埠 11434 會暴露什麼
所有 endpoint 都可存取。沒有唯讀模式,也沒有獨立的管理連接埠。以下是真實的請求,目標是伺服器位址,而不是 localhost:
# List every model on the box
curl http://SERVER_IP:11434/api/tags
# See what is loaded into memory right now
curl http://SERVER_IP:11434/api/ps
# Run a prompt on your hardware
curl http://SERVER_IP:11434/api/generate -d '{"model":"llama3.2","prompt":"Why is the sky blue?"}'
# Write several gigabytes to your disk
curl http://SERVER_IP:11434/api/pull -d '{"model":"llama3.2"}'
# Remove a model
curl -X DELETE http://SERVER_IP:11434/api/delete -d '{"model":"llama3.2"}'以維運角度來看,會發生四個問題:
- 你的 CPU 或 GPU 會替他人執行推論。若方案提供有限的 CPU 合理使用額度,持續負載就代表陌生人正在消耗你的額度;當你不再是唯一的呼叫端時,要控制 VPS 上的 AI 工作負載成本會困難得多。
/api/pull會將資料寫入你的磁碟。每個模型的大小介於 2 到 40 GB。反覆執行 pulls 會填滿磁碟區;磁碟滿載後,主機上的其他服務也會故障,不只 Ollama。- 請求會進入你的程序並被記錄。在預設日誌層級下,Ollama 只會記錄中繼資料,因此你會得到 endpoint、狀態、延遲和用戶端位址,而不是 prompt 文字。這仍會記錄誰使用你的主機以及用途,並留在你的 journal 中;而且這不是你主動選擇要收集的資料。
/api/delete會移除模型。要恢復模型,就必須再次透過你自己的頻寬下載。
這些問題都不需要利用漏洞。這只是文件所述的 API 依設計正常運作。
Ed25519 金鑰不是存取控制
搜尋「Ollama API key」時,會找到兩種不同的項目。兩者都不是伺服器的密碼,先區分清楚即可消除大部分混淆。
第一種是身分金鑰組。 Ollama 第一次執行時會產生 Ed25519 金鑰組。在 Linux 上,安裝腳本會建立名為 ollama 的系統使用者,並將其家目錄設為 /usr/share/ollama,因此金鑰組位於:
/usr/share/ollama/.ollama/id_ed25519
/usr/share/ollama/.ollama/id_ed25519.pub這組金鑰用於對外驗證。ollama signin 會將公開金鑰註冊到你的 ollama.com 帳戶,而 Ollama 會用它授權你將模型推送到 registry,或拉取私有模型。它用來向 ollama.com 證明你的機器身分,不會對連線到你機器的用戶端提出任何要求。刪除或輪替這組金鑰,或從未建立這組金鑰,都不會影響哪些人可以呼叫你的 API。
第二種是 OLLAMA_API_KEY。 該變數包含你在 https://ollama.com/settings/keys 建立的金鑰,而你的用戶端呼叫位於 https://ollama.com/api 的代管 API 時,會將它以 Authorization: Bearer $OLLAMA_API_KEY 傳送。這是對方服務使用的認證資訊,你是以用戶端身分使用它。你自己的 ollama serve 不會讀取它。在 VPS 上設定 OLLAMA_API_KEY,不會為 VPS 加上密碼。
因此,沒有任何設定需要啟用。以下 3 種防護的原理相同:讓連接埠無法連線,並在前方放置確實執行驗證的元件。
確認伺服器目前正在監聽的連接埠
sudo ss -tlnp | grep 11434安全的結果會列出 loopback 位址:
LISTEN 0 4096 127.0.0.1:11434 0.0.0.0:* users:(("ollama",pid=812,fd=3))暴露的結果會列出所有網路介面:
LISTEN 0 4096 0.0.0.0:11434 0.0.0.0:* users:(("ollama",pid=812,fd=3))0.0.0.0 代表主機上的所有 IPv4 位址,包括公開位址。*:11434 和 [::]:11434 則代表包含 IPv6 的相同設定。
現在從外部確認。請在筆電上執行,不要在伺服器上執行:
curl -m 5 http://YOUR_SERVER_IP:11434/api/versioncurl: (28) Connection timed out after 5001 milliseconds 是預期結果,curl: (7) Failed to connect ... Connection refused 也是。包含 version 欄位的 JSON 物件表示任何提出請求的人都能存取整個 API。在伺服器本身使用 curl 測試無法證明任何事,因為 loopback 一律會回應。
暴露通常有兩種方式。第一種是有人刻意修改設定,因為需要讓另一台機器存取模型:
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"這一行就是造成暴露的全部設定。第二種方式是 Docker,完全不需要你修改任何設定。該方式會在下方的專節說明。
防禦 1:維持在 localhost,透過 tunnel 連入
優先採用這個方法。它不需要安裝新軟體,也不會建立可能外洩的認證資訊。該連接埠不會存在於公開介面上,因此掃描無法找到它。
明確設定 bind address,不要依賴預設值:
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"這會寫入 /etc/systemd/system/ollama.service.d/override.conf。套用設定並檢查:
sudo systemctl daemon-reload
sudo systemctl restart ollama
sudo ss -tlnp | grep 11434ss 現在應顯示 127.0.0.1:11434。如果仍顯示 0.0.0.0,表示另一個 drop-in 檔案優先套用。執行 systemctl cat ollama.service,列出該 unit 及所有 drop-in 檔案的路徑,然後刪除過時的檔案。
若要從筆記型電腦使用該模型,請透過 SSH 轉送連接埠:
ssh -N -L 11434:127.0.0.1:11434 you@your-server-L 11434:127.0.0.1:11434 會在筆記型電腦上開啟連接埠 11434,並將送達該連接埠的內容轉送至伺服器所看到的 127.0.0.1:11434。-N 會告訴 SSH 不要執行遠端命令,因此該程序只會維持 tunnel 開啟。tunnel 執行期間,可在筆記型電腦上使用:
curl -s http://localhost:11434/api/tags你會遇到兩種錯誤。bind [127.0.0.1]:11434: Address already in use 表示筆記型電腦正在該連接埠上執行自己的 Ollama,因此請使用 -L 11500:127.0.0.1:11434 選擇其他本機連接埠,並將用戶端指向 11500。若 tunnel 已成功建立,但收到空白回應,表示 SSH 正常運作,而 Ollama 未在伺服器端監聽;請先在伺服器上檢查 ss,再修改 SSH 命令。
如果有多台用戶端機器,使用私有網路比每個人各自建立一條 tunnel 更合適。先透過 WireGuard 或 Tailscale 將機器加入同一個網路,再將 Ollama 綁定到該網路上的位址,而不是 0.0.0.0:
[Service]
Environment="OLLAMA_HOST=10.8.0.1:11434"如此一來,該連接埠只會存在於必須使用金鑰才能加入的介面上。即使防火牆規則設定錯誤,導致意外允許所有來源,也無法公開不存在於公用介面上的 listener。
防禦 2:檢查 bearer token 的反向代理
當公開網際網路上的系統必須呼叫模型時,讓 Ollama 維持在 loopback 上,並在前方放置代理。代理負責終止 TLS(transport layer security),並拒絕缺少正確標頭的請求。Ollama 仍只接受來自 127.0.0.1 的連線,因此代理是唯一的進入路徑。
先產生真正的 token。不要手動編造:
openssl rand -base64 36以下是會檢查 token 的 nginx site:
map $http_authorization $ollama_ok {
default 0;
"Bearer PASTE_YOUR_GENERATED_TOKEN_HERE" 1;
}
server {
listen 443 ssl;
server_name llm.example.com;
ssl_certificate /etc/letsencrypt/live/llm.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/llm.example.com/privkey.pem;
location = /api/pull { return 403; }
location = /api/delete { return 403; }
location = /api/push { return 403; }
location / {
if ($ollama_ok = 0) { return 401; }
proxy_pass http://127.0.0.1:11434;
proxy_set_header Host 127.0.0.1:11434;
proxy_buffering off;
proxy_read_timeout 600s;
}
}其中有 5 行設定會實際發揮作用,每一行都能避免原本會遇到的故障。
在 nginx 中,if 放在 location 區塊內通常不是好做法,但內容恰好為 return 是兩種可預期運作的形式之一,因此這種用法是安全的。
location = /api/pull 是精確比對。nginx 對精確比對的優先順序高於 location / 前綴,因此這 3 個端點會在檢查 token 前先遭到拒絕。有效 token 只能取得推論能力,不能讓對方填滿磁碟。
proxy_set_header Host 127.0.0.1:11434; 很重要,因為 Ollama 會檢查傳入的 Host 與 Origin 標頭。直接傳遞代理的公開主機名稱,可能產生由 Ollama 而非 nginx 傳回的 403 Forbidden,導致除錯困難。對於需要允許特定來源的瀏覽器用戶端,OLLAMA_ORIGINS 是另一項必要設定。
proxy_buffering off; 很重要,因為 Ollama 會逐一串流傳回回應 token。啟用 buffering 時,nginx 會先暫存整個串流,最後一次交付,因此用戶端在整個生成期間看起來都像是停止回應。
proxy_read_timeout 600s; 很重要,因為 nginx 的預設值是 60 秒。CPU 上的長時間生成很容易超過這段時間,用戶端會收到 504 Gateway Time-out,而 /var/log/nginx/error.log 會記錄 upstream timed out (110: Connection timed out) while reading response header from upstream。請求其實仍在處理中,只是 nginx 放棄等待。
重新載入並測試兩條路徑:
sudo nginx -t && sudo systemctl reload nginx
curl -s -o /dev/null -w '%{http_code}\n' https://llm.example.com/api/tags
curl -s -H "Authorization: Bearer YOUR_TOKEN" https://llm.example.com/api/tags第一個命令應輸出 401。第二個命令應輸出模型清單。如果第一個命令也回傳模型清單,表示 map 區塊放在錯誤的範圍。它應位於 http 層級,因此請將它放在 /etc/nginx/conf.d/ 下的檔案中,或放在 server 區塊上方,不要放在 server 內。
Caddy 也能完成相同工作,使用 4 行設定啟用基本驗證。對瀏覽器用戶端而言,這通常比 bearer token 更合適:
llm.example.com {
basic_auth {
apiuser PASTE_BCRYPT_HASH_HERE
}
reverse_proxy 127.0.0.1:11434
}執行 caddy hash-password 來產生它所需的 bcrypt 雜湊。一個容易混淆的命名差異是:在 Caddy v2.8 之前,指令名稱是 basicauth;現在則是 basic_auth。因此,從較舊指南複製的設定會無法載入,Caddy 也會指出它不認得該指令。
無論選擇哪個代理,這都是所有人共用的單一 secret。持有它的每個用戶端都具備相同存取權。撤銷它表示必須修改設定,並同時更新所有呼叫端。
防禦 3:為每個用戶端簽發金鑰的閘道
當模型被不只一個人員或應用程式呼叫時,共用 token 很快就會不敷使用。你無法判斷是哪個用戶端造成負載,也無法在不中斷所有用戶端的情況下,單獨停用其中一個。閘道位於原本 proxy 的位置,使用相同的 OpenAI-compatible API,為每個用戶端簽發獨立金鑰,並記錄各金鑰的使用情況。自架 LiteLLM 閘道 是常見的做法;除了存取控制之外,它還能提供每個金鑰的預算與請求日誌。
防禦 1 的規則不變。Ollama 綁定至 127.0.0.1,只有閘道程序會與它通訊,而且只有閘道服務會監聽公開連接埠。如果伺服器的 11434 埠仍對全世界開放,在這台伺服器上設定閘道就只是裝飾,因為呼叫端可以直接繞過閘道。
防火牆陷阱:已發布的容器連接埠會繞過 UFW
這就是為什麼有些伺服器的擁有者明明正確設定了防火牆,伺服器上仍會出現暴露的服務執行個體。
UFW(uncomplicated firewall)會將規則寫入 kernel 的 filter table 中的 INPUT chain,而 INPUT 會處理目的地是主機本身的封包。Docker 的 -p flag 會將 destination NAT(network address translation)規則寫入 nat table 中的 PREROUTING chain,kernel 會在決定封包去向前先處理這項規則。路由決策發生時,目的地已經被改寫為容器的位址,因此封包會被轉送,而不是在本機交付;它會通過 FORWARD,而不是 INPUT。UFW 的 INPUT 規則完全不會被查閱,因此封包會繞過防火牆,而不是通過防火牆。
這就是為什麼以下步驟會讓 port 11434 對網際網路開放:
sudo ufw default deny incoming
sudo ufw enable
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama而 sudo ufw status 仍會顯示防火牆已啟用,且預設政策為 deny。這兩個結果可以同時正確,正是因此人們容易相信錯誤的結果。你可以查看造成這個結果的規則:
sudo iptables -t nat -L DOCKER -n修正方式是在 publish flag 中指定一個位址:
docker rm -f ollama
docker run -d -v ollama:/root/.ollama -p 127.0.0.1:11434:11434 --name ollama ollama/ollama-p 11434:11434 是 -p 0.0.0.0:11434:11434 的縮寫。指定 127.0.0.1 會將對應關係的主機端綁定至 loopback,因此 SSH tunnel 和 reverse proxy 仍可連線,而網際網路無法連線。此處重新建立容器是安全的,因為模型位於具名的 ollama volume 中,而不在容器內。
確認這兩種檢查結果一致:
docker port ollama
sudo ss -tlnp | grep 11434docker port ollama 應輸出 11434/tcp -> 127.0.0.1:11434。如果輸出 0.0.0.0:11434,表示服務仍然暴露在外。了解一次這個機制後,便適用於之後發布的所有容器:為什麼 Docker 發布的連接埠會繞過 UFW 說明 DOCKER-USER chain,以及 Docker 重新啟動後仍會保留的規則。如果你仍在建立主機本身的政策,新 VPS 需要的 UFW 規則 說明這套設定所依據的基礎。
執行程序所使用的帳號
Linux 安裝腳本會建立專用帳號,並以該帳號執行服務:
useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama/etc/systemd/system/ollama.service 中的單元會設定 User=ollama 和 Group=ollama。請勿修改。直接在終端機手動執行的快速 ollama serve,會以目前登入帳號的身分執行。如果該帳號是 root,未經驗證的 API 就會以 root 身分寫入檔案。請確認實際使用的帳號:
ps -o user= -C ollama結果應為 ollama。其他結果都表示有手動啟動的程序與該單元並行執行,或取代該單元執行。同樣的原則適用於日後新增的所有 daemon,而以最低權限使用者執行服務能正確處理這項問題。
如何確認 Ollama API endpoint 已受到保護
無論採用哪種設定,從另一台機器執行以下測試即可確認:
curl -m 5 http://YOUR_SERVER_IP:11434/api/version
curl -m 5 http://YOUR_SERVER_IP:11434/api/tags兩個請求都應逾時或遭拒絕。如果架設了 proxy,則使用 proxy hostname 存取相同的兩個路徑時,未提供 credentials 應回傳 401,提供 credentials 則應回傳實際的 JSON。
接著讀取一次 access log,因為其中會顯示服務公開時是否有人找到該埠:
journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1Ollama 會為每個請求寫入一行,並包含 client address:
[GIN] 2026/08/12 - 14:01:10 | 200 | 103.965898ms | 127.0.0.1 | POST "/api/generate"Ollama 綁定至 loopback 後,每一行都應顯示一次 127.0.0.1,因為連線只能從該位址進入。該欄位若出現 public address,表示請求來自外部;timestamp 則會顯示請求時間。該命令完全沒有輸出,才是預期結果。如果你不熟悉這部分的模型設定,在 VPS 上執行 Ollama涵蓋安裝、模型大小,以及決定實際可載入內容的記憶體限制。
FAQ
Ollama 有 API key 或密碼嗎?
沒有。您執行的伺服器完全沒有任何驗證機制,官方文件也說明,存取 API 不需要驗證。被稱為「Ollama API key」的兩種項目,其用途其實都不是用來驗證您自行執行的伺服器。/usr/share/ollama/.ollama/ 中的 Ed25519 金鑰組用來向 ollama.com 證明您的機器身分,讓您可以推送模型及拉取私有模型。OLLAMA_API_KEY 是您的用戶端傳送至 https://ollama.com/api 託管 API 的認證資訊。您自己的 ollama serve 兩者都不會讀取,因此存取控制必須由網路或前端 proxy 提供。
如果我有防火牆,OLLAMA_HOST=0.0.0.0 安全嗎?
只有在該主機上沒有其他程式寫入防火牆規則時才安全。0.0.0.0 表示監聽器確實存在於公開介面上,而您完全依賴防火牆阻止外部存取。Docker 發布連接埠後,這項信任就會失效,因為 Docker 加入 nat table 的 DNAT 規則,會在封包到達 UFW 所在的 INPUT chain 前先套用。因此封包會被轉送,UFW 根本看不到它。繫結至 127.0.0.1 或私有 tunnel 位址,可將監聽器從公開介面移除。如此一來,即使防火牆設定錯誤,也沒有監聽器可供暴露。
如何確認 Ollama 連接埠是否對網際網路開放?
在伺服器上執行 sudo ss -tlnp | grep 11434,再從另一台機器執行 curl -m 5 http://YOUR_SERVER_IP:11434/api/version。ss 顯示 127.0.0.1:11434,且遠端 curl 逾時,就是您要確認的結果。若 ss 顯示 0.0.0.0:11434 或 *:11434,同時遠端 curl 回傳 JSON,表示完整 API 可被存取。不要在伺服器本身使用 curl 測試,因為 loopback 會回應實際繫結位址所提供的內容。
我可以直接將連接埠從 11434 改成隨機連接埠嗎?
不行,原因值得說明。更換連接埠只會讓針對單一連接埠的掃描變慢,其他情況沒有改善。掃描器會掃描整個連接埠範圍,而對 /api/tags 發出一個請求,就能辨識服務實際使用的連接埠。更換連接埠也會使所有用戶端的預設設定失效,日後更難理解自己的環境。請改為繫結至 loopback,這會移除監聽器,而不是將它搬到其他位置。
有人存取了我開放的 Ollama。我該檢查什麼?
先將它繫結至 127.0.0.1 並重新啟動服務,讓暴露狀態先停止,再開始調查。接著執行 journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1,查看哪些外部位址在何時呼叫了哪些端點。將 ollama list 與您原本預期擁有的模型相比對,因為 /api/pull 不需要驗證,而您未曾拉取的模型既會占用磁碟空間,也是遭入侵的證據。使用 df -h 檢查可用空間。Ollama 在預設日誌層級不會記錄 prompt 文字,因此您能知道誰提出請求及使用哪個模型,但不知道產生了哪些內容。