База MongoDB сразу после первого запуска дружелюбна до неприличия: подключившись с локальной машины, любой процесс читает, пишет и удаляет что угодно, потому что аутентификация по умолчанию выключена. В изолированной песочнице это удобно, там так и надо. Как только база принимает настоящие данные, дружелюбие превращается в дыру: забытый скрипт, стороннее приложение или открытый наружу порт получают над данными полную власть. Ниже рабочий маршрут по Ubuntu Server: установка из официального репозитория, первый вход, администратор до включения паролей, роли для приложений и аналитика, сеть, закрытая от посторонних.
Почему MongoDB ставят из официального репозитория, а не из архива Ubuntu
Штатные репозитории Ubuntu сервером MongoDB давно не занимаются: в выпуске 24.04 пакета mongodb-server нет вовсе, а в старых релизах он остался в давно устаревших версиях. Проект ведёт собственный репозиторий пакетов и поддерживает оба актуальных выпуска LTS, 22.04 и 24.04. К осени 2026 года стабильной там считается ветка 9.0, свежая точечная сборка 9.0.2, и в комплекте идёт оболочка mongosh версии 2.11.1, сменившая старую консоль mongo ещё в шестой ветке сервера.
Установка начинается с двух утилит и ключа подписи:
sudo apt-get install -y gnupg curl
curl -fsSL https://pgp.mongodb.com/server-9.asc | \
sudo gpg -o /usr/share/keyrings/mongodb-server-9.gpg --dearmor
Строка репозитория пишется в отдельный файл, после чего остаётся обновить индексы и поставить метапакет:
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-9.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/9.0 multiverse main" | sudo tee /etc/apt/sources.list.d/mongodb-org-9.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org
Слово noble в адресе репозитория означает Ubuntu 24.04, для выпуска 22.04 его меняют на jammy. Метапакет mongodb-org притягивает сервер, mongos для шардирования, инструменты резервного копирования и оболочку. Чтобы очередное обновление не притащило новую мажорную ветку, пакеты закрепляют:
echo "mongodb-org hold" | sudo dpkg --set-selections
echo "mongodb-org-database hold" | sudo dpkg --set-selections
echo "mongodb-org-server hold" | sudo dpkg --set-selections
echo "mongodb-mongosh hold" | sudo dpkg --set-selections
Мелкие обновления внутри ветки при этом продолжают ставиться, а мажорный переход потребует осознанного решения и чтения замет о совместимости.
Первый запуск службы mongod и проверка базы через mongosh
После установки сервер регистрируется в systemd и запускается сразу:
sudo systemctl enable --now mongod
sudo systemctl status mongod --no-pager
ss -ltnp | grep 27017
Журнал в /var/log/mongodb/mongod.log должен содержать строку о готовности принимать соединения на порту 27017. Если служба упала на старте, смотреть надо туда же: чаще всего виноват отступ табуляцией в конфигурации или занятый порт. Файл /etc/mongod.conf написан в YAML, где отступы делаются пробелами, а табуляция считается ошибкой. Структура из пакета:
# /etc/mongod.conf
storage:
dbPath: /var/lib/mongodb
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
net:
port: 27017
bindIp: 127.0.0.1
processManagement:
timeZoneInfo: /usr/share/zoneinfo
Данные лежат в /var/lib/mongodb, журнал в /var/log/mongodb. Первый вход и проверка:
mongosh
db.version()
db.adminCommand({ ping: 1 })
show dbs
Команда show dbs выведет пару служебных баз, и это хороший знак: сервер жив, слушает и отвечает.
Для скриптов и регламентных проверок у mongosh есть тихий режим, который печатает только результат:
mongosh --quiet --eval "db.adminCommand({ ping: 1 })"
Автозапуск проверяется одной строкой: systemctl is-enabled mongod отвечает enabled, значит, после перезагрузки сервер поднимется сам. Если служба стартует и сразу падает, причина обычно видна в журнале: не хватает прав на каталог данных, занят порт или сломан отступ в конфиге.
Почему сразу после запуска у базы нет ни пароля, ни запретов
Секции security в конфигурации из пакета нет, и это означает, что аутентификация выключена. Сервер честно слушает localhost и пускает всех, кто сумел до него дотянуться. Проверить состояние сессии:
mongosh
db.runCommand({ connectionStatus: 1 })
В ответе у анонимного подключения список авторизованных пользователей останется пустым. Пока bindIp держит 127.0.0.1, угроза ограничена самой машиной: веб-морда, чужой скрипт или контейнер с проброшенной петлёй получают полный доступ к чтению, записи и удалению. Строка bindIp со значением 0.0.0.0 превращает уютное правило в открытую дверь: автоматические сканеры находят порт 27017 за минуты, и show dbs отдаёт им список баз.
Судьба открытых баз описана исследователями не раз: автоматические сканеры регулярно насчитывают тысячи серверов с выключенной аутентификацией, и для части из них это заканчивается печально. Чужие коллекции стирают и оставляют вместо них записку с контактом для возврата, либо тихо делают базу складом чужих данных. Откат таких историй упирается в резервные копии, которых у небрежно поднятого сервера обычно нет.
Ирония установки в том, что простота старта и есть источник риска. Установщик рассчитан на быстрый первый контакт: подключился, создал базу, пошёл работать. Для боевого сервера порядок обратный, и начинается он с учётной записи администратора.
Создание администратора до включения аутентификации
Администратора создают заранее, пока пароли ещё не включены, и делают это в служебной базе admin:
mongosh
use admin
db.createUser({
user: "admin",
pwd: passwordPrompt(),
roles: [
{ role: "userAdminAnyDatabase", db: "admin" },
{ role: "readWriteAnyDatabase", db: "admin" },
{ role: "dbAdminAnyDatabase", db: "admin" }
]
})
db.getUsers()
Функция passwordPrompt() спрашивает секрет интерактивно и не оставляет его ни в истории интерпретатора, ни в журнале. Роли с суффиксом AnyDatabase дают право управлять пользователями, читать и писать, обслуживать базы во всех базах сразу, но не управлять кластером. Для полноты власти существует root, её выдают точечно и никогда не прописывают приложениям.
Важная деталь называется localhost exception: если аутентификация уже включена, а пользователей ещё нет, сервер разрешает создать первого администратора только с локальной машины. Это аварийная лестница, а не рабочий порядок. Когда сервер рядом и доступ по SSH есть, надёжнее идти путём из блока выше: сначала пользователь, потом включение паролей.
Включение аутентификации в mongod.conf и перезапуск службы
Включение аутентификации это две строки в YAML с отступом в два пробела:
sudo nano /etc/mongod.conf
security:
authorization: enabled
sudo systemctl restart mongod
Проверка проста: mongosh без ключей подключается, но на первую же содержательную команду отвечает ошибкой Unauthorized с кодом 13. Список баз больше не читается, данные не трогаются. Рабочий вход:
mongosh -u admin -p --authenticationDatabase admin
Ключ --authenticationDatabase admin указывает, в какой базе живёт учётная запись. Без него сервер ищет пользователя в базе текущего подключения, и верный пароль оборачивается ошибкой Authentication failed: самая частая путаница первого дня. Внутри открытой сессии есть и своя авторизация:
db.auth("admin", passwordPrompt())
Утилитам и драйверам адрес передаётся строкой подключения, имя пользователя пишется прямо в ней:
mongosh "mongodb://admin@127.0.0.1:27017/admin"
Схема адреса одинаково работает в оболочке и в драйверах приложений, пароль при таком вызове запрашивается интерактивно. Секрет в строке подключения оставляют только в песочнице: боевые значения хранят в переменных окружения или в отдельном хранилище секретов, а не в коде и не в системе контроля версий.
Роли пользователей под конкретные задачи вместо одного пароля на всех
Один общий пароль на все приложения это отложенный инцидент. MongoDB решает вопрос ролями, и принцип простой: каждой стороне ровно столько прав, сколько ей нужно, ни командой больше. Стартовый набор встроенных ролей закрывает большинство сценариев:
- роль read даёт чтение коллекций в пределах одной базы;
- роль readWrite добавляет вставку, изменение и удаление документов;
- роль dbAdmin отвечает за обслуживание базы: индексы, статистику, представления;
- роль userAdmin управляет пользователями своей базы;
- роль dbOwner объединяет readWrite, dbAdmin и userAdmin в одной записи;
- роль root снимает все ограничения и годится только для аварийного входа.
Для служебных задач существуют отдельные роли: clusterAdmin управляет набором узлов и репликацией, backup и restore работают с резервными копиями. Приложениям их не выдают. Пользователь под приложение создаётся в его собственной базе:
mongosh -u admin -p --authenticationDatabase admin
use appdb
db.createUser({
user: "appuser",
pwd: passwordPrompt(),
roles: [ { role: "readWrite", db: "appdb" } ]
})
db.getUsers()
Приложение подключается строкой, в которой authSource указывает базу учётной записи:
mongodb://appuser@127.0.0.1:27017/appdb?authSource=appdb
Права на живой базе меняются без пересоздания пользователя:
use appdb
db.grantRolesToUser("appuser", [ { role: "dbAdmin", db: "appdb" } ])
db.revokeRolesFromUser("appuser", [ { role: "readWrite", db: "appdb" } ])
db.changeUserPassword("appuser", passwordPrompt())
Посмотреть выданные гранты одного пользователя:
use appdb
db.getUser("appuser")
Ответ перечисляет роли и их источник: встроенная роль или назначенная вручную. Разработчику в тестовой базе удобно выдать dbOwner: он создаёт индексы, читает статистику и управляет пользователями, не касаясь соседних баз.
Аналитику выдают read на нужную базу, приложению readWrite на его собственную, админу отдельную запись с userAdminAnyDatabase. Утечка одного секрета тогда ограничена одной ролью, и это лучшая страховка из бесплатных.
Ограничение сети и итоговые проверки защищённой базы
Сетевая гигиена начинается в конфигурации. Список адресов в bindIp пишется через запятую без пробелов, это особенность формата YAML в конфиге mongod:
net:
port: 27017
bindIp: 127.0.0.1,192.168.10.5
sudo systemctl restart mongod
ss -ltnp | grep 27017
Сетевой экран добавляет внешний контур:
sudo ufw allow from 192.168.10.20 to any port 27017 proto tcp
sudo ufw enable
sudo ufw status verbose
Проверка с соседней машины с разрешённого адреса должна пустить пользователя приложения:
mongosh --host 192.168.10.5 -u appuser -p --authenticationDatabase appdb
С постороннего адреса соединение не пройдёт вовсе. Три ошибки портят картину чаще прочих. Первая это табуляция в YAML: служба не стартует, и в журнале прямо указана причина. Вторая это включённая аутентификация на удалённом сервере без единого пользователя: localhost exception через сеть не работает, и сервер остаётся без входа. Третья это снятые заглушки hold перед крупным обновлением, после которого приложение встречает новую ветку без подготовки.
Хороший финальный ритуал состоит из трёх вопросов. Отвечает ли база на ping с localhost. Отвечает ли ошибкой Unauthorized команда без пароля. Отвечает ли таймаутом соединение с адреса, которого нет в правилах экрана. Три утвердительных ответа означают, что контур замкнут.
Неудачные попытки входа попадают в журнал строками Authentication failed, поэтому подозрительную активность видно и глазами, и любым сборщиком журналов. Наблюдение за этими строками обходится дешевле любого расследования после.
База, которая спрашивает пароль у каждого подключения, отдаёт приложению одну базу и не отвечает чужой подсети, спокойно переночует и в дата-центре. Дальше её жизнь продолжается в резервных копиях и репликах, но фундамент закладывается ровно здесь: проверенный вход, минимальные роли и закрытый порт.