Ollama senza password: come proteggere l'API
L'API di Ollama non ha autenticazione: chi raggiunge la porta 11434 puo eseguire, scaricare ed eliminare modelli. Ecco 3 correzioni, in ordine.
L'API di Ollama non ha una password
L'API di Ollama non dispone di autenticazione. Nel server in esecuzione non esistono utenti, password, verifiche delle chiavi o allowlist. Qualsiasi dispositivo in grado di aprire una connessione TCP alla porta 11434 può elencare i modelli, eseguirli, scaricarne di nuovi ed eliminare quelli disponibili.
La documentazione ufficiale lo afferma chiaramente: «Per accedere localmente all'API di Ollama non è richiesta alcuna autenticazione tramite http://localhost:11434». La parola localmente definisce l'intero modello di sicurezza. Per impostazione predefinita, Ollama si associa a 127.0.0.1, quindi su un laptop l'interfaccia loopback funge da controllo degli accessi. Se si sposta il listener su un indirizzo pubblico, il controllo degli accessi scompare perché nulla lo sostituisce.
Per questo il problema è rilevante su un VPS (virtual private server). La configurazione predefinita è sicura. La prima modifica eseguita dalla maggior parte degli utenti, cioè aprire il listener affinché una seconda macchina possa usare il modello, rimuove tutte le protezioni in una sola volta.
Cosa rivela una porta 11434 aperta
Ogni endpoint. Non esiste una modalità di sola lettura né una porta amministrativa separata. Queste sono le richieste reali, indirizzate all'indirizzo del server invece che a 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"}'Dal punto di vista operativo, si verificano quattro problemi:
- La CPU o la GPU esegue inferenze per conto di altri. Con un piano che prevede una disponibilità CPU a consumo equo, un carico prolungato consuma la quota da parte di uno sconosciuto e tenere sotto controllo i costi dei carichi di lavoro AI su un VPS diventa molto più difficile quando non sei l'unico chiamante.
/api/pullscrive sul disco. I modelli occupano da due a quaranta gigabyte ciascuno. Un ciclo di download riempie il volume e un disco pieno interrompe tutti gli altri servizi sul server, non solo Ollama.- Le richieste arrivano nel processo e vengono registrate nei log. Al livello di log predefinito, Ollama registra solo i metadati: endpoint, stato, latenza e indirizzo del client, non il testo del prompt. È comunque una registrazione di chi ha usato il server e per quale scopo, memorizzata nel journal, senza che tu abbia scelto di raccoglierla.
/api/deleterimuove i modelli. Per recuperarli devi scaricarli di nuovo utilizzando la tua larghezza di banda.
Nulla di tutto questo richiede un exploit. È l'API documentata che si comporta esattamente come previsto.
La chiave Ed25519 non è un controllo degli accessi
Cercando "Ollama API key" si incontrano due elementi diversi. Nessuno dei due è la password del server. Distinguerli elimina gran parte della confusione.
Il primo è la coppia di chiavi di identità. Ollama genera una coppia di chiavi Ed25519 al primo avvio. Su Linux, lo script di installazione crea un utente di sistema denominato ollama, con la directory home in /usr/share/ollama. La coppia si trova quindi qui:
/usr/share/ollama/.ollama/id_ed25519
/usr/share/ollama/.ollama/id_ed25519.pubQuesta chiave serve per le connessioni in uscita. ollama signin registra la parte pubblica nel tuo account ollama.com. La chiave autorizza il push di un modello nel registro o il pull di un modello privato. Dimostra l'identità della macchina a ollama.com. Non richiede alcuna credenziale ai client che si connettono alla macchina. Eliminarla, sostituirla o non crearla non modifica in alcun modo chi può chiamare la tua API.
Il secondo è OLLAMA_API_KEY. Questa variabile contiene una chiave che crei in https://ollama.com/settings/keys. Il client la invia come Authorization: Bearer $OLLAMA_API_KEY quando chiama l'API ospitata in https://ollama.com/api. È una credenziale per il loro servizio, utilizzata da te in qualità di client. Il tuo ollama serve non la legge mai. Impostare OLLAMA_API_KEY sul tuo VPS non aggiunge una password al VPS.
Non esiste quindi un'impostazione da attivare. Le tre difese riportate di seguito funzionano allo stesso modo: mantieni la porta irraggiungibile e colloca davanti al servizio un componente che esegua effettivamente il controllo.
Verifica su quali indirizzi il server è attualmente in ascolto
sudo ss -tlnp | grep 11434Il risultato sicuro indica l'indirizzo di loopback:
LISTEN 0 4096 127.0.0.1:11434 0.0.0.0:* users:(("ollama",pid=812,fd=3))Il risultato esposto indica tutte le interfacce:
LISTEN 0 4096 0.0.0.0:11434 0.0.0.0:* users:(("ollama",pid=812,fd=3))0.0.0.0 indica tutti gli indirizzi IPv4 del sistema, incluso quello pubblico. *:11434 e [::]:11434 indicano la stessa cosa includendo IPv6.
Ora verifica dall'esterno. Esegui questo comando sul laptop, non sul server:
curl -m 5 http://YOUR_SERVER_IP:11434/api/versioncurl: (28) Connection timed out after 5001 milliseconds è il risultato previsto, così come curl: (7) Failed to connect ... Connection refused. Un oggetto JSON contenente un campo version indica che l'intera API è raggiungibile da chiunque invii una richiesta. Eseguire il test con curl direttamente sul server non dimostra nulla, perché il loopback risponde sempre.
L'esposizione avviene di solito in uno dei due modi seguenti. Il primo consiste in una modifica deliberata, necessaria perché un secondo computer possa raggiungere il modello:
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"Quella singola riga è sufficiente a esporre il servizio. Il secondo metodo è Docker e non richiede alcuna modifica manuale. Questo caso è descritto nella sezione seguente.
Difesa 1: mantenerlo su localhost e accedere tramite tunnel
Usa prima questa soluzione. Non richiede nuovo software e non crea credenziali che possono essere esposte. La porta non è mai presente su un'interfaccia pubblica, quindi una scansione non può rilevarla.
Imposta esplicitamente l'indirizzo di bind invece di affidarti al valore predefinito:
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"Questo scrive /etc/systemd/system/ollama.service.d/override.conf. Applica la modifica e verifica:
sudo systemctl daemon-reload
sudo systemctl restart ollama
sudo ss -tlnp | grep 11434ss dovrebbe ora mostrare 127.0.0.1:11434. Se mostra ancora 0.0.0.0, è attivo un secondo file drop-in. Esegui systemctl cat ollama.service per elencare l'unità e tutti i drop-in con il relativo percorso, quindi elimina quello obsoleto.
Per usare il modello dal laptop, inoltra la porta tramite SSH:
ssh -N -L 11434:127.0.0.1:11434 you@your-server-L 11434:127.0.0.1:11434 apre la porta 11434 sul laptop e invia tutto ciò che vi arriva a 127.0.0.1:11434, visto dal server. -N indica a SSH di non eseguire un comando remoto, quindi il processo mantiene semplicemente aperto il tunnel. Mentre il tunnel è attivo, sul laptop funziona quanto segue:
curl -s http://localhost:11434/api/tagsPotresti riscontrare due errori. bind [127.0.0.1]:11434: Address already in use indica che sul laptop è in esecuzione una propria istanza di Ollama su quella porta; scegli quindi una porta locale diversa con -L 11500:127.0.0.1:11434 e configura il client per usare la porta 11500. Una risposta vuota tramite un tunnel che si è connesso correttamente indica che SSH funziona, ma Ollama non è in ascolto sul lato server. Prima di modificare il comando SSH, verifica ss sul server.
Per più macchine client, una rete privata è preferibile a un tunnel separato per ogni utente. Collega le macchine tramite WireGuard o Tailscale, quindi associa Ollama all'indirizzo usato su quella rete invece che a 0.0.0.0:
[Service]
Environment="OLLAMA_HOST=10.8.0.1:11434"La porta sarà quindi presente solo su un'interfaccia a cui è necessario accedere con una chiave. Questa configurazione resiste anche a un errore del firewall, perché una regola che consente accidentalmente l'accesso a Internet non può esporre un servizio in ascolto che non è presente sull'interfaccia pubblica.
Difesa 2: un reverse proxy che verifica un bearer token
Quando qualcosa su Internet deve chiamare il modello, mantieni Ollama in ascolto sul loopback e inserisci un proxy davanti. Il proxy termina TLS (Transport Layer Security) e rifiuta le richieste prive dell'header corretto. Ollama continua ad accettare connessioni soltanto da 127.0.0.1, quindi il proxy è l'unico percorso di accesso.
Genera prima un token reale. Non inventarne uno manualmente:
openssl rand -base64 36Un sito nginx che lo 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;
}
}Le cinque righe presenti svolgono un lavoro concreto e prevengono altrettanti problemi che si verificherebbero altrimenti.
if all'interno di un blocco location è generalmente una scelta errata in nginx, ma un body esattamente pari a return è una delle due forme che si comportano in modo prevedibile, quindi questo utilizzo è sicuro.
location = /api/pull è una corrispondenza esatta e nginx assegna alle corrispondenze esatte una priorità maggiore rispetto al prefisso location /. Di conseguenza, questi tre endpoint vengono rifiutati prima ancora di verificare il token. Un token valido consente quindi di eseguire inferenze, ma non di riempire il disco.
proxy_set_header Host 127.0.0.1:11434; è importante perché Ollama analizza gli header Host e Origin in ingresso. Inoltrare direttamente il nome host pubblico del proxy può produrre un 403 Forbidden proveniente da Ollama anziché da nginx, rendendo il troubleshooting più difficile. OLLAMA_ORIGINS è l'altra impostazione utile per un client browser che deve consentire un'origine specifica.
proxy_buffering off; è importante perché Ollama trasmette la risposta token per token. Quando il buffering è attivo, nginx trattiene lo stream e lo consegna tutto insieme al termine. Il client sembra quindi bloccato per l'intera durata della generazione.
proxy_read_timeout 600s; è importante perché nginx ha un timeout predefinito di 60 secondi. Una generazione lunga sulla CPU supera facilmente questo limite, il client riceve 504 Gateway Time-out e /var/log/nginx/error.log registra upstream timed out (110: Connection timed out) while reading response header from upstream. La richiesta era ancora in esecuzione. nginx l'ha interrotta.
Ricarica la configurazione e verifica entrambi i percorsi:
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/tagsIl primo comando dovrebbe stampare 401. Il secondo dovrebbe stampare l'elenco dei modelli. Se anche il primo restituisce l'elenco dei modelli, il blocco map si trova nell'ambito errato. Deve appartenere al livello http, quindi inseriscilo in un file sotto /etc/nginx/conf.d/ oppure sopra il blocco server, mai all'interno di server.
Caddy svolge lo stesso compito con l'autenticazione di base in quattro righe, una soluzione più adatta a un client browser rispetto a un bearer token:
llm.example.com {
basic_auth {
apiuser PASTE_BCRYPT_HASH_HERE
}
reverse_proxy 127.0.0.1:11434
}Esegui caddy hash-password per generare l'hash bcrypt richiesto. Attenzione al nome: la direttiva era basicauth prima di Caddy v2.8 e ora è basic_auth. Una configurazione copiata da una guida precedente non viene caricata e Caddy indica il nome della direttiva che non ha riconosciuto.
Qualunque proxy tu scelga, si tratta di un unico secret condiviso da tutti. Ogni client che lo possiede ha lo stesso livello di accesso. Per revocarlo devi modificare la configurazione e aggiornare contemporaneamente ogni chiamante.
Difesa 3: un gateway che rilascia chiavi per client
Quando più persone o applicazioni chiamano il modello, un token condiviso non è più sufficiente. Non puoi sapere quale client ha generato il carico e non puoi bloccarne uno senza bloccarli tutti. Un gateway si colloca dove prima si trovava il proxy, usa la stessa API compatibile con OpenAI, rilascia una chiave separata per ogni client e registra l'utilizzo associato a ciascuna chiave. Un gateway LiteLLM self-hosted è la soluzione più comune e aggiunge budget per chiave e log delle richieste oltre al controllo degli accessi.
La regola della difesa 1 non cambia. Ollama è in ascolto su 127.0.0.1, il gateway è l'unico processo che comunica con Ollama e il gateway è l'unico servizio con un listener pubblico. Un gateway installato su un host in cui la porta 11434 è ancora esposta a Internet è solo un elemento decorativo, perché i client possono semplicemente aggirarlo.
La trappola del firewall: una porta del container pubblicata aggira UFW
Questo spiega perché sui server possono essere presenti istanze esposte anche quando i proprietari hanno configurato correttamente un firewall.
UFW (uncomplicated firewall) scrive le proprie regole nella catena INPUT della tabella filter del kernel, mentre INPUT gestisce i pacchetti destinati direttamente all'host. Il flag -p di Docker scrive una regola di NAT della destinazione (network address translation) nella catena PREROUTING della tabella nat, che il kernel valuta prima di decidere dove instradare il pacchetto. Quando avviene la decisione di routing, la destinazione è già stata riscritta con l'indirizzo del container. Il pacchetto viene quindi inoltrato invece di essere consegnato localmente e attraversa FORWARD anziché INPUT. Le regole INPUT di UFW non vengono mai consultate, quindi il pacchetto aggira il firewall invece di attraversarlo.
Per questo la seguente sequenza lascia la porta 11434 aperta a 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 indicare che il firewall è attivo con una policy predefinita di rifiuto. Le due letture sono corrette contemporaneamente. È proprio questo che porta a fidarsi dell'indicazione sbagliata. Puoi vedere la regola responsabile:
sudo iptables -t nat -L DOCKER -nLa correzione consiste nell'indicare un indirizzo nel flag di pubblicazione:
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 è una forma abbreviata di -p 0.0.0.0:11434:11434. Specificare 127.0.0.1 associa il lato host della mappatura all'interfaccia di loopback. In questo modo il tunnel SSH e il reverse proxy possono ancora raggiungerlo, mentre Internet no. Ricreare il container è sicuro in questo caso, perché i modelli si trovano nel volume denominato ollama, non all'interno del container.
Verifica che le due visualizzazioni concordino:
docker port ollama
sudo ss -tlnp | grep 11434docker port ollama dovrebbe stampare 11434/tcp -> 127.0.0.1:11434. Se stampa 0.0.0.0:11434, il servizio è ancora esposto. Dopo aver compreso una volta il meccanismo, puoi applicarlo a ogni container che pubblichi: perché le porte pubblicate da Docker aggirano UFW illustra la catena DOCKER-USER e le regole che rimangono dopo un riavvio di Docker. Se stai ancora definendo la policy dell'host, le regole UFW necessarie per un nuovo VPS descrive la configurazione di base su cui si fonda. Su Rocky o AlmaLinux non c'è UFW da configurare, quindi la stessa policy di base configurata in firewalld è il punto di partenza corretto.
Con quale account viene eseguito il processo
Lo script di installazione di Linux crea un account dedicato ed esegue il servizio con quell'account:
useradd -r -s /bin/false -U -m -d /usr/share/ollama ollamaL'unità in /etc/systemd/system/ollama.service imposta quindi User=ollama e Group=ollama. Non modificare questa configurazione. Un rapido ollama serve avviato manualmente in un terminale viene eseguito con l'account dell'utente con cui hai effettuato l'accesso. Se si tratta di root, un'API non autenticata scrive file con i privilegi di root. Verifica quale sia il caso:
ps -o user= -C ollamaIl risultato dovrebbe essere ollama. Qualsiasi altro risultato indica che un processo avviato manualmente è in esecuzione insieme all'unità oppure al suo posto. Lo stesso principio si applica a ogni daemon aggiunto in seguito, e eseguire i servizi con account dotati dei privilegi minimi lo applica correttamente.
Come verificare che l’endpoint API di Ollama sia sicuro
Qualunque soluzione abbiate scelto, un test risolve la verifica. Deve essere eseguito da un altro computer:
curl -m 5 http://YOUR_SERVER_IP:11434/api/version
curl -m 5 http://YOUR_SERVER_IP:11434/api/tagsEntrambi i tentativi devono andare in timeout o essere rifiutati. Se avete configurato un proxy, gli stessi due percorsi sul nome host del proxy devono restituire 401 senza credenziali e JSON valido con le credenziali.
Poi consultate una volta il log degli accessi. Indica se qualcuno ha individuato la porta mentre era esposta:
journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1Ollama scrive una riga per ogni richiesta e include l’indirizzo del client:
[GIN] 2026/08/12 - 14:01:10 | 200 | 103.965898ms | 127.0.0.1 | POST "/api/generate"Dopo che Ollama è in ascolto sull’interfaccia di loopback, ogni riga dovrebbe mostrare 127.0.0.1, perché una connessione può arrivare soltanto da quell’indirizzo. Un indirizzo pubblico in quella colonna indica una richiesta dall’esterno. Il timestamp indica quando è stata ricevuta. Il risultato desiderato è che il comando non produca alcun output. Se la parte relativa ai modelli è nuova per voi, eseguire Ollama su un VPS descrive l’installazione, il dimensionamento dei modelli e i limiti di memoria che determinano quali modelli possono essere caricati.
FAQ
Ollama dispone di una chiave API o di una password?
No. Il server eseguito non dispone di alcun tipo di autenticazione e la documentazione ufficiale specifica che non è necessaria alcuna autenticazione per accedere all'API. Entrambe le credenziali chiamate "Ollama API key" hanno invece un altro scopo. La coppia Ed25519 in /usr/share/ollama/.ollama/ dimostra l'identità della macchina a ollama.com, così puoi eseguire il push dei modelli e il pull di quelli privati. OLLAMA_API_KEY è una credenziale che il client invia all'API ospitata in https://ollama.com/api. Il tuo ollama serve non legge nessuna delle due, quindi il controllo degli accessi deve essere gestito dalla rete o da un proxy anteposto al servizio.
OLLAMA_HOST=0.0.0.0 è sicuro se ho un firewall?
Solo finché nessun altro componente scrive regole firewall su quel server. 0.0.0.0 significa che il listener è effettivamente presente sull'interfaccia pubblica e che ti affidi esclusivamente al firewall per impedirne il raggiungimento. Questa fiducia viene meno nel momento in cui Docker pubblica una porta, perché la regola DNAT aggiunta da Docker alla tabella nat viene valutata prima che il pacchetto raggiunga la catena INPUT, dove opera UFW. Il pacchetto viene quindi inoltrato e UFW non lo vede. Il binding a 127.0.0.1 o a un indirizzo di un tunnel privato rimuove il listener dall'interfaccia pubblica, quindi un errore nella configurazione del firewall non lascia nulla da esporre.
Come posso verificare se la porta Ollama è aperta a Internet?
Esegui sudo ss -tlnp | grep 11434 sul server e curl -m 5 http://YOUR_SERVER_IP:11434/api/version da un'altra macchina. Il risultato ss che mostra 127.0.0.1:11434, insieme al timeout di curl remoto, è la combinazione che vuoi ottenere. Se ss mostra 0.0.0.0:11434 o *:11434 mentre curl remoto restituisce JSON, l'API completa è raggiungibile. Non eseguire mai il test con curl sul server stesso, perché il loopback risponde indipendentemente dall'indirizzo di binding configurato.
Posso semplicemente spostare la porta da 11434 a una porta casuale?
No, ed è importante spiegare il motivo. Una porta diversa rallenta soltanto la scansione di quella singola porta. Gli scanner analizzano l'intero intervallo e una richiesta a /api/tags identifica il servizio indipendentemente dalla porta a cui è arrivata. Spostare la porta inoltre rompe le impostazioni predefinite di tutti i client e rende più difficile comprendere la configurazione in seguito. Usa invece il binding al loopback, che rimuove il listener invece di trasferirlo.
Qualcuno ha raggiunto la mia istanza Ollama esposta. Che cosa devo controllare?
Esegui prima il binding a 127.0.0.1 e riavvia il servizio, in modo da interrompere l'esposizione prima di iniziare l'analisi. Esegui quindi journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1 per vedere quali indirizzi esterni hanno chiamato quali endpoint e in quale momento. Confronta ollama list con i modelli che intendevi avere, perché /api/pull non richiede autenticazione e un modello che non hai scaricato rappresenta sia consumo di spazio su disco sia una prova dell'accesso. Controlla lo spazio libero con df -h. Ollama non registra il testo dei prompt al livello di logging predefinito, quindi puoi sapere chi ha effettuato la richiesta e per quale modello, ma non che cosa è stato generato.