Ollama API没有密码?11434端口为何危险
Ollama 默认不提供身份验证。开放11434端口后,任何可连接的客户端都能运行模型、下载新模型或删除模型,本文按顺序介绍3种修复方法。
Ollama API 没有密码
Ollama API 没有身份验证。您运行的服务器中没有用户验证、密码、密钥检查或任何允许列表。任何能够与 11434 端口建立 TCP 连接的客户端,都可以列出您的模型、运行模型、下载新模型并删除现有模型。
官方文档对此有明确说明:“通过 http://localhost:11434 在本地访问 Ollama API 时不需要身份验证。”安全模型完全取决于 本地 这个限定词。Ollama 默认绑定到 127.0.0.1,因此在笔记本电脑上,回环接口就是访问控制。将监听地址改为公网地址后,访问控制也随之消失,因为没有其他机制取代它。
这就是 VPS(虚拟专用服务器)环境中该问题重要的原因。默认配置是安全的。大多数人进行的第一项更改,是开放监听地址,让第二台计算机使用该模型;但这项更改会一次性移除所有保护。
开放端口 11434 会暴露什么
所有端点都可访问。没有只读模式,也没有单独的管理端口。以下是真实请求,只是目标地址改成了服务器地址,而不是 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"}'从运维角度看,会出现 4 个问题:
- 他人会使用您的 CPU 或 GPU 执行推理。在具有公平使用 CPU 配额的套餐中,持续负载意味着陌生人正在消耗您的配额;一旦您不再是唯一的调用方,控制 VPS 上的 AI 工作负载成本就会困难得多。
/api/pull会向您的磁盘写入数据。每个模型占用 2 到 40 GB。循环拉取模型会填满卷;磁盘已满后,主机上的其他服务也会中断,不只是 Ollama。- 请求会进入您的进程并被记录。Ollama 默认只记录元数据,因此日志中会有端点、状态、延迟和客户端地址,而不会有提示文本。这仍然会记录谁在使用您的主机以及用途,并保存在 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.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 设置密码。
因此,没有需要启用的设置。下面的三种防护方式原理相同:确保端口无法访问,并在端口前面放置一个会执行检查的组件。
查看服务器当前正在监听的地址
sudo ss -tlnp | grep 11434安全结果会显示回环地址:
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 测试不能证明任何问题,因为回环地址始终会响应。
暴露通常有两种来源。第一种是有人为了让另一台机器访问模型而主动修改配置:
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"这一行就是造成暴露的全部配置。第二种来源是 Docker,完全不需要手动编辑配置。相关内容将在下面的章节中介绍。
防护方案 1:仅监听 localhost,并通过隧道访问
优先使用此方案。它无需安装新软件,也不会创建可能泄露的凭据。该端口从不出现在公网接口上,因此无法通过扫描发现。
显式设置绑定地址,不要依赖默认值:
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 11434此时 ss 应显示 127.0.0.1:11434。如果仍显示 0.0.0.0,说明另一个 drop-in 文件覆盖了当前配置。运行 systemctl cat ollama.service,列出该单元及所有 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 不运行远程命令,因此该进程只会保持隧道打开。隧道运行期间,可以在笔记本电脑上执行:
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。隧道连接正常但返回空响应,表示 SSH 工作正常,而服务器端的 Ollama 没有监听,因此先在服务器上检查 ss,再修改 SSH 命令。
如果有多台客户端计算机,使用专用网络优于为每个人建立一个隧道。让这些计算机加入 WireGuard 或 Tailscale,然后将 Ollama 绑定到该网络上的地址,而不是绑定到 0.0.0.0:
[Service]
Environment="OLLAMA_HOST=10.8.0.1:11434"这样,该端口只存在于需要使用密钥才能加入的接口上。即使防火墙配置错误,这种方式也能提供额外保护,因为即使规则意外允许所有来源,公网接口上不存在该监听器,外部仍无法访问它。
防护 2:检查 bearer token 的反向代理
当公网中的某个系统必须调用模型时,应让 Ollama 继续监听 loopback,并在前面部署代理。代理负责终止 TLS(传输层安全),并拒绝缺少正确请求头的请求。Ollama 仍只接受来自 127.0.0.1 的连接,因此代理是唯一的访问路径。
先生成真正的 token。不要手动编造:
openssl rand -base64 36下面是一个会检查该 token 的 nginx 站点配置:
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;
}
}其中有五行配置在执行实际的防护工作,每一行都能阻止一种常见故障。
在 nginx 中,if 放在 location 块内通常不是好做法。不过,长度恰好为 return 的请求体是两种行为可预测的形式之一,因此这里的用法是安全的。
location = /api/pull 表示精确匹配。nginx 对精确匹配的优先级高于 location / 前缀,因此这三个端点会在检查 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 流式返回响应。启用缓冲后,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 也能完成相同的工作,并且只需四行 basic authentication 配置。对于浏览器客户端,这通常比 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 会指出它无法识别的指令。
无论选择哪种代理,这都是所有用户共享的一个密钥。持有该密钥的每个客户端都拥有完全相同的访问权限。撤销该密钥意味着修改配置,并同时更新所有调用方。
防护措施 3:按客户端签发密钥的网关
当不止一个用户或应用调用模型时,共享令牌很快就不够用了。您无法判断负载由哪个客户端造成,也无法在不切断所有客户端的情况下单独停用其中一个。网关位于原代理所在的位置,使用相同的 OpenAI 兼容 API,按客户端分别签发密钥,并记录每个密钥的使用情况。自托管 LiteLLM 网关是通常的解决方案;在访问控制之外,它还提供按密钥设置预算和记录请求日志的功能。
防护措施 1 中的规则不变。Ollama 绑定到 127.0.0.1,只有网关进程与其通信,并且只有网关服务监听公网。若网关所在主机的 11434 端口仍对公网开放,网关就只是摆设,因为调用方可以直接绕过它。
防火墙陷阱:发布容器端口会绕过 UFW
这就是为什么服务器所有者已经正确配置防火墙,但服务器上仍会出现暴露的实例。
UFW(uncomplicated firewall)会将规则写入内核的 filter 表中的 INPUT 链,而 INPUT 负责处理目标为主机自身的数据包。Docker 的 -p 标志会将目标 NAT(网络地址转换)规则写入 nat 表中的 PREROUTING 链,内核会在决定数据包去向之前先处理该规则。路由决策发生时,目标地址已经被改写为容器地址,因此数据包会被转发,而不是在本地交付;它会经过 FORWARD,而不是 INPUT。UFW 的 INPUT 规则根本不会被检查,因此数据包绕过了防火墙,而不是经过防火墙。
因此,以下序列会让端口 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 仍会报告防火墙处于活动状态,并且默认策略为拒绝。这两个结果可以同时正确,这正是人们会相信错误结果的原因。可以查看导致该问题的规则:
sudo iptables -t nat -L DOCKER -n修复方法是在发布标志中指定地址:
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 会将映射的主机端绑定到回环地址,因此 SSH 隧道和反向代理仍可访问该端口,而互联网无法访问。此处重新创建容器是安全的,因为模型存储在命名的 ollama 卷中,而不在容器内部。
确认这两种查看方式的结果一致:
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 链,以及 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 端点是否安全
无论您采用哪种方案,都必须从另一台机器执行一次测试:
curl -m 5 http://YOUR_SERVER_IP:11434/api/version
curl -m 5 http://YOUR_SERVER_IP:11434/api/tags两个请求都应超时或被拒绝。如果您配置了代理,则使用代理主机名访问相同的两个路径时,未提供凭据应返回 401,提供凭据后应返回有效的 JSON。
然后读取一次访问日志,因为日志可以显示服务暴露期间是否有人发现了该端口:
journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1Ollama 会为每个请求写入一行日志,并记录客户端地址:
[GIN] 2026/08/12 - 14:01:10 | 200 | 103.965898ms | 127.0.0.1 | POST "/api/generate"Ollama 绑定到 loopback 后,每一行都应显示一次 127.0.0.1,因为连接只能从该地址到达。如果该列中出现公网地址,说明请求来自外部;时间戳可以显示请求发生的时间。该命令没有任何输出,才是预期结果。如果您不熟悉 Ollama 相关操作,请参阅在 VPS 上运行 Ollama,其中介绍了安装、模型大小以及决定实际可加载内容的内存限制。
FAQ
Ollama 是否有 API 密钥或密码?
没有。您运行的服务器不提供任何形式的身份验证,官方文档也说明访问 API 无需身份验证。所谓的“Ollama API 密钥”实际上指向另外两种凭据。/usr/share/ollama/.ollama/ 中的 Ed25519 密钥对用于向 ollama.com 证明您的计算机身份,以便推送模型和拉取私有模型。OLLAMA_API_KEY 是客户端向 https://ollama.com/api 上的托管 API 发送的凭据。您自己的 ollama serve 不读取这两种凭据,因此访问控制必须由网络或前置代理提供。
如果有防火墙,OLLAMA_HOST=0.0.0.0 是否安全?
只有在该主机上没有其他程序写入防火墙规则时才安全。0.0.0.0 表示监听器确实存在于公网接口上,而您完全依赖防火墙阻止外部访问。一旦 Docker 发布端口,这种依赖就会失效,因为 Docker 添加到 nat 表中的 DNAT 规则,会在数据包到达 UFW 所在的 INPUT 链之前被处理。因此,数据包会被转发,UFW 根本看不到它。绑定到 127.0.0.1 或私有隧道地址,可以将监听器从公网接口移除。这样即使防火墙配置错误,也没有可暴露的监听器。
如何检查 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 测试,因为回环接口会根据绑定地址返回响应,无法反映公网访问情况。
是否可以将端口从 11434 改成某个随机端口?
不可以,原因需要说明。更换端口只能减慢针对这一个端口的扫描。扫描器会遍历整个端口范围,而对 /api/tags 发出一个请求,就能识别该服务实际使用的端口。更换端口还会破坏所有客户端的默认配置,使您之后更难理解和维护自己的配置。应改为绑定到回环接口,这样可以移除监听器,而不是将其移动到另一个端口。
有人访问了我开放的 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 在默认日志级别下不会记录提示词文本,因此您只能知道谁请求了哪个模型,无法知道生成了什么内容。