Как защитить API Ollama паролем на порту 11434
Сервер Ollama по умолчанию не требует аутентификации, позволяя любому пользователю управлять моделями через порт 11434. Узнайте три способа закрыть доступ и обезопасить сервер.
В API Ollama нет пароля
В API Ollama отсутствует аутентификация. В запущенном сервере нет пользователей, паролей, проверки ключей или списков разрешенных IP-адресов. Любой, кто может установить TCP-соединение с портом 11434, может просматривать список ваших моделей, запускать их, загружать новые и удалять существующие.
Официальная документация прямо заявляет: «При локальном доступе к API Ollama через http://localhost:11434 аутентификация не требуется». Слово локально определяет всю модель безопасности. По умолчанию Ollama привязывается к 127.0.0.1, поэтому на ноутбуке интерфейс обратной петли (loopback) выступает в роли контроля доступа. Если перенести слушающий сокет на публичный адрес, контроль доступа исчезает, так как его ничто не заменяет.
Именно поэтому данный вопрос критичен для VPS (виртуального выделенного сервера). Настройки по умолчанию безопасны. Первое изменение, которое вносит большинство пользователей — открытие доступа для других машин — одновременно отключает все механизмы защиты.
Что раскрывает открытый порт 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, постоянная нагрузка означает, что ваш лимит расходует посторонний человек, а контроль расходов на AI-нагрузки на VPS становится гораздо сложнее, когда вы не единственный пользователь.
/api/pullвыполняет запись на ваш диск. Модели весят от 2 до 40 гигабайт каждая. Цикл загрузок заполняет том, а переполненный диск выводит из строя все остальные сервисы на сервере, а не только Ollama.- Запросы поступают в ваш процесс и логируются. При стандартном уровне логирования Ollama записывает только метаданные: endpoint, статус, задержку и адрес клиента, но не текст промпта. Тем не менее, это запись о том, кто использовал ваш сервер и для чего, которая сохраняется в вашем журнале, хотя вы не планировали её собирать.
/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/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 11434ss теперь должен отображать 127.0.0.1:11434. Если по-прежнему выводится 0.0.0.0, значит, приоритет имеет другой drop-in файл. Выполните systemctl cat ollama.service, чтобы вывести список юнита и всех его 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"В этом случае порт будет существовать только на интерфейсе, доступ к которому требует наличия ключа. Это защищает от ошибок в настройках брандмауэра: даже если правило случайно откроет доступ для всего мира, сервис останется недоступным, так как он не привязан к публичному интерфейсу.
Защита 2: обратный прокси с проверкой bearer-токена
Если к модели необходимо обращаться из публичной сети, оставьте Ollama на loopback и установите перед ней прокси. Прокси выполняет TLS termination (завершение TLS) и отклоняет запросы без корректного заголовка. Ollama по-прежнему принимает соединения только с 127.0.0.1, поэтому прокси остается единственным путем доступа.
Сначала сгенерируйте реальный токен. Не придумывайте его вручную:
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 /, поэтому в доступе к этим трем эндпоинтам будет отказано еще до проверки токена. Таким образом, валидный токен дает право на инференс, а не на заполнение диска.
proxy_set_header Host 127.0.0.1:11434; важен, так как Ollama проверяет входящие заголовки Host и Origin. Передача публичного имени хоста прокси напрямую может привести к тому, что 403 Forbidden будет исходить от Ollama, а не от nginx, что затрудняет отладку. OLLAMA_ORIGINS — еще один рычаг, необходимый для браузерного клиента, которому нужно разрешить конкретный origin.
proxy_buffering off; важен, так как Ollama передает ответ потоком, токен за токеном. При включенном буферизации nginx удерживает поток и отдает его целиком только в конце, из-за чего клиент выглядит зависшим на все время генерации.
proxy_read_timeout 600s; важен, так как по умолчанию в nginx установлено 60 секунд. Длительная генерация на 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 выполняет ту же задачу с помощью базовой аутентификации всего в четырех строках, что лучше подходит для браузерного клиента, чем bearer-токен:
llm.example.com {
basic_auth {
apiuser PASTE_BCRYPT_HASH_HERE
}
reverse_proxy 127.0.0.1:11434
}Запустите caddy hash-password, чтобы создать bcrypt-хэш, который ожидает Caddy. Одна ловушка с именованием: директива называлась basicauth до версии Caddy v2.8, а теперь называется basic_auth, поэтому конфигурация, скопированная из старого руководства, не загрузится, а Caddy укажет на нераспознанную директиву.
Какой бы прокси вы ни выбрали, это будет один общий секрет для всех. Каждый клиент, владеющий им, имеет одинаковый доступ, а отзыв токена означает необходимость редактирования конфигурации и одновременного обновления всех клиентов.
Защита 3: шлюз, выдающий ключи для каждого клиента
Когда модель используют более одного человека или приложения, общий токен быстро исчерпывает лимиты. Вы не можете определить, какой именно клиент создает нагрузку, и не можете ограничить одного из них, не отключив остальных. Шлюз устанавливается там же, где находился прокси, использует тот же API, совместимый с OpenAI, выдает отдельный ключ для каждого клиента и ведет учет использования для каждого ключа. Самостоятельно развернутый шлюз LiteLLM — стандартное решение, которое добавляет управление бюджетами для каждого ключа и логирование запросов поверх контроля доступа.
Правило из защиты 1 остается неизменным. Ollama привязывается к 127.0.0.1, шлюз является единственным процессом, который взаимодействует с ней, и шлюз — единственный сервис с публичным портом. Шлюз на сервере, где порт 11434 остается открытым для всего мира, является лишь декорацией, так как вызывающие стороны могут просто обойти его.
Ловушка межсетевого экрана: опубликованный порт контейнера обходит UFW
Именно поэтому открытые экземпляры сервисов существуют на серверах, владельцы которых правильно настроили межсетевой экран.
UFW (uncomplicated firewall) записывает свои правила в цепочку INPUT таблицы filter ядра, а INPUT обрабатывает пакеты, адресованные самому хосту. Флаг -p в Docker добавляет правило трансляции сетевых адресов (destination NAT) в цепочку PREROUTING таблицы nat, которое ядро проверяет до принятия решения о маршрутизации пакета. К моменту принятия решения о маршрутизации адрес назначения уже изменён на адрес контейнера, поэтому пакет пересылается (forwarded), а не доставляется локально, и проходит через FORWARD вместо INPUT. Правила INPUT в UFW никогда не задействуются, поэтому пакет проходит мимо межсетевого экрана, а не через него.
Вот почему эта последовательность оставляет порт 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-туннель и обратный прокси по-прежнему смогут получить доступ к сервису, а интернет — нет. Пересоздание контейнера здесь безопасно, так как модели хранятся в именованном томе 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 ollamaЮнит в /etc/systemd/system/ollama.service затем устанавливает User=ollama и Group=ollama. Не меняйте эти параметры. Быстрый ollama serve, запущенный вручную в терминале, выполняется от имени того пользователя, под которым вы вошли в систему. Если это root, то неаутентифицированный API будет записывать файлы с правами root. Проверьте, кто является владельцем процесса:
ps -o user= -C ollamaРезультатом должно быть ollama. Любое другое значение означает, что процесс, запущенный вручную, работает параллельно с юнитом или вместо него. Тот же подход применим к любому демону, который вы добавите позже, и запуск служб от имени пользователей с минимальными привилегиями подробно описывает этот процесс.
Как проверить безопасность 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"В каждой строке должно отображаться 127.0.0.1, как только Ollama будет привязана к loopback, поскольку это единственный адрес, с которого может поступить соединение. Публичный адрес в этом столбце означает запрос извне, а метка времени указывает, когда это произошло. Отсутствие вывода этой команды — это именно тот результат, который вам нужен. Если работа с моделями для вас в новинку, запуск 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, если у меня есть межсетевой экран?
Только до тех пор, пока никто другой не вносит правила в межсетевой экран на этом узле. 0.0.0.0 означает, что слушающий сокет действительно существует на публичном интерфейсе, и вы полагаетесь исключительно на межсетевой экран, чтобы сделать его недоступным. Это доверие пропадает в момент, когда Docker публикует порт, так как правило DNAT, которое Docker добавляет в таблицу nat, обрабатывается до того, как пакет достигнет цепочки INPUT, где работает UFW. В результате пакет пересылается, и UFW его не видит. Привязка к 127.0.0.1 или к адресу частного туннеля убирает слушающий сокет с публичного интерфейса, поэтому ошибка в настройках межсетевого экрана не приведет к раскрытию сервиса.
Как проверить, открыт ли мой порт 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 отвечает независимо от того, какой адрес привязки установлен.
Могу ли я просто сменить порт с 11434 на какой-нибудь случайный?
Нет, и причина заслуживает упоминания. Другой порт не замедляет ничего, кроме сканирования одного конкретного порта. Сканеры проходят весь диапазон, и один запрос к /api/tags идентифицирует сервис независимо от того, на какой порт он пришел. Смена порта также нарушает настройки по умолчанию у всех клиентов и усложняет понимание вашей конфигурации в будущем. Вместо этого привяжите сервис к loopback: это уберет слушающий сокет, а не просто переместит его.
Кто-то получил доступ к моему открытому Ollama. Что мне проверить?
Сначала привяжите его к 127.0.0.1 и перезапустите сервис, чтобы прекратить доступ до начала расследования. Затем запустите journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1, чтобы увидеть, какие внешние адреса обращались к каким эндпоинтам и когда. Сравните ollama list с моделями, которые вы планировали использовать, так как /api/pull не требует аутентификации, и модель, которую вы не скачивали, — это и расход дискового пространства, и улика. Проверьте свободное место с помощью df -h. Ollama не записывает текст запросов на стандартном уровне логирования, поэтому у вас будет запись о том, кто и какую модель запрашивал, но не о том, что было сгенерировано.