Короткий ответ

Соберите один поздний drop-in /etc/ssh/sshd_config.d/99-hardening.conf: PermitRootLogin no, PasswordAuthentication no, KbdInteractiveAuthentication no, PubkeyAuthentication yes, AllowUsers admin, MaxAuthTries 3, LoginGraceTime 30. Эффективность проверяйте sshd -T, не глазами. Второе окно ssh -o BatchMode=yes admin@10.0.20.10 до reload. Консоль BMC открыта. Это сборка политики; точечные инциденты — root SSH, боты, ключ.

Хост host.example.

Симптомы и как отличить

Нужна эта инструкция, если ставите baseline на новый узел или сверяете парк. Если SSH уже не принимает коннект — сначала доступность порта (сетевой раздел linux), не hardening.

Возможные причины «конфиг не тот»

  1. 50-cloud-init.conf позже/раньше по лексике.
  2. Match в основном файле.
  3. Include не подхватывает ваш файл (права, имя).
  4. Два sshd.
  5. ansible каждый раз перетирает.

Диагностика

sudo sshd -t
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauthentication|kbdinteractive|pubkeyauthentication|allowusers|allowgroups|maxauthtries|logingracetime|x11forwarding|allowtcpforwarding|clientalive|authenticationmethods|port'
ls -l /etc/ssh/sshd_config.d/
sudo grep -RniE '^(Match|PermitRootLogin|PasswordAuthentication|AllowUsers)' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
systemctl status ssh.service ssh.socket --no-pager

Match-тест с адреса бота и с LAN:

sudo sshd -T -C user=admin,host=host.example,addr=10.0.20.11 | grep passwordauthentication
sudo sshd -T -C user=admin,host=host.example,addr=1.2.3.4 | grep passwordauthentication

Решение

1. Пользователь и ключ

admin в группе sudo, ключ в authorized_keys 600, каталог 700. Проверка BatchMode. Не начинайте с PasswordAuthentication no без ключа.

2. Drop-in

sudo tee /etc/ssh/sshd_config.d/99-hardening.conf >/dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
AllowUsers admin
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
ClientAliveInterval 30
ClientAliveCountMax 3
AuthorizedKeysFile .ssh/authorized_keys
EOF
sudo sshd -t && sudo systemctl reload ssh

AllowTcpForwarding no ломает ssh -L с этой машины. Если туннели нужны админам — yes или local. Не оставляйте forwarding «всем» без политики.

AllowUsers admin — единственное имя. Добавление deploy — явная правка.

3. Источник, не только sshd

UFW allow 22 с CIDR. Fail2Ban — слой: jail. Полный хост: baseline.

4. Banner и версия

Не полагайтесь на смену порта и DebianBanner no как защиту. Можно уменьшить шум, не модель угроз.

5. Откат

Консоль: rm 99-hardening.conf или вернуть из git, sshd -t, reload ssh. Снимок VM перед массовой выкладкой.

Дополнительные параметры, которые стоит явно зафиксировать (после проверки клиентов). PermitEmptyPasswords no — дефолт, но проверьте sshd -T. MaxSessions и MaxStartups режут наводнение соединений; слишком жёсткий MaxStartups на jump-хосте с автоматикой мешает ansible — подберите, не копируйте «10:30:60» вслепую без теста параллелизма.

DebianBanner no и кастомный Banner — юридический warning, не security control. Баннер не должен светить версию приложения.

TrustedUserCAKeys не включайте, пока нет процесса выдачи/отзыва сертификатов и KRL. Иначе получите вход по чужому CA с ноутбука подрядчика.

AuthorizedKeysCommand (sss, opk) — тогда файловые authorized_keys могут быть пусты; hardening всё равно нужен (PermitRootLogin, пароль, AllowUsers/AllowGroups). Сверьте sshd -T | grep authorizedkeyscommand.

Drop-in имена: 00- ранние, 99- поздние. Cloud-init любит 50-cloud-init.conf и 60-cloudimg-settings.conf. Ваш файл должен быть лексически последним среди тех, что задают те же ключи.

Проверка без lockout: sshd -t ловит синтаксис, не логику AllowUsers. Логику ловит вторая сессия. Держите старую, пока новая true.

Клиентский ~/.ssh/config для admin:

Host host.example
  HostName 10.0.20.10
  User admin
  IdentityFile ~/.ssh/admin-host-example-2026
  IdentitiesOnly yes

Иначе агент переберёт ключи и упрётся в MaxAuthTries.

Журналирование sshd: LogLevel VERBOSE даёт fingerprint ключа в Accepted — полезно для IR, чуть шумнее. На 22.04/24.04 это приемлемо для admin-хоста. Не DEBUG3 на проде постоянно: диск и секреты в логах handshake.

Subsystem sftp оставьте internal-sftp если нужен SFTP; chroot — отдельная задача. Не отключайте sftp «для hardening», если бэкапы им живут — ограничьте Match user backup.

Проверка Match Group vs AllowUsers: если оба заданы, должны пересекаться. Иначе вход нулевой. Тестируйте каждого пользователя из политики, не только admin.

Капча «сменили порт на 2222 и успокоились»: порт в drop-in Port 2222 требует UFW allow 2222 и обновления fail2ban port. Легко забыть и отрезать себя. Смена порта не в минимуме hardening, если не закрыли 22 в SG.

Документируйте эффективный sshd -T в git (без секретов). Diff при drift cloud-init.

На 24.04 после reload проверьте, что ssh.socket не держит старый Listen. ss -tlnp | grep ssh должен совпасть с Port в sshd -T. Если слушаете 22 и 2222 одновременно — это два входа, оба в UFW и в матрице, не «забытый дефолт».

Include в основном sshd_config должен быть Include /etc/ssh/sshd_config.d/*.conf. Не удаляйте его, иначе drop-in мёртв.

sshd -T на 24.04 всё ещё бинарь OpenSSH; не ищите ssh -T. Ошибки синтаксиса смотрите stderr sshd -t до reload.

Как проверить, что проблема устранена

sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|allowusers|maxauthtries|authenticationmethods'
ssh -o BatchMode=yes admin@10.0.20.10 true
ssh -o BatchMode=yes root@10.0.20.10 true; echo root:$?
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no -o BatchMode=yes admin@10.0.20.10 true; echo pass:$?

Ожидание: permitrootlogin no, passwordauthentication no, allowusers admin, maxauthtries 3. admin ключ 0. root и пароль — отказ.

Если не помогло

  • Параметр не меняется: файл после 99- по алфавиту, или Match.
  • AllowUsers отрезал ubuntu cloud — добавьте временно второе имя или консоль.
  • socket activation: systemctl restart ssh.socket только если понимаете unit 24.04.
  • GSSAPI снова включает интерактив: выключите явно если не нужен Kerberos.

Профилактика

  • Образ с 99-hardening и cloud-init disable_root.
  • Мониторинг sshd -T vs эталон.
  • Ключи именные, отзыв в HR.
  • Не править /etc/ssh/sshd_config руками если есть Include.

FAQ

MaxAuthTries 3 и несколько ключей в агенте

Клиент перебирает ключи, каждый может считаться попыткой. IdentitiesOnly=yes и IdentityFile в ~/.ssh/config.

PermitRootLogin prohibit-password

Не цель. См. статью root login.

Ciphers/Kex

Можно сузить по доке OpenSSH вашей версии, после проверки клиентов (WinSCP, ansible). Не копируйте список 2016 года — отрежете себя. Не выдумывайте CVE в статье: смотрите changelog пакета.

ListenAddress 10.0.20.10

Хорошо если один NIC. Не забудьте IPv6 политику.

DebianBanner / VersionAddendum

Косметика. Ключи и AllowUsers важнее.