Ollama API без пароля: як захистити порт 11434
Ollama не має вбудованої автентифікації: кожен, хто має доступ до порту 11434, може запускати, завантажувати й видаляти моделі. Ось 3 способи захисту.
В API Ollama немає пароля
В API Ollama немає автентифікації. На сервері, який ви запускаєте, немає ні користувача, ні пароля, ні перевірки ключа, ні allowlist. Будь-який клієнт, який може відкрити TCP-з’єднання з портом 11434, може переглядати список ваших моделей, запускати їх, завантажувати нові та видаляти наявні.
В офіційній документації це зазначено прямо: «Для доступу до API Ollama локально через http://localhost:11434 автентифікація не потрібна». Увесь захисний механізм визначає слово локально. За замовчуванням Ollama прив’язується до 127.0.0.1, тому на ноутбуці інтерфейс loopback виконує роль контролю доступу. Якщо перенести цей listener на публічну адресу, контроль доступу зникне, оскільки його нічим не замінено.
Саме тому це важливо для VPS (virtual private server). Типова конфігурація безпечна. Перша зміна, яку зазвичай роблять користувачі, — відкривають listener, щоб друга машина могла використовувати модель. Водночас ця зміна одразу усуває всі механізми захисту.
Що видає відкритий порт 11434
Доступний кожен endpoint. Режиму лише для читання та окремого адміністративного порту немає. Ось фактичні запити, спрямовані на адресу сервера, а не на 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"}'З погляду оператора виникають чотири проблеми:
- Ваш CPU або GPU виконує інференс для сторонньої особи. У тарифі з обмеженням CPU за політикою fair use тривале навантаження означає, що ваш ліміт витрачає незнайома людина. А контролювати витрати на AI-навантаження на VPS стає значно складніше, якщо виклики надходять не лише від вас.
/api/pullзаписує дані на ваш диск. Кожна модель займає від двох до сорока гігабайтів. Цикл завантажень заповнює том, а переповнений диск виводить з ладу всі інші сервіси на сервері, а не лише Ollama.- Запити надходять усередину вашого процесу та записуються в журнал. За замовчуванням Ollama реєструє лише метадані: endpoint, статус, затримку та адресу клієнта, але не текст prompt. Проте це все одно запис про те, хто використовував ваш сервер і для чого. Він зберігається у вашому журналі, хоча ви не вирішували його збирати.
/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. Клієнт надсилає його як Authorization: Bearer $OLLAMA_API_KEY під час виклику розміщеного API за адресою https://ollama.com/api. Це облікові дані для їхнього сервісу, які ви використовуєте як клієнт. Ваш власний ollama serve ніколи його не читає. Встановлення OLLAMA_API_KEY на вашому VPS не встановлює пароль для VPS.
Отже, вмикати жоден параметр не потрібно. Усі три наведені нижче засоби захисту працюють однаково: зробіть порт недоступним і розмістіть перед ним компонент, який перевіряє доступ.
Перевірте, на яких адресах сервер зараз прослуховує з’єднання
sudo ss -tlnp | grep 11434Безпечний результат містить loopback-адресу:
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/versionПотрібний результат — curl: (28) Connection timed out after 5001 milliseconds, так само як і curl: (7) Failed to connect ... Connection refused. JSON-об’єкт із полем version означає, що весь API доступний будь-кому, хто надішле запит. Перевірка через curl безпосередньо на сервері нічого не доводить, оскільки loopback завжди відповідає.
Доступ із зовнішньої мережі зазвичай з’являється одним із двох способів. Перший — навмисна зміна конфігурації, оскільки іншому комп’ютеру потрібно було підключатися до моделі:
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, щоб переглянути unit і всі 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 не слухає на стороні сервера. Перш ніж змінювати команду SSH, перевірте там ss.
Для кількох клієнтських машин приватна мережа зручніша, ніж окремий тунель для кожного користувача. Об’єднайте машини через WireGuard або Tailscale, а потім прив’яжіть Ollama до адреси в цій мережі замість 0.0.0.0:
[Service]
Environment="OLLAMA_HOST=10.8.0.1:11434"Тоді порт існуватиме лише на інтерфейсі, для підключення до якого потрібен криптографічний ключ. Це також захищає від помилки у firewall, оскільки правило, яке випадково дозволяє доступ усьому світу, все одно не зможе відкрити listener, якого немає на публічному інтерфейсі.
Захист 2: reverse proxy, який перевіряє bearer token
Якщо до моделі має звертатися щось із публічного інтернету, залиште Ollama доступним лише через loopback і розмістіть перед ним proxy. Proxy завершує TLS (transport layer security) і відхиляє запити без правильного заголовка. Ollama і далі приймає з’єднання лише з 127.0.0.1, тому proxy є єдиним шляхом доступу.
Спочатку згенеруйте справжній token. Не створюйте його вручну:
openssl rand -base64 36Конфігурація сайту 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;
}
}П’ять рядків виконують основну роботу. Кожен із них запобігає окремій проблемі, з якою ви інакше зіткнулися б.
if усередині блоку location зазвичай є невдалою ідеєю в nginx. Але тіло, що містить рівно return, є однією з двох форм, які поводяться передбачувано, тому тут таке використання безпечне.
location = /api/pull задає точний збіг. nginx надає точним збігам вищий пріоритет, ніж префіксу location /, тому ці три кінцеві точки відхиляються ще до перевірки token. Валідний token дає доступ до inference, але не дає змоги заповнити диск.
proxy_set_header Host 127.0.0.1:11434; важливий, оскільки Ollama перевіряє вхідні заголовки Host і Origin. Якщо безпосередньо передати публічне ім’я хоста proxy, можна отримати 403 Forbidden, сформований Ollama, а не nginx. Це ускладнює діагностику. OLLAMA_ORIGINS є іншим параметром для клієнта в браузері, якому потрібно дозволити певний origin.
proxy_buffering off; важливий, оскільки Ollama передає відповідь потоково, token за token. Якщо buffering увімкнено, nginx утримує потік і передає його одним фрагментом після завершення. Через це клієнт виглядає завислим протягом усього generation.
proxy_read_timeout 600s; важливий, оскільки стандартне значення nginx становить 60 секунд. Тривала generation на 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 hash, який він очікує. Є одна проблема з назвою: до Caddy v2.8 директива називалася basicauth, а тепер називається basic_auth. Тому конфігурація зі старого посібника не завантажиться, і Caddy повідомить назву директиви, яку не розпізнав.
Незалежно від вибраного proxy, це один спільний секрет для всіх. Кожен клієнт, який його має, отримує однаковий доступ. Відкликання цього доступу потребує редагування конфігурації та одночасного оновлення всіх клієнтів.
Захист 3: шлюз, який видає ключі для кожного клієнта
Коли модель викликають більше ніж одна людина або застосунок, спільний токен стає непридатним. Неможливо визначити, який клієнт створив навантаження, і неможливо заблокувати одного клієнта, не заблокувавши всіх. Шлюз розміщується там, де раніше був проксі, використовує той самий OpenAI-compatible API, видає окремий ключ для кожного клієнта та реєструє використання кожного ключа. Self-hosted шлюз LiteLLM є типовим рішенням. Він також додає бюджети для окремих ключів і журнали запитів на додаток до контролю доступу.
Правило із захисту 1 не змінюється. Ollama прив’язаний до 127.0.0.1, лише шлюз взаємодіє з ним, і лише шлюз є сервісом із публічним listener. Шлюз на сервері, де порт 11434 досі відкритий для всього світу, не забезпечує захисту, оскільки клієнти можуть просто обійти його.
Пастка брандмауера: опублікований порт контейнера обходить UFW
Саме тому на серверах із відкритими інстансами може бути правильно налаштований брандмауер.
UFW (uncomplicated firewall) записує свої правила в ланцюжок INPUT таблиці filter ядра, а INPUT обробляє пакети, адресовані безпосередньо хосту. Прапорець -p у Docker записує правило destination NAT (network address translation) у ланцюжок PREROUTING таблиці nat, яку ядро перевіряє до визначення маршруту пакета. На момент прийняття рішення про маршрутизацію адресу призначення вже змінено на адресу контейнера, тому пакет пересилається, а не доставляється локально, і проходить через 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 прив’язує сторону хоста в цьому відображенні до loopback, тому SSH-тунель і reverse proxy все ще можуть підключатися до нього, а інтернет — ні. Пересоздання контейнера тут безпечне, оскільки моделі зберігаються в іменованому томі 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. Якщо ви ще налаштовуєте саму політику хоста, у матеріалі правила UFW, потрібні новому VPS описано базову конфігурацію, на якій це ґрунтується.
Від імені якого користувача запущено процес
Скрипт інсталяції Linux створює окремий обліковий запис і запускає службу від його імені:
useradd -r -s /bin/false -U -m -d /usr/share/ollama ollamaUnit у /etc/systemd/system/ollama.service встановлює User=ollama і Group=ollama. Не змінюйте ці параметри. Швидкий ollama serve, запущений вручну в терміналі, працює від імені користувача, під яким ви ввійшли в систему. Якщо це root, неавтентифікований API записує файли від імені root. Перевірте, який саме процес запущено:
ps -o user= -C ollamaРезультат має бути ollama. Будь-яке інше значення означає, що процес, запущений вручну, працює паралельно з unit або замість нього. Такий самий підхід застосовується до кожного daemon, який ви додасте пізніше, а запуск служб від імені користувачів із мінімальними привілеями дає змогу правильно це налаштувати.
Як перевірити безпеку API endpoint Ollama
Незалежно від вибраного варіанта, один тест дає остаточну відповідь. Його потрібно виконати з іншої машини:
curl -m 5 http://YOUR_SERVER_IP:11434/api/version
curl -m 5 http://YOUR_SERVER_IP:11434/api/tagsОбидва запити мають завершитися тайм-аутом або отримати відмову в підключенні. Якщо ви налаштували проксі, ті самі два шляхи на імені хоста проксі мають повертати 401 без облікових даних і коректний JSON із ними.
Після цього один раз перегляньте access log. Він покаже, чи встиг хтось виявити порт, поки той був відкритий:
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"У кожному рядку має бути 127.0.0.1, якщо Ollama слухає лише loopback, оскільки це єдина адреса, з якої може надійти підключення. Публічна адреса в цьому стовпці означає запит ззовні, а timestamp показує, коли його виконано. Відсутність будь-якого виводу цієї команди — очікуваний результат. Якщо ця частина роботи з моделями для вас нова, у матеріалі Запуск Ollama на VPS описано встановлення, вибір розміру моделі та обмеження пам’яті, які визначають, що фактично завантажиться.
FAQ
Чи має Ollama API-ключ або пароль?
Ні. Запущений вами сервер не має жодної автентифікації, а в офіційній документації зазначено, що для доступу до API автентифікація не потрібна. Обидва об’єкти, які називають «Ollama API key», призначені для іншого. Пара Ed25519 у /usr/share/ollama/.ollama/ підтверджує вашу машину для ollama.com, щоб ви могли надсилати моделі та отримувати приватні моделі. OLLAMA_API_KEY — це облікові дані, які ваш клієнт надсилає до розміщеного API за адресою https://ollama.com/api. Ваш власний ollama serve не читає ні те, ні інше, тому контроль доступу має забезпечуватися мережею або проксі перед сервером.
Чи безпечно використовувати OLLAMA_HOST=0.0.0.0, якщо в мене є firewall?
Лише доки ніщо інше не змінює правила firewall на цьому сервері. 0.0.0.0 означає, що listener справді існує на публічному інтерфейсі, а ви покладаєтеся лише на firewall, який має блокувати до нього доступ. Ця довіра порушується, щойно Docker публікує порт, оскільки правило DNAT, яке Docker додає до таблиці nat, обробляється до того, як пакет досягне ланцюжка INPUT, де працює UFW. Тому пакет пересилається, а UFW його не бачить. Прив’язка до 127.0.0.1 або до адреси приватного тунелю прибирає listener із публічного інтерфейсу, тож помилка у firewall уже нічого не зможе відкрити.
Як перевірити, чи відкритий мій порт 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 на самому сервері, оскільки loopback відповість незалежно від того, яку bind-адресу налаштовано.
Чи можна просто змінити порт з 11434 на випадковий?
Ні, і причину важливо пояснити. Інший порт уповільнює лише сканування одного конкретного порту. Сканери перевіряють увесь діапазон, а один запит до /api/tags ідентифікує сервіс незалежно від порту, на який він надійшов. Зміна порту також порушує стандартні налаштування всіх клієнтів і згодом ускладнює розуміння власної конфігурації. Натомість прив’яжіть сервіс до loopback. Це прибирає listener, а не просто переміщує його.
Хтось отримав доступ до мого відкритого Ollama. Що перевірити?
Спочатку прив’яжіть його до 127.0.0.1 і перезапустіть сервіс, щоб припинити доступ до нього до початку розслідування. Потім виконайте journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1, щоб побачити, які зовнішні адреси, до яких endpoint і коли зверталися. Порівняйте ollama list із моделями, які ви планували мати, оскільки /api/pull не потребує автентифікації, а модель, яку ви не отримували, одночасно займає місце на диску та є доказом стороннього доступу. Перевірте вільне місце за допомогою df -h. Ollama не записує текст prompt на рівні журналювання за замовчуванням. Тому у вас є дані про те, хто і для якої моделі надсилав запит, але немає даних про те, що саме було згенеровано.