MAATRIX / Блог / Предел числа пользователей и ключей SSH: где ломается администрирование, а не техника

Предел числа пользователей и ключей SSH: где ломается администрирование, а не техника

MAATRIX

Файл authorized_keys может содержать хоть тысячу строк — sshd переберёт их за миллисекунды, и с точки зрения протокола никакого предела на число пользователей или ключей нет. Но команда, которая раздаёт доступ через ssh-copy-id и ручное редактирование файлов, стабильно упирается в стену на 15-20 активных пользователях — не потому что сервер не тянет, а потому что никто больше не может ответить на вопрос «у кого сейчас есть доступ и кто заходил в четверг». Разберём, где именно ломается ручное администрирование SSH-доступа и какие решения снимают эту боль на разных масштабах команды.

Технический потолок, которого на практике не существует

Стоит закрыть вопрос сразу: sshd не хранит состояние о ключах в памяти и не ограничивает их число программно. Каждое подключение — это чтение ~/.ssh/authorized_keys нужного пользователя с диска, построчный перебор и проверка подписи. Файл на несколько тысяч строк читается и парсится практически мгновенно на любом современном CPU — это I/O и однопроходный текстовый разбор, а не вычислительно тяжёлая операция.

Единственные технические ограничения, которые теоретически могут проявиться: размер самого файла (на практике не станет проблемой раньше, чем администрирование станет невозможным по человеческим причинам); число системных пользователей, ограниченное полем UID (32-битное значение на современных Linux, то есть на практике не предел); и, если используется AuthorizedKeysCommand для динамической подгрузки ключей из внешнего хранилища — узкое место переносится на систему, которая эти ключи отдаёт, а не на sshd.

Вывод простой: если у вас сегодня 200 ключей в authorized_keys на одном сервере и всё "работает" — технически действительно работает. Проблема не отобразится в top или в логах производительности. Она отобразится в вопросе службы безопасности "как быстро вы можете закрыть доступ уволенному сотруднику на всех серверах" — и в том, что честный ответ окажется "не знаем, пойдём проверять руками".

Где именно ломается ручное администрирование

Три конкретные точки разрыва, которые проявляются по мере роста команды — не одновременно, а последовательно, по мере пересечения условных порогов.

Первая точка: единообразие раздачи, 5-15 человек. Пока в команде три-пять разработчиков, каждый ключ добавляют вручную — кто-то через ssh-copy-id, кто-то копипастой в файл, кто-то через плейбук Ansible, который не запускали три месяца и не совпадает с реальностью. Уже на 10-15 пользователях на 3-5 серверах типично появляется расхождение: у Иванова на проде есть доступ, а на стейджинге нет ключа, который он добавил позже; у Петрова остался старый ключ с ноутбука, которым он не пользуется полгода. Никто не злоумышленник — просто ручной процесс без единого источника правды расходится сам по себе, это статистическая неизбежность при числе операций, растущем как произведение людей на серверы.

Вторая точка: отзыв доступа при увольнении, 10-30 человек, несколько серверов. Это самая болезненная и частая причина инцидентов. Если ключи раздавались вручную на N серверов, отзыв требует зайти на все N и удалить строку из каждого authorized_keys — а прежде нужно точно знать, на каких серверах она вообще есть. Без централизованного реестра эта информация размазана по истории коммитов Ansible-репозитория (если он вообще использовался последовательно), по памяти админа, который когда-то "накатывал ключи руками для срочности", и по самим файлам на серверах. Ровно об этом инцидент, который мы разбирали отдельно: ключ уволенного сотрудника проработал восемь месяцев именно потому, что часть доступа была выдана в обход единого процесса и о ней просто забыли.

Третья точка: аудит "кто и когда заходил", от 20+ пользователей или при первом внешнем аудите безопасности. Стандартный sshd пишет в auth.log/journalctl факт подключения — время, IP, имя unix-пользователя и (при LogLevel VERBOSE) fingerprint ключа. Но это не то же самое, что "кто из живых людей заходил под общим пользователем deploy". Если несколько человек используют общую учётку с разными ключами в одном authorized_keys, сопоставление ключа с человеком существует только там, где вы сами его ведёте — обычно нигде, кроме комментария в файле, который никто не обновляет. Когда приходит запрос "покажите, кто имел доступ к продовым серверам и когда заходил последний раз" — команда без централизованного учёта тратит на этот вопрос дни ручного сведения логов с десятков хостов.

Границы "5-15", "10-30" — не жёсткие цифры, а ориентир. Команда с высокой текучкой или большим числом подрядчиков упрётся раньше при меньшей численности; стабильная команда на трёх серверах может тянуть ручной процесс дольше. Смотреть нужно не на число людей, а на то, можете ли вы прямо сейчас честно ответить на вопрос "у кого есть доступ куда" за минуты, а не за день сверки.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Централизованное управление ключами: первый шаг без смены архитектуры

Прежде чем переходить к SSH CA или SSO, есть промежуточный шаг, который снимает боль первой и отчасти второй точки разрыва, не меняя саму модель authorized_keys — просто перестаёт быть ручной.

Единый источник правды + Ansible. Ключи хранятся не в головах и не в разрозненных файлах, а в одном репозитории (или в секрет-хранилище) в виде декларативного списка "пользователь → ключи → серверы, куда он должен попадать". Плейбук раскатывает authorized_keys на все серверы из этого источника при каждом изменении — про сам инструмент и его частые грабли у нас есть отдельный разбор: настройка Ansible для сервера.

Что это решает: единообразие раздачи и частично отзыв — если процесс запускается при каждом изменении команды, а не "когда вспомнили". Чего не решает: если прогон забыли запустить после увольнения, доступ остаётся живым так же, как при ручном процессе. Автоматизация раздачи не равна автоматизации отзыва, если триггер отзыва — человек, который должен не забыть нажать кнопку.

Секрет-менеджер для самих ключей. Отдельная, но смежная задача — не потерять приватные ключи и не хранить их в открытом виде на ноутбуках или в чатах. Здесь помогает HashiCorp Vault: выдачу и хранение приватных ключей подрядчикам стоит вести через хранилище с аудитом доступа, а не через мессенджер.

Централизованное управление ключами — это гигиена, а не решение проблемы отзыва в реальном времени: она снимает "мы забыли, что ключ вообще есть", но не снимает "мы забыли его отозвать".

SSH certificate authority: решение проблемы отзыва

SSH умеет работать не только с голыми публичными ключами, но и с сертификатами — эта функциональность встроена в OpenSSH с версии 5.6 (присутствует практически на любой актуальной системе), но её заметно недоиспользуют.

Идея: вместо того чтобы раскладывать публичный ключ каждого пользователя на каждый сервер, вы поднимаете один центр сертификации (SSH CA) — пара ключей, приватный держится в защищённом месте (тот же Vault умеет выступать SSH CA "из коробки"). Сервер настраивается один раз доверять этому CA:

# /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/ca.pub

Дальше вместо раздачи публичных ключей пользователей вы подписываете их сертификатами с ограниченным сроком жизни:

ssh-keygen -s ca_key -I "ivanov-prod-access" \
  -n ivanov -V +8h -z 1001 \
  ivanov_id_ed25519.pub

Здесь -V +8h — сертификат живёт 8 часов и перестаёт работать сам, без действий на сервере; -n ivanov — принципал, unix-пользователь, на котором он валиден; -z 1001 — серийный номер для точечного отзыва.

Что это меняет принципиально: на серверах больше не нужно ничего менять при выдаче или отзыве доступа человеку. authorized_keys вообще может быть пустым или содержать только сервисные ключи — доступ определяется тем, подписал ли CA сертификат и не истёк ли он. Увольнение сотрудника = не выдавать больше сертификатов на его имя; для немедленного отзыва ещё не истёкшего сертификата используется RevokedKeys со списком отозванных серийных номеров — это один файл, а не N записей в N файлах на N серверах.

Ограничения подхода, которые стоит проговорить честно: нужна инфраструктура вокруг выдачи сертификатов — как минимум скрипт или сервис, проверяющий личность запрашивающего перед подписью (иначе проблема просто переносится на "кто может подписывать сертификаты"); короткий TTL означает, что пользователю нужен удобный способ перевыпускать сертификат — руками каждые несколько часов неудобно, отсюда обычно вырастает интеграция с SSO (следующий раздел); а список отозванных ключей всё равно нужно доставлять на серверы через ту же Ansible-раскатку или AuthorizedKeysCommand — полностью "нулевого" администрирования не будет.

LDAP и SSO для SSH: когда доступ становится частью общего IAM

Следующий шаг по масштабу — когда SSH-доступ перестаёт быть отдельной системой и становится частью общего IAM компании, то есть тем же процессом, что заводит и отключает почту, VPN, доступ к внутренним панелям.

LDAP/AD как источник учёток и ключей. Классический вариант — центральный каталог (например, OpenLDAP), где у каждого пользователя в атрибутах хранится публичный SSH-ключ (схема sshPublicKey из стандартного openssh-lpk LDAP-schema). sshd настраивается получать ключи не из файла, а через AuthorizedKeysCommand, который на лету запрашивает LDAP:

# /etc/ssh/sshd_config
AuthorizedKeysCommand /usr/local/bin/ldap-keys-lookup %u
AuthorizedKeysCommandUser nobody

Отзыв доступа в этой схеме — одно действие в LDAP (деактивировать учётку или снять атрибут с ключом), мгновенно применяющееся на всех серверах, использующих этот источник, без раскатки на хосты.

SSO/OIDC-интеграции. Более современный вариант — короткоживущие сертификаты, выдаваемые через браузерную аутентификацию в корпоративном IdP (Okta, Google Workspace, Keycloak и подобные), без постоянных ключей на диске пользователя вообще. Пользователь запускает команду, браузер открывает окно логина через SSO, после успешной аутентификации локально генерируется одноразовый сертификат на несколько часов — механика похожа на SSH CA выше, но выдача полностью автоматизирована и привязана к тому же логину, что и остальные корпоративные сервисы.

Практический эффект этой связки: увольнение сотрудника = одно действие в HR-системе или IdP, которое каскадом закрывает и почту, и VPN, и SSH — а не отдельный чеклист "не забыть удалить с N серверов". Ровно эта каскадность и есть главная ценность.

Стоимость входа выше: нужен работающий LDAP/IdP, сам требующий администрирования — HA, бэкапы, мониторинг — плюс план на случай, если IdP недоступен: аварийный локальный ключ в обход SSO стоит предусмотреть заранее, а не изобретать в момент, когда IdP лёг.

Сравнение подходов и когда какой ставить

ПодходИсточник правдыСкорость отзываПорог внедренияКогда оправдан
Ручной authorized_keysФайлы на серверахЧасы-дниНулевой1-5 серверов, до ~10 человек
Ansible-раскаткаGit/VaultМинуты (если прогон запущен)Низкий, нужна дисциплина5-30 серверов, нет своего IAM
SSH CAПриватный ключ CAМгновенно для новых, для старых — RevokedKeysСредний, нужен сервис выдачиОборот подрядчиков, временный доступ
LDAP-интеграцияЦентральный каталогМгновенно на всех серверахСредне-высокий, нужен LDAP/ADУже держат LDAP для других сервисов
SSO/OIDCКорпоративный IdPМгновенно, каскадноВысокий изначальноЗрелый SSO, десятки-сотни серверов

"Порог внедрения" и "когда оправдан" в таблице — не жёсткие границы, а ориентировочная логика перехода. Небольшая команда с высокими требованиями комплаенса (финтех, обработка платежей, персональные данные) может оправданно поставить SSH CA даже при пяти серверах — если цена инцидента с забытым доступом выше цены настройки. И наоборот, команда на полусотне серверов без внешнего аудита может годами жить на дисциплинированной Ansible-раскатке, если процесс реально соблюдается.

Практический маршрут перехода без остановки инфраструктуры

Переход не обязан быть одномоментным и рискованным — работающие ключи в authorized_keys можно оставлять как fallback на весь переходный период.

  1. Инвентаризация. Соберите факт: какие ключи реально есть на каждом сервере сейчас. Скрипт, похожий на приведённый в разборе инцидента с ключом уволенного сотрудника, пройдётся по инвентарю хостов, соберёт fingerprints через ssh-keygen -lf и сведёт их в одну таблицу. Без этого шага любое "централизованное управление" начинается с несогласованного состояния.
  2. Единый реестр как источник правды. Даже до внедрения CA или LDAP заведите один git-репозиторий или таблицу с явным "человек → ключ → на каких серверах должен быть". Это снимает первую точку разрыва почти бесплатно.
  3. Автоматизация раскатки. Подключите Ansible-плейбук, который приводит authorized_keys в соответствие с реестром при каждом запуске — и запускайте его по факту изменения состава команды, а не по расписанию "когда вспомнили".
  4. Пилот SSH CA на непродакшн-контуре. Настройте TrustedUserCAKeys на паре стейджинг-серверов, отработайте выдачу и отзыв сертификатов, прежде чем переносить на прод, и обкатайте fallback на случай недоступности сервиса выдачи.
  5. Интеграция с существующим IAM, если он уже есть для других систем компании — LDAP или SSO с SSH обычно не самая сложная часть, если каталог пользователей уже поддерживается для почты и VPN.

Ни одно из перечисленных решений не работает само по себе без процесса офбординга, в котором закрытие SSH-доступа — обязательный пункт чеклиста, а не то, что вспоминают постфактум. Автоматизация убирает человеческий фактор из исполнения, но не из инициации — кто-то всё равно должен нажать кнопку "уволен" в системе, из которой каскадом закроется остальное.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Сколько ключей реально выдержит один authorized_keys без деградации производительности?

Технически — тысячи, файл читается и парсится за миллисекунды даже на скромном CPU. Проблема с ручным управлением никогда не была про производительность, только про то, что человек физически не успевает поддерживать соответствие файла реальному составу команды на всех серверах.

Обязательно ли поднимать LDAP или SSO, если в компании 20 разработчиков?

Не обязательно — часто достаточно дисциплинированной Ansible-раскатки ключей из единого git-репозитория плюс регулярного ручного аудита. LDAP/SSO оправданы, когда команда растёт дальше или уже держит такую инфраструктуру для других сервисов.

Что делать, если центр сертификации (SSH CA) недоступен, а на сервер нужно зайти прямо сейчас?

Оставляйте на каждом сервере аварийный доступ в обход CA — отдельный ключ с ограниченными правами, хранящийся офлайн и регулярно проверяемый на актуальность. Полагаться только на CA без запасного плана — риск остаться снаружи собственной инфраструктуры при сбое сервиса выдачи.

Может ли AuthorizedKeysCommand замедлить подключение, если он ходит во внешний LDAP или API?

Да, добавляет задержку на сетевой запрос при каждой попытке аутентификации — обычно доли секунды, но при недоступности или медленном ответе источника это ощутимо, особенно при массовых параллельных подключениях. Стоит держать таймаут в скрипте-обработчике и кэш на разумное время.

Нужно ли менять что-то на клиентской стороне при переходе на SSH CA?

Способ подключения не меняется — ssh user@host работает так же. Меняется то, что вместо статичного ключа появляется процесс получения короткоживущего сертификата (вручную через ssh-keygen -s или автоматически через SSO), который нужно периодически обновлять.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →