MaxSessions, MaxStartups или регистрация устройств: какой лимит SSH выбрать
MaxSessions считает каналы внутри одного транспорта, MaxStartups защищает неаутентифицированные рукопожатия, и ни один из них не считает устройства. Сначала решите, что именно обещано.
Туннельные сервисы, обещающие одно устройство на абонента, редко получают это обещание от MaxSessions, и первый тест обычно это доказывает. Абонент открывает второй процесс ssh, первый сеанс продолжает работать, и оба туннеля несут трафик. Каждый параметр конфигурации считает что-то своё, и ни один из них не считает устройства. Именно название обещания в первую очередь делает выбор между ними очевидным.
Что на самом деле считает каждый параметр
MaxSessions ограничивает число открытых сеансов, которые несёт одно аутентифицированное сетевое соединение. Сеанс здесь означает оболочку, запрос exec, подсистему вроде SFTP или похожий канал, мультиплексированный внутри одного транспорта SSH. Второй вызов ssh открывает второй транспорт, заново проходит аутентификацию и получает собственное разрешение. Число сеансов первого соединения для него не имеет значения.
MaxStartups ограничивает одновременные соединения, которые ещё не прошли аутентификацию, выраженные как начальный порог, скорость и полный порог. Он существует, чтобы всплеск полуоткрытых рукопожатий не мог исчерпать ресурсы демона до аутентификации. Как только клиент аутентифицируется, это соединение перестаёт учитываться в MaxStartups и становится чьим-то сеансом. Он никогда не был квотой на пользователя, и его повышение не позволяет абоненту делать больше.
Правило регистрации устройства — вовсе не параметр sshd. Оно принадлежит любой системе, которая выдаёт учётные данные и отслеживает, какое устройство их хранит, а значит, ей нужны регистрация, инвентаризация и отзыв, а не счётчик. Смешивание этого требования с конфигурацией sshd — то место, где большинство таких проектов застревает.
Где MaxSessions перестаёт быть квотой
Блок Match User меняет, какие значения конфигурации применяются к этому пользователю, поэтому это естественное место задать более строгий MaxSessions для служебной учётной записи. Чего он не может — агрегировать между соединениями, потому что параметр оценивается для каждого транспорта. Два транспорта получают каждый свой лимит, и у пользователя в итоге вдвое больше, чем разрешает блок.
Перенаправление — отдельный сюрприз. Перенаправлению TCP не нужен канал сеанса, поэтому MaxSessions 0 может запретить оболочки и подсистемы и при этом разрешать туннели -L и -R. Сервис, который только перенаправляет и считает число сеансов своей квотой на туннель, не измеряет ничего. Это поведение описано в справочнике по конфигурации сервера OpenSSH, и его стоит прочитать, прежде чем проектировать вокруг имени параметра.
Выведите действующую конфигурацию, а не файл, который, по вашему мнению, загружен. Включения и каталоги drop-in часто означают, что действующее значение — не то, которое вы правили.
MaxStartups защищает демон, а не абонента
Трёхчастная форма MaxStartups описывает, сколько неаутентифицированных соединений может существовать, прежде чем демон начнёт отбрасывать процент новых. Его форма по умолчанию существует потому, что неаутентифицированное соединение SSH дёшево открыть и дорого держать. Это контроль защиты ресурсов сервера, и он действует для всех одновременно.
Повышение во время шторма входов меняет защиту демона на допуск, что иногда является правильным обменом и всегда осознанным. Оно ничего не делает для абонента, который хочет второй туннель, потому что первый туннель уже аутентифицирован. Смешение этих двух ведёт к изменению, которое исправляет не тот симптом и одновременно ослабляет сервер.
Когда входы отклоняются волнами, посмотрите в журнале демона сообщение до аутентификации, прежде чем трогать любой из двух параметров. Сообщение называет, какой страж сработал, и стражи не взаимозаменяемы.
Слот на пользователя требует общего состояния
Настоящая политика одного активного транспорта требует резервирования, которое хранится где-то вне sshd. Шаг допуска должен атомарно занять слот учётной записи, освободить его при закрытии соединения, дать ему истечь, когда клиент исчезает без закрытия, и решить, отклоняется ли новая попытка или заменяет старую. Это последнее решение — продуктовое, а не техническая деталь, потому что замена активного туннеля отключает кого-то.
Резервирование должно быть общим и для каждого сервера, который принимает учётную запись. Два хоста с независимыми локальными счётчиками применяют два отдельных лимита, и абонент, который переподключается через другой шлюз, бесплатно получает второй слот. Тому, что хранит состояние, нужно собственное поведение при сбое, поскольку недоступное хранилище не должно блокировать всех абонентов.
Для обещания одного устройства выдавайте отдельные учётные данные для каждого зарегистрированного устройства и управляйте регистрацией и отзывом на этом уровне. Скопированный закрытый ключ всё равно аутентифицируется откуда угодно, поэтому выдача одного ключа на пользователя не доказывает, сколько машин его хранят.
Проверьте политику двумя реальными соединениями
Напишите приёмочные тесты как команды, прежде чем писать политику. Пять случаев — это второй транспорт, открытый при активном первом, несколько каналов внутри одного транспорта, клиент, который только перенаправляет порты, переподключение после обрыва связи и та же учётная запись, приходящая на другой шлюз.
Сначала прочитайте действующие лимиты, затем откройте соединения, которые политика должна отклонять. Обе половины этого теста важны, и ни одна не заменяет другую.
sshd -T | grep -iE "maxsessions|maxstartups|allowtcpforwarding|permitopen"
ss -tn state established '( sport = :22 )' | head
ssh -N -L 8080:127.0.0.1:80 [email protected]Запустите команду туннеля дважды в двух терминалах и посчитайте установленные соединения на порту 22. Если оба несут трафик, пока ваша политика утверждает один активный транспорт, политика — это документация, а не принудительное применение. Устраните этот разрыв на уровне допуска, а не снижая MaxSessions в надежде, что второй транспорт это заметит.