Como proteger a API do Ollama sem palavra-passe
A API do Ollama não exige autenticação: quem alcança a porta 11434 pode executar, baixar e excluir modelos. Veja três correções, em ordem.
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. Tudo o que conseguir abrir uma ligação TCP à porta 11434 pode listar os seus modelos, executá-los, descarregar novos modelos e eliminar os que já tem.
A documentação oficial afirma-o claramente: "No authentication is required when accessing Ollama's API locally via http://localhost:11434." A palavra localmente define todo o modelo de segurança. O Ollama faz bind a 127.0.0.1 por predefinição. Num laptop, 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 virtual privado). A predefinição é segura. A primeira alteração que muitas pessoas fazem, abrindo o listener para que uma segunda máquina possa usar o modelo, é precisamente 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 solicitações reais, direcionadas ao 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"}'Do ponto de vista operacional, quatro coisas dão errado:
- A sua CPU ou GPU executa inferências para outra pessoa. Num plano com uma franquia de CPU de uso razoável, uma carga sustentada consome a sua franquia por ação de um desconhecido, e manter os custos das cargas de trabalho de IA sob controle num VPS fica muito mais difícil quando você não é o único cliente.
/api/pullgrava dados no seu disco. Os modelos ocupam de dois a 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 solicitações chegam ao seu processo e são registadas. No nível de log padrão, o Ollama regista apenas metadados: o endpoint, o estado, a latência e o endereço do cliente, não o texto do prompt. Isso ainda cria um registo de quem usou o seu servidor e para quê, armazenado no seu journal, sem que você tenha escolhido recolhê-lo.
/api/deleteremove modelos. Para recuperá-los, é necessário descarregá-los novamente usando a sua própria largura de banda.
Nada disso exige uma exploração. É a API documentada a funcionar exatamente como foi concebida.
A chave Ed25519 não é um mecanismo de controlo de acesso
Procure por "Ollama API key" e encontrará duas coisas diferentes. Nenhuma delas é uma palavra-passe do seu servidor. Separá-las elimina grande 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 comunicações de saída. ollama signin regista a parte pública na sua conta ollama.com. É essa chave que autoriza o envio de um modelo para o registo ou a obtenção de um modelo privado. Ela comprova a identidade da sua máquina perante o ollama.com. Não exige nada dos 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 seu 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, utilizada por si como 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á a escutar 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 com IPv6 incluído.
Agora confirme a partir do exterior. Execute isto no seu portátil, não no servidor:
curl -m 5 http://YOUR_SERVER_IP:11434/api/versioncurl: (28) Connection timed out after 5001 milliseconds é a resposta pretendida, tal 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 o pedido. 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 alteração deliberada, porque alguém precisava que uma segunda máquina acedesse ao 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 é através do Docker, que não exige qualquer alteração manual. Essa situação tem uma secção própria abaixo.
Defesa 1: mantenha no localhost e use um túnel
Comece por esta opção. Ela não requer software novo e não cria nenhuma credencial que possa vazar. A porta nunca existe numa interface pública, portanto as varreduras não conseguem encontrá-la.
Defina explicitamente o endereço de bind em vez de depender do padrão:
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"Isso 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 agora deve mostrar 127.0.0.1:11434. Se continuar a mostrar 0.0.0.0, outro ficheiro drop-in está 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 laptop, 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 laptop e envia tudo o que chegar nessa porta para 127.0.0.1:11434, conforme visto a partir do servidor. -N informa o SSH para não executar um comando remoto, por isso o processo mantém apenas o túnel aberto. Enquanto o túnel estiver ativo, isto funciona no seu laptop:
curl -s http://localhost:11434/api/tagsEncontrará duas falhas. bind [127.0.0.1]:11434: Address already in use significa que o seu laptop está a executar o seu próprio Ollama nessa porta. Escolha outra porta local 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 utilizador. Coloque as máquinas numa rede WireGuard ou Tailscale. 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 limita o impacto de um erro de firewall, porque uma regra que permita acidentalmente o acesso a toda a Internet ainda não consegue expor um serviço que não está presente na interface pública.
Defesa 2: um reverse proxy que verifica um bearer token
Quando algo na Internet pública precisa 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 via de acesso.
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;
}
}Cinco linhas fazem trabalho efetivo nesse bloco, e cada uma evita uma falha que, de outra forma, encontraria.
if dentro de um bloco location é geralmente uma má ideia no nginx, mas um corpo exatamente igual a return é uma das duas formas que se comportam de modo 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 /. Assim, esses três endpoints são recusados antes de o token ser sequer considerado. Um token válido permite fazer inferência, mas não permite encher o disco.
proxy_set_header Host 127.0.0.1:11434; é importante porque o Ollama inspeciona os cabeçalhos Host e Origin recebidos. Encaminhar diretamente o nome de anfitrião público do proxy pode produzir um 403 Forbidden proveniente do Ollama e não 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-o todo de uma vez no fim, fazendo o cliente parecer bloqueado durante toda a geração.
proxy_read_timeout 600s; é importante porque o nginx utiliza 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 imprimir 401. O segundo deve imprimir a lista de modelos. Se o primeiro também devolver a lista de modelos, o bloco map está no âmbito errado. Ele pertence 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. Existe uma diferença de nomenclatura importante: a diretiva chamava-se basicauth antes do Caddy v2.8 e chama-se basic_auth atualmente. Assim, uma configuração copiada de um guia antigo recusa-se a carregar, e o Caddy identifica a diretiva que não reconheceu.
Independentemente do proxy escolhido, esta é uma chave secreta partilhada por todos. Todos os clientes que a tenham têm acesso idêntico, e revogá-la 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, usa a mesma API compatível com OpenAI, emite uma chave separada para cada cliente e regista o consumo de cada chave. Um gateway LiteLLM autoalojado é a opção habitual. Além do controlo de acesso, acrescenta orçamentos 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 toda a Internet é apenas uma decoração, porque os clientes podem simplesmente contorná-lo.
A armadilha do firewall: uma porta de contentor publicada ignora o UFW
É por isso que existem instâncias expostas em servidores cujos administradores configuraram corretamente um firewall.
O 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 se dirige. Quando a decisão de encaminhamento ocorre, o destino já foi alterado para o endereço do contentor. Por isso, o pacote é encaminhado em vez de ser entregue localmente e atravessa FORWARD, não INPUT. As regras INPUT do UFW nunca são consultadas. O pacote contorna o firewall em vez de passar por ele.
É 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 o firewall está ativo com uma política padrão de negação. As duas leituras estão corretas ao mesmo tempo. É exatamente por isso que muitas pessoas confiam na leitura errada. Pode ver a regra responsável:
sudo iptables -t nat -L DOCKER -nA correção consiste em especificar 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 abreviatura de -p 0.0.0.0:11434:11434. Especificar 127.0.0.1 associa o lado do host do mapeamento à interface de loopback. Assim, o seu túnel SSH e o seu reverse proxy continuam a conseguir aceder à porta, 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 mostrar 11434/tcp -> 127.0.0.1:11434. Se mostrar 0.0.0.0:11434, o serviço continua exposto. Depois de compreender este mecanismo, pode aplicá-lo a todos os contentores que publicar: por que as portas publicadas pelo Docker ignoram o UFW explica a cadeia DOCKER-USER e as regras que permanecem depois de um reinício do Docker. Se ainda estiver a configurar a política do próprio host, as regras UFW necessárias num VPS novo explica a base em que esta configuração assenta.
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 depois 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 é a situação:
ps -o user= -C ollamaO resultado deve ser ollama. Qualquer outro resultado significa que existe um processo iniciado manualmente a ser executado em paralelo com a unidade ou em vez dela. O mesmo princípio aplica-se a todos os daemons que adicionar posteriormente, e executar serviços com contas sem privilégios ajuda a aplicar esta configuração corretamente.
Como verificar se o endpoint da API do Ollama é seguro
Independentemente da opção escolhida, um teste confirma o resultado. 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 atingir o tempo limite ou ser recusados. Se configurou um proxy, os mesmos dois caminhos no hostname do proxy devem devolver 401 sem credenciais e JSON válido com elas.
Em seguida, leia o access log 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"Todas as linhas devem mostrar 127.0.0.1 uma vez que o Ollama esteja vinculado 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 externo, e o timestamp mostra quando ocorreu. O resultado pretendido é não haver qualquer saída desse comando. 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 afirma que não é necessária autenticação para aceder à API. As duas coisas chamadas "chave de API do Ollama" têm outra finalidade. O par Ed25519 em /usr/share/ollama/.ollama/ comprova a identidade da 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, por isso o controlo de acesso tem de ser aplicado pela rede ou por um proxy à frente do serviço.
O OLLAMA_HOST=0.0.0.0 é seguro se eu tiver uma firewall?
Apenas enquanto mais nada escrever regras de firewall nesse sistema. 0.0.0.0 significa que o listener existe efetivamente na interface pública e que está a confiar apenas na firewall para impedir o acesso. 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 à cadeia 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 a um endereço de túnel privado remove o listener da interface pública. Assim, um erro na firewall já não tem nada 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. Se ss mostrar 127.0.0.1:11434 e o curl remoto exceder o tempo limite, esse é o resultado esperado. Se ss mostrar 0.0.0.0:11434 ou *:11434 enquanto o curl remoto devolve JSON, significa que a API completa está acessível. Nunca faça o teste com curl no próprio servidor, porque o loopback responde independentemente do endereço de bind utilizado.
Posso simplesmente mudar a porta de 11434 para uma porta aleatória?
Não, e vale a pena explicar o motivo. Uma porta diferente apenas atrasa uma verificação que teste uma única porta. Os scanners percorrem todo o intervalo, e um pedido a /api/tags identifica o serviço, independentemente da porta em que chegou. Mudar a porta também quebra os valores predefinidos 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 deslocar.
Alguém acedeu ao meu Ollama exposto. 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. Em seguida, 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 tanto utilização de espaço em disco como 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.