25 правил SSH, которые ITfresh применяет у каждого клиента — стандарт 2026
Представьте, к нам обратилась одна бухгалтерская фирма из ЦАО, у них 22 рабочих места. После всех новостей о взломе одного известного провайдера их директор стал беспокоиться о безопасности собственных серверов. Мы сразу же провели аудит и обнаружили 14 нарушений, используя наш внутренний чек-лист SSH. Ниже я поделюсь этим чек-листом целиком: это 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 видеть, кто конкретно что делал.
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 — это юридическое уведомление о том, что система частная и попытки попасть на неё являются нарушением:
# /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 — есть критические замечания, чинить
Утилита проверяет криптографические настройки и сравнивает с актуальной базой уязвимостей. Очень полезна, потому что иногда мы упускаем смену алгоритма и без неё узнали бы только из новостей.
Правило 20. Квартальная ротация ключей пользователей
В команде ITfresh мы строго следим за тем, чтобы раз в три месяца каждый наш инженер перевыпускал свой клиентский ключ и затем обновлял authorized_keys на всех серверах с помощью Ansible. Да, это довольно долгий и кропотливый процесс, но именно он гарантирует, что даже если какой-то ключ незаметно скомпрометирован, он всё равно не сможет работать дольше 3 месяцев.
Правило 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 только факт «вызвал sudo с командой 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-сервере — отключить пароль и разрешить только ключи. Всё остальное добавляет операционных накладных и ничего не приносит, потому что сервер а) неинтересен злоумышленнику, б) если зайдёт — переразвернётся за 20 минут из git.
На мой взгляд, реально жёсткие меры (это пункты 22-25) абсолютно необходимы там, где компрометация SSH может обернуться потерей боевых данных или серьёзными денежными потерями. То есть, если речь идёт о серверах с базой 1С, серверах с эквайрингом или серверах с медицинскими данными — однозначно да. А вот для тестовой VM для разработчика — скорее нет.
FAQ: что чаще всего спрашивают клиенты
Реально ли менять стандартный порт SSH с 22 на нестандартный?
Ох, это, конечно, вопрос для целых религиозных войн. Я лично всегда ставлю на 22 порт и закрываю его через файрвол, плюс использую ключи и fail2ban. Да, перенос на 2222 порт, конечно, позволяет убрать 99% автоматических ботов из логов, но, давайте будем честны, по существу это не сильно повышает безопасность — ведь целевой атакующий просканирует все порты всего за 30 секунд с помощью nmap. Именно поэтому для клиентских серверов, у которых есть публичный 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 годы, работают именно на ed25519.
Что делать, если не работает SSH-агент-форвардинг через jump-host?
Я настоятельно рекомендую использовать ProxyJump в ~/.ssh/config вместо ForwardAgent, потому что последний небезопасен и, честно говоря, часто "ломается" на промежуточных хостах. ProxyJump работает так: он устанавливает прозрачный туннель, где 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 лет работы стандарт, который мы применяем абсолютно у каждого клиента. Помните ту бухгалтерскую фирму из ЦАО? Всего за 1 рабочий день наш инженер закрыл им 14 уязвимостей, и количество попыток брутфорса в логах снизилось с 18 000 в сутки до 30. Впечатляет, правда? Если вам нужен такой же аудит, просто напишите мне — я пришлю вам чек-лист и, конечно, оценку работ.
Похожая задача в вашей компании?
Просто расскажите мне, какая у вас сейчас ситуация, и я обязательно пришлю вам план работ и оценку в течение одного рабочего дня.
Написать в Telegram или +7 903 729-62-41
Семёнов Е.С., руководитель ITfresh
