25 правил SSH, которые ITfresh применяет у каждого клиента — стандарт 2026
Был у нас клиент — бухгалтерская фирма из ЦАО, 22 рабочих места. После новостей о взломе одного известного провайдера их директор всерьёз занервничал насчёт собственных серверов. Мы пришли, провели аудит по внутреннему чек-листу SSH — и нашли 14 нарушений. Четырнадцать. На, казалось бы, небольшой конторе. Ниже я выкладываю этот чек-лист полностью: 25 правил, которые мы прогоняем у каждого клиента начиная с первой же настройки сервера. Будут реальные команды, конфиг sshd_config и объяснение того, где именно всё ломается — особенно когда правила применяют формально, не разбираясь, зачем они вообще нужны.
Зачем вообще нужен жёсткий стандарт SSH
SSH — это входная дверь к серверу. Если она из картона или просто не заперта — никакой firewall, никакой WAF, никакой антивирус не спасёт. Просто не спасёт, и всё. По нашей статистике, примерно 6 из 10 серверов в небольших компаниях при первом подключении имеют хотя бы одно из двух критичных нарушений: либо разрешён логин root по паролю, либо PasswordAuthentication стоит в yes. Этого вполне хватает, чтобы за 2–3 недели в логах накопилось от 100 000 до 5 миллионов попыток брутфорса — боты лезут со всего мира, непрерывно, методично.
Хорошая новость: привести SSH в порядок — это 1–2 часа работы инженера на сервер. Плохая: без чёткого чек-листа что-нибудь обязательно выпадет. Именно поэтому мы формализовали всё в список из 25 пунктов, разбитых на 4 группы — ключи и аутентификация, конфиг sshd, сетевая часть, аудит и эксплуатация.
Группа A: ключи и аутентификация (правила 1-7)
Правило 1. Только ed25519, никаких RSA-1024 и DSA
В 2026 году для SSH-ключей нормален ровно один алгоритм — ed25519. Быстрый, компактный, без задокументированных бэкдоров. Команда генерации:
ssh-keygen -t ed25519 -C "semenov@itfresh-ws01-2026q2" -f ~/.ssh/id_ed25519
# Обязательно с парольной фразой!
# При вопросе passphrase — вводим длинный пароль из менеджера паролей
RSA-2048 ещё проходит там, где нужна совместимость со старым железом — например, Cisco IOS до 15-й версии или совсем древние кастомные SSH-серверы. RSA-1024 и DSA — убираем везде. Они взломаны и формально, и практически.
Правило 2. PasswordAuthentication no
Аутентификация по паролю — запрещена. Без исключений. В sshd_config:
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
Забыл загрузить ключ перед командировкой? Это проблема инженера, а не повод открывать парольный вход. Для экстренных ситуаций у нас есть отдельная break-glass-процедура — о ней в правиле 7.
Правило 3. PermitRootLogin no
Логин под root — закрыт. Все инженеры заходят под именными учётками и поднимают привилегии через sudo. Благодаря этому в auth.log всегда видно, кто именно и что делал — а не просто «кто-то зашёл как root».
PermitRootLogin no
Правило 4. AllowUsers и AllowGroups — белый список
Не «все, кроме запрещённых», а «только разрешённые». В конфиге явно перечисляем конкретные учётки или группы:
AllowGroups ssh-users itfresh-engineers
# Альтернатива:
AllowUsers semenov ivanov ansible deploy
Пользователь вне этого списка не зайдёт, даже с правильным ключом. Спасает в ситуации, когда где-то осталась забытая учётка с непонятно чьим ключом — а такое, на нашей практике, встречается регулярно.
Правило 5. Парольная фраза на каждом приватном ключе
Приватный ключ без парольной фразы — это пароль, лежащий открытым текстом. Украли ноутбук — и у злоумышленника мгновенно есть доступ к админским правам на всех серверах. Поэтому каждый наш ключ защищён длинной парольной фразой, сгенерированной в менеджере паролей. Чтобы не вводить её вручную при каждом подключении, используем ssh-agent или 1Password CLI:
# macOS / Linux
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519
# Windows 11 — встроенный OpenSSH agent
Set-Service -Name ssh-agent -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519
Правило 6. Один ключ — одна цель
У инженеров ITFresh не один ключ на всё подряд — у каждого несколько, под разные классы задач. Один для административных серверов клиентов, второй для собственных production-сервисов, третий для GitHub. Если один ключ скомпрометирован — меняем именно его, не трогая остальное. Никакой паники на всю инфраструктуру.
# ~/.ssh/config
Host clients-*
IdentityFile ~/.ssh/id_ed25519_clients
IdentitiesOnly yes
Host github.com
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
Host production-*
IdentityFile ~/.ssh/id_ed25519_prod
IdentitiesOnly yes
Правило 7. Break-glass-учётка с физическим хранением ключа
Одна учётка break-glass с длинным паролем, ключ к которой лежит на USB-токене в сейфе у директора заказчика. Логин разрешён только с консоли (через VPN или через iLO/iDRAC). Используется в ситуациях, когда отвалилось всё остальное.
Группа B: конфигурация sshd (правила 8-15)
Правило 8. MaxAuthTries 3 и LoginGraceTime 30s
Боты не должны бесконечно перебирать ключи. И полуоткрытые соединения не должны висеть часами. Решается это парой параметров с таймаутами:
MaxAuthTries 3
MaxStartups 10:30:60
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
MaxStartups 10:30:60 работает так: после 10 одновременных неаутентифицированных подключений новые соединения начинают отбрасываться с вероятностью 30%, после 60 — все без исключения. Это прямая защита от DoS.
Правило 9. LogLevel VERBOSE
Дефолтный уровень логирования INFO — почти бесполезен при разборе инцидента. VERBOSE — другое дело: он пишет все попытки аутентификации и fingerprint каждого ключа. Когда что-то идёт не так, именно это позволяет понять, что произошло и откуда пришли.
LogLevel VERBOSE
SyslogFacility AUTH
Правило 10. Match-блоки для разных групп пользователей
Не все пользователи равны. Разработчику нужен полный shell — это понятно. Бухгалтеру достаточно SFTP к конкретной папке, а бэкап-скрипту вообще только rsync. Match-блоки решают это чисто и без лишних движений:
Match Group sftp-only
ChrootDirectory %h
ForceCommand internal-sftp -l VERBOSE
AllowTcpForwarding no
X11Forwarding no
PermitTunnel no
Match Group backup-only
ForceCommand /usr/local/bin/rrsync -ro /backup/source
AllowTcpForwarding no
X11Forwarding no
Match User semenov,ivanov
PermitTTY yes
AllowAgentForwarding yes
Правило 11. Запрет X11/TCP-форвардинга по умолчанию
X11 не нужен? Отключаем глобально. Нужен туннелированный доступ к внутренним сервисам — разрешаем точечно, прямо через Match-блок:
X11Forwarding no
AllowTcpForwarding no
GatewayPorts no
PermitTunnel no
AllowAgentForwarding no
Правило 12. Современные KexAlgorithms, Ciphers, MACs
Старые алгоритмы шифрования и обмена ключами нельзя просто «оставить на потом». Их нужно явно отключать. Актуальный набор на 2026 год выглядит так:
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,sntrup761x25519-sha512@openssh.com,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256
PubkeyAcceptedAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256
Результат проверяем через ssh-audit — подробнее об этом в правиле 19.
Правило 13. Только ed25519 и rsa-2048+ host keys
Дефолтные ключи DSA и ECDSA удаляем. Оставляем только ed25519 и RSA:
cd /etc/ssh
rm -f ssh_host_dsa_key* ssh_host_ecdsa_key*
ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" < /dev/null
ssh-keygen -t rsa -b 4096 -f ssh_host_rsa_key -N "" < /dev/null
# В sshd_config
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
# DSA и ECDSA hostkey-строки убираем
Правило 14. Banner и legal-предупреждение
Banner — это не про красоту. Это про юридическую чистоту. Если случится инцидент с попыткой несанкционированного доступа, banner послужит официальным уведомлением: система частная, любые попытки проникновения — нарушение. Без такого уведомления доказать злой умысел в суде куда сложнее:
# /etc/issue.net
*****************************************************************
ВНИМАНИЕ! Это закрытая система ITfresh.
Доступ только для авторизованных лиц.
Все действия логируются и могут быть переданы
в правоохранительные органы.
*****************************************************************
# В sshd_config
Banner /etc/issue.net
Правило 15. UseDNS no и StrictModes yes
UseDNS no # быстрее логин, нет зависимости от DNS
StrictModes yes # проверка прав на authorized_keys
PermitUserEnvironment no # запрет на ~/.ssh/environment
AcceptEnv LANG LC_* # только нужные переменные среды
Группа C: сеть и обвязка (правила 16-21)
Правило 16. fail2ban с агрессивными правилами
fail2ban необязателен, но ключевую аутентификацию он усиливает хорошо. Конкретная конфигурация для SSH:
# /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
backend = systemd
maxretry = 3
findtime = 600
bantime = 86400
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 1209600
ignoreip = 127.0.0.1/8 10.0.0.0/8 192.168.0.0/16
Параметр bantime.increment двойной: первая блокировка — 24 часа, вторая для того же IP — 48 часов, потом 96 и так до максимума 14 дней. Это эффективно «выжигает» спамеров из логов.
Правило 17. Бастион-хост и ProxyJump
Мой принцип простой: продакшен-серверы не должны торчать напрямую в интернет. Только через jump-host, он же бастион. Вот что мы прописываем на клиентской стороне в ~/.ssh/config:
Host bastion
HostName bastion.client.ru
User semenov
IdentityFile ~/.ssh/id_ed25519_clients
IdentitiesOnly yes
Host prod-*
User semenov
IdentityFile ~/.ssh/id_ed25519_clients
IdentitiesOnly yes
ProxyJump bastion
Команда ssh prod-app01 подключается через бастион автоматически. На бастионе нет производственных данных — только логи всех проходов.
Правило 18. Файрвол с белым списком IP
На клиентском сервере SSH открыт исключительно для:
- внутренней сети офиса (10.0.0.0/8 при подключении через VPN);
- VPN ITfresh (наша корпоративная сеть);
- Бастиона клиента;
- резервного публичного IP инженера — на случай аварийной ситуации.
ufw default deny incoming
ufw allow from 10.20.0.0/16 to any port 22
ufw allow from 91.218.245.10 to any port 22 # ITfresh VPN exit
ufw allow from 91.218.245.11 to any port 22 # ITfresh standby
ufw enable
Правило 19. Регулярный аудит через ssh-audit
Раз в квартал я прогоняю утилиту ssh-audit по всем клиентским серверам:
pip install ssh-audit
ssh-audit prod.client.ru -p 22
# Ожидаемый результат: общая оценка A или A+
# Если ниже B — есть критические замечания, чинить
ssh-audit проверяет криптографические настройки и сравнивает их с актуальной базой уязвимостей. На нашей практике бывало: алгоритм тихо менял статус на устаревший, а без утилиты мы бы узнали об этом только из новостей — и не в лучший момент.
Правило 20. Квартальная ротация ключей пользователей
В ITfresh действует жёсткое правило: раз в три месяца каждый инженер перевыпускает свой клиентский ключ и обновляет authorized_keys на всех серверах через Ansible. Долго? Да, кропотливо. Но смысл именно в этом — даже если ключ где-то незаметно утёк, он физически не проживёт дольше квартала.
Правило 21. Pin known_hosts на всех клиентских машинах
HashKnownHosts yes — не просто опция в конфиге. Это защита от случайного раскрытия списка серверов, к которым подключается инженер. И отдельный момент, который нельзя игнорировать: предупреждение «host key changed» требует немедленного разбора. Это либо атака MITM, либо переустановка сервера — в обоих случаях молча кликнуть «продолжить» нельзя.
# ~/.ssh/config
Host *
HashKnownHosts yes
StrictHostKeyChecking ask
VerifyHostKeyDNS no
Группа D: аудит и эксплуатация (правила 22-25)
Правило 22. Централизованный auth.log в Wazuh/Graylog
На каждом сервере rsyslog отправляет auth.log в наш центральный SIEM — Wazuh у большинства клиентов, Graylog у крупных. Там настроены правила:
- тревога — более 50 failed login за час с одного IP;
- Тревога: зафиксирован successful login с IP-адреса, которого нет в белом списке.
- Тревога: в файле sshd_config обнаружено изменение — auditd это поймал и сообщил.
- Тревога: в /home/*/.ssh/authorized_keys появилась новая строка. Кто-то добавил ключ.
cat > /etc/rsyslog.d/50-auth-to-wazuh.conf <<EOF
auth.* @@10.20.99.5:514
EOF
systemctl restart rsyslog
Правило 23. 2FA через TOTP для критичных серверов
На бастион-хосте и серверах с боевой бухгалтерией одного ключа мало. Дополнительно — TOTP-код через Google Authenticator или Aegis. Подключается через pluggable-модуль libpam-google-authenticator:
apt install libpam-google-authenticator
# В /etc/pam.d/sshd
auth required pam_google_authenticator.so
# В /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
# Для каждого пользователя — настройка
google-authenticator -t -d -f -r 3 -R 30 -W
Получается двойная защита: ключ плюс код из приложения. Брутфорсить такую связку — пустая трата времени.
Правило 24. Sudo с записью команд через io-log
sudo сам по себе пишет в auth.log только одно: «пользователь вызвал команду X». Реальный shell-сеанс под sudo -i там не отражается никак. Если нужно зафиксировать всё, что инженер делал с правами root — буквально каждую команду — включаем sudo io-log. Тогда картина полная.
# /etc/sudoers.d/log-everything
Defaults log_input, log_output
Defaults iolog_dir=/var/log/sudo-io
Defaults iolog_user=root:root
Defaults iolog_group=root
Логи бьются по сессиям, потом проигрываются через sudoreplay. Бывает критично при разборе инцидента «кто удалил production-таблицу в 14:32».
Правило 25. Регулярная проверка sshd -T в CI
Финальное правило — автоматизация. Раз в день Ansible идёт по всем клиентским серверам и собирает sshd -T (выдача актуального состояния конфига), сравнивает с эталоном. Если что-то отличается — алерт.
# Снимок конфига
ssh prod-app01 "sudo sshd -T" | sort > /tmp/actual.txt
diff /opt/itfresh/baseline/sshd-prod-app01.txt /tmp/actual.txt
# Отрабатывает в Ansible через checksum-модуль
- name: Validate sshd live config
command: sshd -T
register: live_sshd
- assert:
that:
- "'permitrootlogin no' in live_sshd.stdout|lower"
- "'passwordauthentication no' in live_sshd.stdout|lower"
- "'pubkeyauthentication yes' in live_sshd.stdout|lower"
Полный sshd_config «по уму»
Чтобы каждый раз не собирать конфиг из разных источников по кусочкам, выкладываем наш рабочий референсный sshd_config 2026 года — тот самый, который мы накатываем на клиентские Ubuntu/Debian-серверы:
# /etc/ssh/sshd_config — ITfresh standard 2026
Port 22
AddressFamily inet
ListenAddress 0.0.0.0
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
# Crypto
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,sntrup761x25519-sha512@openssh.com,diffie-hellman-group16-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
PubkeyAcceptedAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
# Auth
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
MaxSessions 5
MaxStartups 10:30:60
LoginGraceTime 30
AuthorizedKeysFile .ssh/authorized_keys
AuthenticationMethods publickey
# Allow lists
AllowGroups itfresh-engineers app-deploy sftp-only
# Behaviour
AllowTcpForwarding no
X11Forwarding no
PermitTunnel no
AllowAgentForwarding no
GatewayPorts no
PrintLastLog yes
TCPKeepAlive no
ClientAliveInterval 300
ClientAliveCountMax 2
UseDNS no
StrictModes yes
PermitUserEnvironment no
AcceptEnv LANG LC_*
Subsystem sftp internal-sftp
# Logging
LogLevel VERBOSE
SyslogFacility AUTH
# Banner
Banner /etc/issue.net
# Match blocks
Match Group sftp-only
ChrootDirectory %h
ForceCommand internal-sftp -l VERBOSE
AllowTcpForwarding no
X11Forwarding no
Match Group itfresh-engineers
AllowAgentForwarding yes
AllowTcpForwarding yes
Контр-нарратив: где SSH-харденинг — это перебор
Скажу вещь, которую многие не любят слышать. Один dev-сервер, на котором разработчик крутит тестовый WordPress, — это не то место, куда стоит тащить TOTP, бастион-хост и SIEM с алертами на изменение sshd_config. Бессмысленно. На dev достаточно отключить парольную аутентификацию и оставить только ключи. Остальное — операционные накладные расходы без реального выхлопа: сервер злоумышленнику неинтересен, а если что-то пойдёт не так — он поднимается заново из git за 20 минут.
По моему мнению, действительно жёсткие меры — пункты 22–25 — оправданы там, где компрометация SSH напрямую грозит потерей боевых данных или деньгами. Серверы с базой 1С, эквайринг, медицинские данные — вот где это обязательно. Тестовая VM разработчика? Там такой уровень защиты избыточен.
FAQ: что чаще всего спрашивают клиенты
Реально ли менять стандартный порт SSH с 22 на нестандартный?
Тема, вокруг которой копья ломают годами. Моя личная позиция: оставляю 22 порт, закрываю его файрволом, добавляю ключи и fail2ban. Перенос на 2222 убирает из логов 99% автоматических ботов — это факт. Но целевой атакующий просканирует все порты nmap-ом секунд за 30, и никакой нестандартный порт его не остановит. Именно поэтому для клиентских серверов с публичным IP мы переносим SSH на нестандартный порт исключительно ради тишины в логах. Не ради безопасности — давайте называть вещи своими именами.
Можно ли разрешить вход root по SSH, если ключ длинный?
PermitRootLogin no — это не про длину ключа. Это про разделение ответственности. На сервере должна быть именная учётка инженера — например, semenov — которая через sudo получает root по запросу, и каждый такой запрос пишется в auth.log. Когда 10 инженеров ходят под одной root-учёткой, концы найти невозможно. Регламент security audit в любой нормальной компании требует именно персональных учёток. Без вариантов.
Какой ключ выбрать — RSA 4096, ECDSA или ed25519?
Мой выбор — только ed25519. Исключение одно: если у вас жёсткие требования совместимости с совсем древними системами. RSA 4096 медленный и громоздкий. ECDSA построен на NIST-кривых, к которым у части аудиторов накопились исторические вопросы. ed25519 — современный, быстрый, компактный алгоритм с открытой криптографией и без каких-либо задокументированных бэкдоров. Все клиентские серверы, которые мы настраивали с 2024 по 2026 год, работают именно на нём.
Что делать, если не работает SSH-агент-форвардинг через jump-host?
ForwardAgent — небезопасная штука, и на практике он регулярно ломается на промежуточных хостах. Вместо него рекомендую ProxyJump в ~/.ssh/config. Схема простая: ssh client сначала коннектится к jump-host, а уже через него — к target. Ключ при этом остаётся на вашем локальном компьютере и никуда по цепочке не передаётся. На нашей практике такой подход и стабильнее, и безопаснее — без исключений.
Сколько стоит у ITfresh аудит SSH-настроек серверов?
Парк из 5–15 серверов — это 25–40 тысяч рублей и 1–2 рабочих дня нашего инженера. Что входит: проверка sshd_config по всем 25 пунктам, аудит ~/.ssh/authorized_keys на каждом сервере, проверка fail2ban, настройка централизованного auth.log в Wazuh или Graylog, отчёт с приоритезированными уязвимостями. Если параллельно делаем харденинг — ещё 1–2 дня, итоговая сумма 50–80 тысяч рублей.
Итог
SSH — не то место, где стоит экспериментировать. Те 25 правил, о которых я рассказал, — это стандарт, который мы отточили за 15 лет и применяем у каждого клиента без исключений. Была у нас бухгалтерская фирма из ЦАО: за один рабочий день инженер закрыл 14 уязвимостей, и количество попыток брутфорса в логах упало с 18 000 в сутки до 30. Тридцать. Если хотите такой же аудит — напишите, пришлю чек-лист и оценку работ.
Похожая задача в вашей компании?
Просто расскажите мне, какая у вас сейчас ситуация, и я обязательно пришлю вам план работ и оценку в течение одного рабочего дня.
Написать в Telegram или +7 903 729-62-41
Семёнов Е.С., руководитель ITfresh
