SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-28

Ollama API beveiligen: zo stelt u authenticatie in

De Ollama API heeft standaard geen wachtwoord op poort 11434. Ontdek hoe u uw server beveiligt tegen ongeautoriseerde toegang met drie concrete stappen voor uw VPS of netwerk.

De Ollama API heeft geen wachtwoord

De Ollama API bevat geen authenticatie. Er is geen gebruiker, geen wachtwoord, geen sleutelcontrole en geen toegestane lijst aanwezig op de server die u draait. Alles wat een TCP-verbinding kan openen naar poort 11434 kan uw modellen weergeven, uitvoeren, nieuwe downloaden en de bestaande modellen verwijderen.

De officiële documentatie stelt dit duidelijk: "Er is geen authenticatie vereist bij het lokaal benaderen van de API van Ollama via http://localhost:11434." Het woord lokaal vormt het volledige beveiligingsmodel. Ollama bindt standaard aan 127.0.0.1, dus op een laptop fungeert de loopback-interface als toegangscontrole. Verplaats die listener naar een publiek adres en de toegangscontrole is verdwenen, omdat er niets voor in de plaats is gekomen.

Dit is waarom dit van belang is op een VPS (virtual private server). De standaardinstelling is veilig. De eerste wijziging die de meeste mensen doorvoeren, het openstellen van de listener zodat een tweede machine het model kan gebruiken, is de wijziging die direct elke bescherming verwijdert.

Wat een open poort 11434 prijsgeeft

Elk endpoint. Er is geen alleen-lezen modus en geen afzonderlijke beheerpoort. Dit zijn de werkelijke verzoeken, gericht aan een serveradres in plaats van 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"}'

In termen van beheer gaan er vier dingen mis:

  • Uw CPU of GPU voert inferentie uit voor iemand anders. Bij een abonnement met een fair-use CPU-limiet wordt uw limiet verbruikt door een vreemde bij aanhoudende belasting, en het beheersbaar houden van AI-werklastkosten op een VPS wordt aanzienlijk lastiger zodra u niet de enige gebruiker bent.
  • /api/pull schrijft naar uw schijf. Modellen variëren in grootte van twee tot veertig gigabyte per stuk. Een lus van pulls vult het volume, en een volle schijf verstoort elke andere service op de server, niet alleen Ollama.
  • Verzoeken komen binnen in uw proces en worden gelogd. Op het standaard logniveau registreert Ollama alleen metadata; u krijgt dus het endpoint, de status, de latentie en het clientadres, niet de prompttekst. Dat is nog steeds een overzicht van wie uw server heeft gebruikt en waarvoor, opgeslagen in uw journal, terwijl u er niet voor heeft gekozen om dit te verzamelen.
  • /api/delete verwijdert modellen. Deze terugkrijgen betekent ze opnieuw downloaden via uw eigen bandbreedte.

Hiervoor is geen exploit nodig. Het is de gedocumenteerde API die precies functioneert zoals ontworpen.

De Ed25519-sleutel is geen toegangscontrole

Zoek naar "Ollama API key" en u komt bij twee verschillende zaken uit. Geen van beide is een wachtwoord voor uw server; het onderscheid hiertussen neemt de meeste verwarring weg.

De eerste is het identiteitssleutelpaar. Ollama genereert bij de eerste uitvoering een Ed25519-sleutelpaar. Op Linux maakt het installatiescript een systeemgebruiker aan genaamd ollama met de thuismap op /usr/share/ollama, dus het paar bevindt zich hier:

/usr/share/ollama/.ollama/id_ed25519
/usr/share/ollama/.ollama/id_ed25519.pub

Die sleutel is naar buiten gericht. ollama signin registreert de publieke helft bij uw ollama.com-account, en dit is wat u autoriseert om een model naar de registry te pushen of een privaat model te pullen. Het bewijst aan ollama.com dat u de eigenaar van de machine bent. Het vraagt niets van de clients die verbinding maken met uw machine. Het verwijderen, roteren of nooit aanmaken ervan verandert niets aan wie uw API mag aanroepen.

De tweede is OLLAMA_API_KEY. Die variabele bevat een sleutel die u aanmaakt op https://ollama.com/settings/keys, en uw client stuurt deze mee als Authorization: Bearer $OLLAMA_API_KEY bij het aanroepen van de gehoste API op https://ollama.com/api. Het is een inloggegeven voor hun dienst, door u gebruikt als client. Uw eigen ollama serve leest deze nooit. Het instellen van OLLAMA_API_KEY op uw VPS plaatst geen wachtwoord op uw VPS.

Er is dus geen instelling om in te schakelen. De drie onderstaande verdedigingslinies werken allemaal op dezelfde manier: zorg dat de poort onbereikbaar is en plaats er iets voor dat wel controleert.

Controleer waarop uw server momenteel luistert

sudo ss -tlnp | grep 11434

Het veilige resultaat benoemt het loopback-adres:

LISTEN 0  4096  127.0.0.1:11434  0.0.0.0:*  users:(("ollama",pid=812,fd=3))

Het blootgestelde resultaat benoemt elke interface:

LISTEN 0  4096  0.0.0.0:11434  0.0.0.0:*  users:(("ollama",pid=812,fd=3))

0.0.0.0 staat voor alle IPv4-adressen op de machine, inclusief het publieke adres. *:11434 en [::]:11434 betekenen hetzelfde, inclusief IPv6.

Bevestig dit nu vanaf een externe locatie. Voer dit uit op uw laptop, niet op de server:

curl -m 5 http://YOUR_SERVER_IP:11434/api/version

curl: (28) Connection timed out after 5001 milliseconds is het gewenste antwoord, evenals curl: (7) Failed to connect ... Connection refused. Een JSON-object met een version-veld betekent dat de volledige API bereikbaar is voor iedereen die erom vraagt. Testen met curl op de server zelf bewijst niets, omdat de loopback-interface altijd antwoordt.

Blootstelling ontstaat meestal op een van de twee volgende manieren. De eerste is een bewuste aanpassing, omdat iemand een tweede machine nodig had om het model te bereiken:

sudo systemctl edit ollama.service
[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"

Die enkele regel is de volledige blootstelling. De tweede manier is Docker, en daarbij hoeft u niets aan te passen. Dat onderwerp heeft hieronder een eigen sectie.

Verdediging 1: houd het op localhost en gebruik een tunnel

Kies hier als eerste voor. Er is geen nieuwe software nodig en er worden geen inloggegevens aangemaakt die kunnen lekken. De poort bestaat niet op een publieke interface, waardoor deze niet vindbaar is via een scan.

Stel het bind-adres expliciet in in plaats van te vertrouwen op de standaardinstelling:

sudo systemctl edit ollama.service
[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"

Dit schrijft /etc/systemd/system/ollama.service.d/override.conf. Pas dit toe en controleer het:

sudo systemctl daemon-reload
sudo systemctl restart ollama
sudo ss -tlnp | grep 11434

ss zou nu 127.0.0.1:11434 moeten tonen. Als er nog steeds 0.0.0.0 staat, overschrijft een tweede drop-in bestand de instelling. Voer systemctl cat ollama.service uit om de unit en elk drop-in bestand met het bijbehorende pad weer te geven, en verwijder vervolgens het verouderde bestand.

Om het model vanaf uw laptop te gebruiken, forwardt u de poort via SSH:

ssh -N -L 11434:127.0.0.1:11434 you@your-server

-L 11434:127.0.0.1:11434 opent poort 11434 op uw laptop en stuurt al het verkeer dat daar aankomt door naar 127.0.0.1:11434, gezien vanaf de server. -N vertelt SSH om geen extern commando uit te voeren, waardoor het proces de tunnel openhoudt. Terwijl dit draait, werkt het volgende op uw laptop:

curl -s http://localhost:11434/api/tags

U zult twee fouten tegenkomen. bind [127.0.0.1]:11434: Address already in use betekent dat uw laptop zijn eigen Ollama op die poort draait; kies daarom een andere lokale poort met -L 11500:127.0.0.1:11434 en richt uw client op 11500. Een leeg antwoord via een tunnel die wel verbinding maakte, betekent dat SSH werkt maar Ollama niet luistert aan de serverkant; controleer daarom ss op de server voordat u het SSH-commando aanpast.

Voor meerdere client-machines is een privaat netwerk beter dan één tunnel per persoon. Plaats de machines in WireGuard of Tailscale en bind Ollama vervolgens aan het adres op dat netwerk in plaats van aan 0.0.0.0:

[Service]
Environment="OLLAMA_HOST=10.8.0.1:11434"

De poort bestaat dan alleen op een interface waarvoor u een sleutel nodig heeft om deel te nemen. Dit is ook veilig bij een fout in de firewall, omdat een regel die per ongeluk toegang verleent aan de hele wereld, nog steeds geen listener kan blootstellen die niet op de publieke interface aanwezig is.

Verdediging 2: een reverse proxy die een bearer token controleert

Wanneer een service op het publieke internet het model moet aanroepen, houd Ollama dan op loopback en plaats er een proxy voor. De proxy beëindigt TLS (transport layer security) en weigert verzoeken zonder de juiste header. Ollama accepteert nog steeds alleen verbindingen vanaf 127.0.0.1, dus de proxy is de enige toegangsweg.

Genereer eerst een echt token. Verzin er zelf geen:

openssl rand -base64 36

Een nginx-site die dit controleert:

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;
    }
}

Vijf regels daar verrichten het eigenlijke werk, en elk daarvan voorkomt een fout die anders zou optreden.

if binnen een location-blok is meestal een slecht idee in nginx, maar een body van exact return is een van de twee vormen die zich voorspelbaar gedragen, dus dit gebruik is veilig.

location = /api/pull is een exacte match, en nginx rangschikt exacte matches boven het location /-prefix, dus die drie endpoints worden geweigerd voordat het token überhaupt wordt overwogen. Een geldig token geeft vervolgens toegang tot inferentie, niet tot de mogelijkheid om uw schijf vol te schrijven.

proxy_set_header Host 127.0.0.1:11434; is van belang omdat Ollama de inkomende Host- en Origin-headers inspecteert. Het direct doorgeven van de publieke hostnaam van de proxy kan resulteren in een 403 Forbidden die afkomstig is van Ollama in plaats van nginx, wat verwarrend is bij het debuggen. OLLAMA_ORIGINS is de andere hendel, voor een browserclient die een specifieke origin moet toestaan.

proxy_buffering off; is van belang omdat Ollama zijn antwoord token voor token streamt. Met buffering ingeschakeld houdt nginx de stream vast en levert deze pas aan het einde in één keer af, waardoor uw client de hele generatie lang bevroren lijkt.

proxy_read_timeout 600s; is van belang omdat nginx standaard op 60 seconden staat. Een lange generatie op de CPU overschrijdt dat gemakkelijk, de client krijgt 504 Gateway Time-out, en /var/log/nginx/error.log registreert upstream timed out (110: Connection timed out) while reading response header from upstream. Het verzoek was nog steeds bezig; nginx heeft het opgegeven.

Herlaad en test beide paden:

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

De eerste zou 401 moeten afdrukken. De tweede zou uw modellijst moeten afdrukken. Als de eerste ook de modellijst teruggeeft, staat het map-blok in de verkeerde scope. Het hoort op http-niveau, dus plaats het in een bestand onder /etc/nginx/conf.d/ of boven het server-blok, nooit binnen server.

Caddy doet hetzelfde werk met basisauthenticatie in vier regels, wat beter past bij een browserclient dan een bearer token:

llm.example.com {
	basic_auth {
		apiuser PASTE_BCRYPT_HASH_HERE
	}
	reverse_proxy 127.0.0.1:11434
}

Voer caddy hash-password uit om de bcrypt-hash te produceren die het verwacht. Eén valkuil bij de naamgeving: de richtlijn was basicauth vóór Caddy v2.8 en is nu basic_auth, dus een configuratie die uit een oudere handleiding is gekopieerd, weigert te laden en Caddy benoemt de richtlijn die het niet herkende.

Welke proxy u ook kiest, dit is één gedeeld geheim voor iedereen. Elke client die dit bezit heeft identieke toegang, en het intrekken ervan betekent de configuratie bewerken en elke aanroeper tegelijkertijd bijwerken.

Verdediging 3: een gateway die per client sleutels uitgeeft

Zodra meer dan één persoon of applicatie het model aanroept, schiet een gedeelde token tekort. U kunt niet achterhalen welke client de belasting veroorzaakte en u kunt er niet één afsluiten zonder ze allemaal af te sluiten. Een gateway bevindt zich op de plek waar de proxy stond, spreekt dezelfde OpenAI-compatibele API, geeft per client een aparte sleutel uit en registreert het verbruik per sleutel. Een zelfgehoste LiteLLM gateway is de gebruikelijke oplossing; deze voegt budgetten per sleutel en verzoeklogs toe bovenop de toegangscontrole.

De regel uit verdediging 1 blijft ongewijzigd. Ollama bindt aan 127.0.0.1, de gateway is het enige proces dat ermee communiceert en de gateway is de enige service met een publieke listener. Een gateway op een machine waar poort 11434 nog steeds openstaat voor de wereld is slechts decoratie, omdat aanroepers deze eenvoudig kunnen omzeilen.

De firewall-valstrik: een gepubliceerde containerpoort omzeilt UFW

Dit is de reden waarom er blootgestelde instanties bestaan op servers waarvan de eigenaren de firewall correct hebben geconfigureerd.

UFW (uncomplicated firewall) schrijft zijn regels naar de INPUT-chain van de filter-tabel van de kernel, en INPUT verwerkt pakketten die aan de host zelf zijn gericht. De -p-vlag van Docker schrijft een destination NAT (network address translation)-regel naar de PREROUTING-chain van de nat-tabel, die de kernel evalueert voordat wordt besloten waar het pakket naartoe gaat. Tegen de tijd dat de routeringsbeslissing wordt genomen, is de bestemming al herschreven naar het adres van de container, waardoor het pakket wordt doorgestuurd in plaats van lokaal afgeleverd en het FORWARD doorkruist in plaats van INPUT. De INPUT-regels van UFW worden nooit geraadpleegd, dus het pakket gaat om de firewall heen in plaats van erdoorheen.

Dat is de reden waarom deze reeks poort 11434 open laat staan voor het internet:

sudo ufw default deny incoming
sudo ufw enable
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama

en sudo ufw status nog steeds rapporteert dat de firewall actief is met een standaard weigering (default deny). Beide lezingen zijn tegelijkertijd correct, en dat is precies waarom mensen de verkeerde vertrouwen. U kunt de regel zien die dit heeft veroorzaakt:

sudo iptables -t nat -L DOCKER -n

De oplossing is één adres in de publish-vlag:

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 is een afkorting voor -p 0.0.0.0:11434:11434. Door 127.0.0.1 op te geven, wordt de host-zijde van de mapping gebonden aan de loopback, zodat uw SSH-tunnel en uw reverse proxy deze nog steeds kunnen bereiken, maar het internet niet. Het opnieuw aanmaken van de container is hier veilig omdat de modellen in de benoemde ollama-volume leven, niet in de container zelf.

Bevestig dat beide weergaven overeenstemmen:

docker port ollama
sudo ss -tlnp | grep 11434

docker port ollama zou 11434/tcp -> 127.0.0.1:11434 moeten afdrukken. Als het 0.0.0.0:11434 afdrukt, bent u nog steeds blootgesteld. Begrijp het mechanisme één keer en het is van toepassing op elke container die u ooit publiceert: waarom Docker gepubliceerde poorten UFW omzeilen behandelt de DOCKER-USER-chain en de regels die een Docker-herstart overleven. Als u nog steeds het hostbeleid zelf aan het opbouwen bent, behandelt de UFW-regels die een nieuwe VPS nodig heeft de basis waarop dit rust. Op Rocky of AlmaLinux is er geen UFW om te configureren, dus hetzelfde basisbeleid geschreven in firewalld is waar u in plaats daarvan moet beginnen.

Onder welke gebruiker draait het proces

Het installatiescript voor Linux maakt een toegewezen account aan en voert de service daaronder uit:

useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama

De unit op /etc/systemd/system/ollama.service stelt vervolgens User=ollama en Group=ollama in. Wijzig dit niet. Een snelle ollama serve die handmatig in een terminal wordt gestart, draait onder de gebruiker waarmee u bent ingelogd. Als dit root is, schrijft een niet-geauthenticeerde API bestanden als root. Controleer welke gebruiker actief is:

ps -o user= -C ollama

Het antwoord zou ollama moeten zijn. Iets anders betekent dat er een handmatig gestart proces naast of in plaats van de unit draait. Dezelfde logica is van toepassing op elke daemon die u later toevoegt, en services draaien met minimale rechten behandelt dit op de juiste wijze.

Controleren of het Ollama API-eindpunt veilig is

Wat u ook kiest, één test geeft uitsluitsel en deze moet vanaf een andere machine worden uitgevoerd:

curl -m 5 http://YOUR_SERVER_IP:11434/api/version
curl -m 5 http://YOUR_SERVER_IP:11434/api/tags

Beide zouden een time-out moeten geven of geweigerd moeten worden. Als u een proxy heeft gebouwd, zouden dezelfde twee paden op de hostnaam van de proxy 401 moeten retourneren zonder inloggegevens, en echte JSON met inloggegevens.

Lees daarna eenmaal het toegangslogboek, omdat dit aangeeft of iemand de poort heeft gevonden terwijl deze openstond:

journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1

Ollama schrijft één regel per verzoek en bevat het adres van de client:

[GIN] 2026/08/12 - 14:01:10 | 200 | 103.965898ms | 127.0.0.1 | POST "/api/generate"

Elke regel zou 127.0.0.1 moeten tonen zodra Ollama is gebonden aan loopback, omdat dat het enige adres is waarvan een verbinding kan binnenkomen. Een publiek adres in die kolom is een verzoek van buitenaf, en de tijdstempel vertelt u wanneer dit gebeurde. Geen enkele output van dat commando is het resultaat dat u wilt zien. Als de modelkant van dit verhaal nieuw voor u is, behandelt Ollama draaien op een VPS de installatie, de modelgrootte en de geheugenlimieten die bepalen wat er daadwerkelijk wordt geladen.

FAQ

Heeft Ollama een API-key of een wachtwoord?

Nee. De server die u draait, heeft geen enkele vorm van authenticatie en de officiële documentatie stelt dat er geen authenticatie vereist is om de API te benaderen. Beide zaken die "Ollama API-key" worden genoemd, werken de andere kant op. Het Ed25519-paar in /usr/share/ollama/.ollama/ bewijst aan ollama.com dat uw machine de juiste is, zodat u modellen kunt pushen en privémodellen kunt pullen. OLLAMA_API_KEY is een inloggegeven dat uw client naar de gehoste API op https://ollama.com/api stuurt. Uw eigen ollama serve leest geen van beide, dus toegangscontrole moet afkomstig zijn van het netwerk of van een proxy die ervoor staat.

Is OLLAMA_HOST=0.0.0.0 veilig als ik een firewall heb?

Alleen zolang niets anders op die machine firewallregels schrijft. 0.0.0.0 betekent dat de listener daadwerkelijk bestaat op de publieke interface, en u vertrouwt er enkel op dat de firewall deze onbereikbaar houdt. Dat vertrouwen vervalt op het moment dat Docker een poort publiceert, omdat de DNAT-regel die Docker toevoegt aan de nat-tabel wordt geëvalueerd voordat het pakket de INPUT-chain bereikt waar UFW actief is. Het pakket wordt dus doorgestuurd en UFW ziet het nooit. Binden aan 127.0.0.1 of aan een privétunneladres verwijdert de listener van de publieke interface, waardoor een fout in de firewall niets meer blootstelt.

Hoe controleer ik of mijn Ollama-poort openstaat voor het internet?

Voer sudo ss -tlnp | grep 11434 uit op de server en curl -m 5 http://YOUR_SERVER_IP:11434/api/version vanaf een andere machine. ss dat 127.0.0.1:11434 toont en een remote curl die een timeout geeft, is de combinatie van antwoorden die u wilt zien. ss dat 0.0.0.0:11434 of *:11434 toont terwijl de remote curl JSON teruggeeft, betekent dat de volledige API bereikbaar is. Test nooit met curl op de server zelf, omdat loopback antwoordt ongeacht wat het bind-adres is.

Kan ik de poort gewoon verplaatsen van 11434 naar iets willekeurigs?

Nee, en de reden daarvoor is belangrijk. Een andere poort vertraagt niets, behalve een scan van één enkele poort. Scanners doorlopen het hele bereik, en één verzoek aan /api/tags identificeert de service, ongeacht op welke poort het verzoek binnenkwam. Het verplaatsen van de poort verbreekt bovendien alle standaardinstellingen van clients en maakt uw eigen configuratie later lastiger te doorgronden. Bind in plaats daarvan aan loopback; dit verwijdert de listener in plaats van deze te verplaatsen.

Iemand heeft mijn openstaande Ollama bereikt. Wat moet ik controleren?

Bind de service aan 127.0.0.1 en herstart deze eerst, zodat de blootstelling stopt voordat u begint met onderzoeken. Voer daarna journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1 uit om te zien welke externe adressen welke endpoints hebben aangeroepen en wanneer. Vergelijk ollama list met de modellen die u bedoeld had te hebben, aangezien /api/pull niet geauthenticeerd is en een model dat u niet zelf heeft gepulld zowel schijfgebruik als bewijslast is. Controleer de vrije schijfruimte met df -h. Ollama registreert geen prompttekst op het standaard logniveau; u heeft dus een overzicht van wie er vroeg en voor welk model, maar niet van wat er werd gegenereerd.