Ollama sem palavra-passe: como proteger a API 11434
A API do Ollama não tem autenticação: quem alcança a porta 11434 pode executar, baixar e apagar modelos. Veja as três correções, na ordem certa.
A API do Ollama não tem palavra-passe
A API do Ollama não tem autenticação. Não existe utilizador, palavra-passe, verificação de chave nem allowlist no servidor que executa. Qualquer sistema que consiga abrir uma ligação TCP à porta 11434 pode listar os seus modelos, executá-los, descarregar modelos novos e eliminar os que já tem.
A documentação oficial afirma-o claramente: "Não é necessária autenticação ao aceder localmente à API do Ollama através de http://localhost:11434." A palavra localmente define todo o modelo de segurança. Por predefinição, o Ollama faz bind a 127.0.0.1, por isso, num portátil, a interface de loopback funciona como controlo de acesso. Se mover esse listener para um endereço público, o controlo de acesso desaparece, porque nada o substitui.
É por isso que isto é importante num VPS (servidor privado virtual). A predefinição é segura. A primeira alteração que a maioria das pessoas faz, abrir o listener para permitir que uma segunda máquina use o modelo, é a alteração que remove todas as proteções de uma só vez.
O que uma porta 11434 aberta revela
Todos os endpoints. Não existe um modo somente leitura nem uma porta de administração separada. Estas são as requisições reais, direcionadas para o endereço do servidor em vez de 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"}'Em termos operacionais, quatro problemas ocorrem:
- A sua CPU ou GPU executa inferência para outra pessoa. Num plano com uma franquia de CPU de uso razoável, a carga contínua é a sua franquia a ser consumida por um desconhecido, e manter os custos das cargas de trabalho de IA sob controlo num VPS torna-se muito mais difícil quando não é o único cliente.
/api/pullescreve no seu disco. Os modelos ocupam entre dois e quarenta gigabytes cada. Um ciclo de downloads preenche o volume, e um disco cheio interrompe todos os outros serviços no servidor, não apenas o Ollama.- As requisições entram no seu processo e são registadas. No nível de log predefinido, o Ollama regista apenas metadados. Obtém o endpoint, o estado, a latência e o endereço do cliente, mas não o texto do prompt. Ainda assim, fica registado quem utilizou o seu servidor e para quê, no seu journal, sem que tenha escolhido recolher esses dados.
/api/deleteremove modelos. Para recuperá-los, é necessário fazer novamente o download usando a sua própria largura de banda.
Nada disso requer uma exploração de vulnerabilidade. É a API documentada a funcionar exatamente como foi concebida.
A chave Ed25519 não é um mecanismo de controlo de acesso
Pesquise por "Ollama API key" e encontrará duas coisas diferentes. Nenhuma delas é uma palavra-passe do seu servidor. Distingui-las elimina a maior parte da confusão.
A primeira é o par de chaves de identidade. O Ollama gera um par de chaves Ed25519 na primeira execução. No Linux, o script de instalação cria um utilizador de sistema chamado ollama, com o diretório pessoal em /usr/share/ollama. Por isso, o par fica aqui:
/usr/share/ollama/.ollama/id_ed25519
/usr/share/ollama/.ollama/id_ed25519.pubEssa chave é usada para ligações de saída. ollama signin regista a parte pública na sua conta ollama.com. É essa chave que o autoriza a enviar um modelo para o registry ou a obter um modelo privado. A chave comprova a identidade da sua máquina perante o ollama.com. Não impõe qualquer requisito aos clientes que se ligam à sua máquina. Eliminá-la, substituí-la ou nunca a criar não altera quem pode chamar a sua API.
A segunda é OLLAMA_API_KEY. Essa variável contém uma chave que cria em https://ollama.com/settings/keys. O cliente envia-a como Authorization: Bearer $OLLAMA_API_KEY ao chamar a API alojada em https://ollama.com/api. É uma credencial do serviço deles, usada por si enquanto cliente. O seu próprio ollama serve nunca a lê. Definir OLLAMA_API_KEY no seu VPS não coloca uma palavra-passe no seu VPS.
Por isso, não há nenhuma definição para ativar. As três defesas abaixo funcionam da mesma forma: mantenha a porta inacessível e coloque à frente dela algo que faça a verificação.
Verifique em que endereços o servidor está escutando neste momento
sudo ss -tlnp | grep 11434O resultado seguro identifica o endereço de loopback:
LISTEN 0 4096 127.0.0.1:11434 0.0.0.0:* users:(("ollama",pid=812,fd=3))O resultado exposto identifica todas as interfaces:
LISTEN 0 4096 0.0.0.0:11434 0.0.0.0:* users:(("ollama",pid=812,fd=3))0.0.0.0 significa todos os endereços IPv4 no servidor, incluindo o endereço público. *:11434 e [::]:11434 significam o mesmo, incluindo IPv6.
Agora confirme a partir de fora. Execute isto no seu laptop, não no servidor:
curl -m 5 http://YOUR_SERVER_IP:11434/api/versioncurl: (28) Connection timed out after 5001 milliseconds é o resultado esperado, assim como curl: (7) Failed to connect ... Connection refused. Um objeto JSON com um campo version significa que toda a API está acessível a qualquer pessoa que faça uma solicitação. Testar com curl no próprio servidor não prova nada, porque o loopback responde sempre.
A exposição normalmente ocorre de uma de duas formas. A primeira é uma edição deliberada, porque alguém precisava que uma segunda máquina alcançasse o modelo:
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"Essa única linha é toda a exposição. A segunda forma é o Docker, e ela não exige nenhuma edição. Esse caso tem uma seção própria abaixo.
Defesa 1: mantenha no localhost e use um túnel
Comece por esta opção. Não requer software novo e não cria nenhuma credencial que possa vazar. A porta nunca existe numa interface pública, portanto uma varredura não a encontra.
Defina explicitamente o endereço de bind em vez de depender do valor predefinido:
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"Isto grava /etc/systemd/system/ollama.service.d/override.conf. Aplique a alteração e verifique:
sudo systemctl daemon-reload
sudo systemctl restart ollama
sudo ss -tlnp | grep 11434ss deve agora mostrar 127.0.0.1:11434. Se ainda mostrar 0.0.0.0, existe um segundo ficheiro drop-in a prevalecer. Execute systemctl cat ollama.service para listar a unidade e todos os drop-ins com os respetivos caminhos e, em seguida, elimine o ficheiro obsoleto.
Para usar o modelo a partir do seu portátil, encaminhe a porta através de SSH:
ssh -N -L 11434:127.0.0.1:11434 you@your-server-L 11434:127.0.0.1:11434 abre a porta 11434 no seu portátil e envia tudo o que chegar nessa porta para 127.0.0.1:11434, conforme visto a partir do servidor. -N indica ao SSH que não deve executar um comando remoto, para que o processo mantenha apenas o túnel aberto. Enquanto o túnel estiver ativo, isto funciona no seu portátil:
curl -s http://localhost:11434/api/tagsIrá encontrar duas falhas. bind [127.0.0.1]:11434: Address already in use significa que o seu portátil está a executar o próprio Ollama nessa porta; escolha uma porta local diferente com -L 11500:127.0.0.1:11434 e aponte o cliente para 11500. Uma resposta vazia através de um túnel que estabeleceu ligação corretamente significa que o SSH está a funcionar, mas o Ollama não está a escutar no lado do servidor. Nesse caso, verifique ss no servidor antes de alterar o comando SSH.
Para várias máquinas cliente, uma rede privada é melhor do que um túnel por pessoa. Coloque as máquinas numa rede WireGuard ou Tailscale e, em seguida, associe o Ollama ao endereço dessa rede, em vez de 0.0.0.0:
[Service]
Environment="OLLAMA_HOST=10.8.0.1:11434"A porta passa a existir apenas numa interface à qual é necessário ter uma chave para aderir. Isto também continua a funcionar mesmo perante um erro de firewall, porque uma regra que permita acidentalmente o acesso a toda a Internet não consegue expor um serviço que a interface pública não tem.
Defesa 2: um reverse proxy que verifica um bearer token
Quando algo na Internet pública precisar de chamar o modelo, mantenha o Ollama em loopback e coloque um proxy à frente dele. O proxy termina o TLS (transport layer security) e rejeita pedidos sem o cabeçalho correto. O Ollama continua a aceitar ligações apenas de 127.0.0.1, por isso o proxy é a única entrada.
Gere primeiro um token real. Não invente um manualmente:
openssl rand -base64 36Um site nginx que o verifica:
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;
}
}Há cinco linhas que fazem trabalho efetivo, e cada uma evita uma falha que, de outro modo, encontraria.
if dentro de um bloco location é normalmente uma má ideia no nginx, mas um corpo com exatamente return é uma das duas formas que se comportam de maneira previsível, por isso esta utilização é segura.
location = /api/pull é uma correspondência exata, e o nginx atribui prioridade às correspondências exatas sobre o prefixo location /, por isso esses três endpoints são recusados antes de o token ser sequer considerado. Um token válido permite fazer inferência, não encher o disco.
proxy_set_header Host 127.0.0.1:11434; é importante porque o Ollama inspeciona os cabeçalhos Host e Origin recebidos. Passar diretamente o nome de host público do proxy pode produzir um 403 Forbidden gerado pelo Ollama em vez do nginx, o que dificulta a depuração. OLLAMA_ORIGINS é a outra opção, para um cliente de browser que precise de uma origem específica autorizada.
proxy_buffering off; é importante porque o Ollama transmite a resposta token a token. Com o buffering ativo, o nginx retém o fluxo e entrega tudo de uma vez no fim, fazendo o cliente parecer bloqueado durante toda a geração.
proxy_read_timeout 600s; é importante porque o nginx usa 60 segundos por predefinição. Uma geração longa em CPU ultrapassa facilmente esse tempo, o cliente recebe 504 Gateway Time-out e /var/log/nginx/error.log regista upstream timed out (110: Connection timed out) while reading response header from upstream. O pedido continuava a ser processado. O nginx desistiu dele.
Recarregue a configuração e teste os dois caminhos:
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/tagsO primeiro deve apresentar 401. O segundo deve apresentar a lista de modelos. Se o primeiro também devolver a lista de modelos, o bloco map está no âmbito errado. Ele deve estar ao nível http, por isso coloque-o num ficheiro em /etc/nginx/conf.d/ ou acima do bloco server, nunca dentro de server.
O Caddy faz o mesmo trabalho com autenticação básica em quatro linhas, o que é mais adequado para um cliente de browser do que um bearer token:
llm.example.com {
basic_auth {
apiuser PASTE_BCRYPT_HASH_HERE
}
reverse_proxy 127.0.0.1:11434
}Execute caddy hash-password para gerar o hash bcrypt esperado. Há uma diferença de nomenclatura importante: a diretiva chamava-se basicauth antes do Caddy v2.8 e chama-se basic_auth agora. Por isso, uma configuração copiada de um guia antigo recusa-se a carregar, e o Caddy indica a diretiva que não reconheceu.
Independentemente do proxy escolhido, este é um segredo partilhado por todos. Todos os clientes que o possuam têm acesso idêntico, e revogá-lo implica editar a configuração e atualizar todos os chamadores ao mesmo tempo.
Defesa 3: um gateway que emite chaves por cliente
Quando mais do que uma pessoa ou aplicação chama o modelo, um token partilhado deixa de ser suficiente. Não é possível saber qual cliente causou a carga, nem bloquear apenas um deles sem bloquear todos. Um gateway fica no lugar onde estava o proxy, utiliza a mesma API compatível com OpenAI, emite uma chave separada por cliente e regista o que cada chave utilizou. Um gateway LiteLLM autoalojado é a resposta habitual. Além do controlo de acesso, acrescenta limites por chave e logs de pedidos.
A regra da defesa 1 não muda. O Ollama faz bind a 127.0.0.1, o gateway é o único processo que comunica com ele e o gateway é o único serviço com um listener público. Um gateway num servidor onde a porta 11434 continua aberta para a Internet é apenas decoração, porque os clientes podem contorná-lo.
A armadilha da firewall: uma porta de contentor publicada ignora a UFW
É por isso que existem instâncias expostas em servidores cujos administradores configuraram corretamente uma firewall.
A UFW (uncomplicated firewall) escreve as suas regras na cadeia INPUT da tabela filter do kernel, e INPUT trata os pacotes destinados ao próprio host. A opção -p do Docker escreve uma regra de NAT de destino (network address translation) na cadeia PREROUTING da tabela nat, que o kernel avalia antes de decidir para onde o pacote deve ir. Quando a decisão de encaminhamento acontece, o destino já foi reescrito para o endereço do contentor. Por isso, o pacote é encaminhado em vez de ser entregue localmente e atravessa FORWARD em vez de INPUT. As regras INPUT da UFW nunca são consultadas. O pacote contorna a firewall em vez de passar por ela.
É por isso que esta sequência deixa a porta 11434 aberta à Internet:
sudo ufw default deny incoming
sudo ufw enable
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollamae sudo ufw status continua a indicar que a firewall está ativa com uma política de negação por omissão. As duas leituras estão corretas ao mesmo tempo. É exatamente por isso que as pessoas confiam na leitura errada. Pode ver a regra responsável:
sudo iptables -t nat -L DOCKER -nA correção consiste em indicar um endereço na opção de publicação:
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 é uma forma abreviada de -p 0.0.0.0:11434:11434. Indicar 127.0.0.1 associa o lado do host do mapeamento à interface de loopback. Assim, o seu túnel SSH e o reverse proxy continuam a conseguir aceder-lhe, mas a Internet não. Recriar o contentor é seguro neste caso porque os modelos estão no volume nomeado ollama, não dentro do contentor.
Confirme que as duas perspetivas coincidem:
docker port ollama
sudo ss -tlnp | grep 11434docker port ollama deve apresentar 11434/tcp -> 127.0.0.1:11434. Se apresentar 0.0.0.0:11434, o serviço continua exposto. Depois de compreender este mecanismo, pode aplicá-lo a qualquer contentor que publique: por que motivo as portas publicadas pelo Docker ignoram a UFW explica a cadeia DOCKER-USER e as regras que persistem depois de um reinício do Docker. Se ainda estiver a definir a própria política do host, as regras UFW necessárias num novo VPS explica a base desta configuração. No Rocky ou AlmaLinux não existe UFW para configurar. Nesse caso, a mesma política base escrita em firewalld é o ponto de partida correto.
Com que conta o processo está a ser executado
O script de instalação do Linux cria uma conta dedicada e executa o serviço com essa conta:
useradd -r -s /bin/false -U -m -d /usr/share/ollama ollamaA unidade em /etc/systemd/system/ollama.service define então User=ollama e Group=ollama. Não altere essa configuração. Um ollama serve iniciado manualmente num terminal é executado com a conta do utilizador com que iniciou sessão. Se essa conta for root, uma API sem autenticação estará a escrever ficheiros como root. Verifique qual é o caso:
ps -o user= -C ollamaA resposta deve ser ollama. Qualquer outro resultado significa que existe um processo iniciado manualmente a correr em paralelo com a unidade ou em vez dela. O mesmo princípio aplica-se a cada daemon que adicionar posteriormente, e executar serviços com contas de privilégios mínimos trata corretamente deste caso.
Como verificar se o endpoint da API do Ollama está seguro
Independentemente da opção escolhida, um teste confirma a configuração. Ele tem de ser executado a partir de outra máquina:
curl -m 5 http://YOUR_SERVER_IP:11434/api/version
curl -m 5 http://YOUR_SERVER_IP:11434/api/tagsAmbos devem exceder o tempo limite ou ser recusados. Se criou um proxy, os mesmos dois caminhos no hostname do proxy devem devolver 401 sem credenciais e JSON real com elas.
Depois, leia o log de acesso uma vez. Ele mostra se alguém encontrou a porta enquanto ela esteve aberta:
journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1O Ollama escreve uma linha por pedido e inclui o endereço do cliente:
[GIN] 2026/08/12 - 14:01:10 | 200 | 103.965898ms | 127.0.0.1 | POST "/api/generate"Cada linha deve mostrar 127.0.0.1 uma vez que o Ollama esteja associado ao loopback, porque esse é o único endereço de onde uma ligação pode chegar. Um endereço público nessa coluna indica um pedido vindo do exterior, e o timestamp mostra quando ocorreu. Não obter qualquer saída desse comando é o resultado pretendido. Se esta parte relacionada com modelos é nova para si, executar o Ollama num VPS aborda a instalação, o dimensionamento do modelo e os limites de memória que determinam o que será efetivamente carregado.
FAQ
O Ollama tem uma chave de API ou uma palavra-passe?
Não. O servidor que executa não tem qualquer tipo de autenticação, e a documentação oficial indica que não é necessária autenticação para aceder à API. As duas coisas chamadas "Ollama API key" têm outra finalidade. O par Ed25519 em /usr/share/ollama/.ollama/ comprova a sua máquina perante ollama.com, para que possa enviar modelos e obter modelos privados. OLLAMA_API_KEY é uma credencial que o seu cliente envia para a API alojada em https://ollama.com/api. O seu próprio ollama serve não lê nenhuma delas, pelo que o controlo de acesso tem de ser aplicado pela rede ou por um proxy à frente do serviço.
OLLAMA_HOST=0.0.0.0 é seguro se eu tiver uma firewall?
Apenas enquanto nenhum outro componente escrever regras de firewall nesse servidor. 0.0.0.0 significa que o listener existe efetivamente na interface pública e que está a confiar apenas na firewall para o manter inacessível. Essa confiança deixa de ser válida quando o Docker publica uma porta, porque a regra DNAT que o Docker adiciona à tabela nat é avaliada antes de o pacote chegar à chain INPUT, onde o UFW está ativo. Assim, o pacote é encaminhado e o UFW nunca o vê. Fazer o bind a 127.0.0.1 ou ao endereço de um túnel privado remove o listener da interface pública. Deste modo, um erro na firewall deixa de ter algo para expor.
Como verifico se a minha porta do Ollama está aberta à Internet?
Execute sudo ss -tlnp | grep 11434 no servidor e curl -m 5 http://YOUR_SERVER_IP:11434/api/version a partir de outra máquina. O resultado ss a mostrar 127.0.0.1:11434 e o timeout do curl remoto são o par de respostas esperado. Se ss mostrar 0.0.0.0:11434 ou *:11434 e o curl remoto devolver JSON, a API completa está acessível. Nunca teste com curl no próprio servidor, porque o loopback responde independentemente do endereço de bind configurado.
Posso simplesmente mudar a porta de 11434 para uma porta aleatória?
Não, e o motivo é importante. Uma porta diferente não abranda nada, exceto uma verificação limitada a uma única porta. Os scanners percorrem todo o intervalo, e um pedido a /api/tags identifica o serviço, independentemente da porta de destino. Mudar a porta também quebra as predefinições de todos os clientes e torna a sua configuração mais difícil de compreender no futuro. Faça o bind ao loopback. Isso remove o listener em vez de o relocalizar.
Alguém acedeu ao meu Ollama aberto. O que devo verificar?
Faça primeiro o bind a 127.0.0.1 e reinicie o serviço, para interromper a exposição antes de iniciar a investigação. Depois, execute journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1 para ver quais endereços externos chamaram quais endpoints e em que momento. Compare ollama list com os modelos que pretendia ter, porque /api/pull não tem autenticação e um modelo que não obteve representa simultaneamente utilização de espaço em disco e evidência. Verifique o espaço livre com df -h. O Ollama não regista o texto dos prompts no nível de logs predefinido. Por isso, terá um registo de quem fez o pedido e para qual modelo, mas não do conteúdo gerado.