Ollama API без пароля: як захистити порт 11434
Ollama не має вбудованої автентифікації: кожен, хто досягне порту 11434, може запускати, завантажувати й видаляти моделі. Ось 3 способи захисту.
В Ollama API немає пароля
В Ollama API немає автентифікації. Сервер, який ви запускаєте, не перевіряє користувача, пароль або криптографічний ключ і не має жодного списку дозволених адрес. Будь-який клієнт, здатний встановити 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 виконує інференс для іншого користувача. Якщо тариф передбачає fair-use ліміт CPU, тривале навантаження означає, що стороння особа витрачає ваш ліміт. Після цього контролювати витрати на 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. Цей ключ потрібен, щоб передавати модель до registry або отримувати приватну модель. Він підтверджує ollama.com, що запит надходить від вашої машини. Для клієнтів, які підключаються до вашої машини, цей ключ нічого не змінює. Його видалення, заміна або відсутність не впливають на те, хто може звертатися до вашого API.
Друге — це OLLAMA_API_KEY. Ця змінна містить ключ, який ви створюєте в https://ollama.com/settings/keys. Клієнт передає його як Authorization: Bearer $OLLAMA_API_KEY під час виклику hosted 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/versioncurl: (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 не прослуховує порт на стороні сервера. У такому разі спочатку перевірте ss на сервері, а вже потім змінюйте команду SSH.
Для кількох клієнтських машин приватна мережа краща за окремий тунель для кожного користувача. Об’єднайте машини у WireGuard або Tailscale, а потім прив’яжіть Ollama до адреси в цій мережі, а не до 0.0.0.0:
[Service]
Environment="OLLAMA_HOST=10.8.0.1:11434"Тоді порт існуватиме лише на інтерфейсі, для підключення до якого потрібен cryptographic key. Це також захищає від помилки у 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, який перевіряє token:
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 /, тому ці три endpoint відхиляються ще до перевірки token. Дійсний token дає доступ до inference, але не дає змоги заповнити диск.
proxy_set_header Host 127.0.0.1:11434; важливий, оскільки Ollama перевіряє вхідні заголовки Host і Origin. Якщо без змін передати публічне ім’я хоста proxy, можна отримати 403 Forbidden, сформований Ollama, а не nginx. Це ускладнює діагностику. OLLAMA_ORIGINS — інший потрібний параметр для browser client, якому потрібно дозволити певний origin.
proxy_buffering off; важливий, оскільки Ollama передає відповідь потоком, token за token. Якщо buffering увімкнено, nginx утримує потік і передає його одним фрагментом наприкінці. Через це client виглядає завислим протягом усього процесу генерації.
proxy_read_timeout 600s; важливий, оскільки стандартне значення nginx становить 60 секунд. Тривала генерація на CPU легко перевищує цей час. Client отримує 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 у чотирьох рядках. Для browser client це зручніше, ніж 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, це один спільний секрет для всіх. Кожен client, який його має, отримує однаковий доступ. Щоб відкликати цей доступ, потрібно змінити конфігурацію та одночасно оновити її в кожного caller.
Захист 3: шлюз, який видає ключі для кожного клієнта
Коли модель викликає більше ніж одна людина або застосунок, спільний токен стає непридатним. Ви не можете визначити, який клієнт створив навантаження, і не можете заблокувати одного клієнта, не заблокувавши всіх. Шлюз розміщується там, де раніше працював проксі, використовує той самий OpenAI-compatible API, видає окремий ключ для кожного клієнта та записує використання кожного ключа. Self-hosted шлюз LiteLLM є типовим рішенням. Додатково до контролю доступу він надає бюджети для окремих ключів і журнали запитів.
Правило із захисту 1 не змінюється. Ollama прив’язаний до 127.0.0.1, лише шлюз взаємодіє з ним, а шлюз є єдиним сервісом із публічним listener. Шлюз на сервері, де порт 11434 досі відкритий для всього світу, є лише формальністю, оскільки клієнти можуть просто обійти його.
Пастка з firewall: опублікований порт контейнера обходить UFW
Саме тому на серверах із відкритими інстансами таке трапляється навіть тоді, коли власники правильно налаштували firewall.
UFW (uncomplicated firewall) записує свої правила в ланцюжок INPUT таблиці ядра filter, а INPUT обробляє пакети, адресовані самому хосту. Прапорець -p у Docker записує правило destination NAT (network address translation) у ланцюжок PREROUTING таблиці nat. Ядро перевіряє це правило до визначення призначення пакета. На момент прийняття рішення про маршрутизацію адресу призначення вже замінено на адресу контейнера. Тому пакет пересилається, а не доставляється локально, і проходить через FORWARD, а не через INPUT. Правила INPUT UFW взагалі не перевіряються. Пакет обходить firewall, а не проходить через нього.
Саме тому ця послідовність залишає порт 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 і далі повідомляє, що firewall активний і для нього встановлено політику default deny. Обидва показники можуть бути правильними одночасно. Саме тому часто довіряють неправильному показнику. Правило, яке це спричинило, можна переглянути так:
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 tunnel і 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 описано базову конфігурацію, на якій це рішення ґрунтується. У Rocky або AlmaLinux немає UFW для налаштування, тому почніть із матеріалу та сама базова політика у firewalld.
Хто запускає процес
Скрипт інсталяції 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 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"Після прив’язування Ollama до loopback у кожному рядку має один раз з’явитися 127.0.0.1, оскільки це єдина адреса, з якої може надійти підключення. Публічна адреса в цьому стовпці означає запит із зовнішньої мережі, а timestamp показує, коли саме він надійшов. Відсутність будь-якого виводу цієї команди — очікуваний результат. Якщо ви вперше налаштовуєте цю частину Ollama, у матеріалі Запуск Ollama на VPS описано встановлення, вибір розміру моделі та обмеження пам’яті, які визначають, що саме вдасться завантажити.
FAQ
Чи має Ollama API key або пароль?
Ні. Запущений вами сервер не має жодної автентифікації, а в офіційній документації зазначено, що для доступу до 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 port для інтернету?
Виконайте 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, тому ви маєте дані про те, хто і для якої моделі надсилав запит, але не про те, що було згенеровано.