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

Парольный SSH на 0.0.0.0:22 — конвейер ботов. Цель: PasswordAuthentication no, KbdInteractiveAuthentication no, вход только ключом пользователя admin, плюс ограничение источника (UFW/nft allow с jump-хоста или VPN, не «весь интернет»). Fail2Ban снижает шум, но не заменяет выключенный пароль и закрытый WAN. Сначала второй ключ и живая сессия, потом reload ssh, потом сужение firewall. Не ставьте ufw disable, чтобы «боты отстали».

Хост host.example (10.0.20.10).

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

  • journalctl -u ssh полон Failed password и Invalid user;
  • с вашей сети вход паролем ещё работает — вы в той же очереди, что боты;
  • ss -tlnp | grep :22 слушает все интерфейсы;
  • после Fail2Ban баны есть, а пароль всё ещё yes — вы лечите симптом.
КартинаНе этот кейсСтатья
Accepted publickey для rootполитика rootroot login
Баны не появляютсяjail/backendFail2Ban молчит
Timeout с интернета, LAN окfirewallfirewall не настроен
Свой пароль отвергается, боты тожеключ/AllowUserssshd_config

Возможные причины

  1. Дефолт образа или «чтобы с телефона зайти» — PasswordAuthentication yes.
  2. KbdInteractiveAuthentication yes при выключенном Password — PAM всё ещё предлагает пароль.
  3. Порт 22 в security group 0.0.0.0/0, гостевой UFW inactive.
  4. Нет AllowUsers/AllowGroups — перебор имён дешёвый.
  5. Ключ не разложен, пароль оставили «на переход», переход не закончили.
  6. Второй sshd на 2222 с паролем «для партнёра».

Диагностика

sudo sshd -T | grep -Ei 'passwordauthentication|kbdinteractive|pubkeyauthentication|challengeresponse|authenticationmethods|permitrootlogin|maxauthtries|allowusers|allowgroups'
sudo ss -tlnp | grep -E ':22|:2222'
sudo ufw status verbose
sudo nft list ruleset 2>/dev/null | head

Журнал за сутки:

sudo journalctl -u ssh --since '24 hours ago' --no-pager \
  | grep -cE 'Failed password|Invalid user'
sudo journalctl -u ssh --since '24 hours ago' --no-pager \
  | grep -E 'Failed password|Invalid user' | awk '{print $NF}' | sort | uniq -c | sort -nr | head

Кто слушает и откуда пускают:

ip -4 addr show
sudo grep -RniE 'PasswordAuthentication|KbdInteractive' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/

Проверка своего ключа, не пароля:

ssh -vvv -o PreferredAuthentications=publickey -o BatchMode=yes admin@10.0.20.10 true

Если BatchMode падает, а ssh admin@10.0.20.10 спрашивает пароль — ключ не принят (права на .ssh, IdentitiesOnly, другой пользователь).

Решение

Сценарий A. Ключ работает, выключить пароль

Drop-in:

sudo tee /etc/ssh/sshd_config.d/99-keys-only.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
LoginGraceTime 30
AllowUsers admin
EOF
sudo sshd -t && sudo systemctl reload ssh

Не закрывайте текущую сессию. Новая:

ssh -o BatchMode=yes admin@10.0.20.10 true

ChallengeResponseAuthentication в новых OpenSSH — алиас к KbdInteractive; на 22.04/24.04 пишите KbdInteractiveAuthentication.

Сценарий B. Ограничить источник, не отключая firewall

Пароль можно выключить и оставить 22 в мир — боты останутся на Invalid user / ключах. Лучше сузить:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 10.0.20.0/24 to any port 22 proto tcp comment 'admin-lan'
sudo ufw allow OpenSSH
sudo ufw status numbered

Если allow OpenSSH без источника уже висит — удалите номерное правило «Anywhere» и оставьте CIDR. Порядок: не ufw reset на проде без консоли.

Облако: закройте SG/NSG до того же CIDR. Гостевой UFW не заменяет группу безопасности.

Подробнее про default deny: firewall Linux. Полный каркас sshd: как настроить SSH.

Сценарий C. Fail2Ban как слой, не как щит

После ключей и CIDR Fail2Ban имеет смысл против оставшегося шума (сканеры на закрытый пароль всё равно пишут Invalid user). Ставьте jail, который банит в nft/UFW, а не только логирует: настройка Fail2Ban. Если jail не банит — диагностика jail.

Не увеличивайте MaxAuthTries до 10 «чтобы себе было удобно» — ботам тоже удобно.

Сценарий D. Нужен пароль в аварии

Break-glass: консоль VM, не PasswordAuthentication на WAN. Если политика требует пароль с LAN:

Match Address 10.0.20.0/24
    PasswordAuthentication yes

Глобально выше — no. Проверьте sshd -T -C user=admin,addr=1.2.3.4 vs -C user=admin,addr=10.0.20.11. Не держите Match без Address.

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

С hop вне allow-листа (или с телефона через LTE, осторожно — не заприте себя):

ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no \
  -o BatchMode=yes admin@10.0.20.10 true; echo $?

Ожидание: отказ без интерактива. С разрешённого hop — ключ код 0.

sudo sshd -T | grep -E 'passwordauthentication|kbdinteractiveauthentication|authenticationmethods'
sudo journalctl -u ssh --since '10 min ago' | grep -c 'Failed password' || true

passwordauthentication no. Новые Failed password для валидных пользователей должны исчезнуть (останутся Invalid user с WAN, если 22 ещё торчит — тогда режьте firewall).

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

  • Пароль всё ещё спрашивает: cloud-init 50-cloud-init.conf или Match ниже вашего drop-in.
  • AuthenticationMethods publickey,password в другом файле — уберите password.
  • GSSAPI/Kerberos: отдельная тема; GSSAPIAuthentication no, если не используете.
  • Dropbear на IoT-образе: это не OpenSSH, правьте его конфиг.
  • Боты идут на IPv6, вы закрыли только IPv4: ufw allow from 2001:db8:20::/64 и sshd -T | grep port.
  • Ключ отвергается после отключения пароля — права 700/600, SELinux не штатен на Ubuntu; AppArmor sshd обычно не виноват. Смотрите sshd -ddd только на копии, не на проде вслепую.

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

  • В образе сразу ключи, пароль SSH выключен.
  • Jump-хост / VPN как единственный источник 22; матрица в заявке.
  • Алерт по всплеску Failed password и по passwordauthentication yes.
  • Ротация ключей и отзыв: скомпрометированный ключ.
  • Не публикуйте баннер с версией Ubuntu как «защиту».

FAQ

Смена порта 22 на 2222 спасёт?

Снизит шум скриптов-дефолтов, не модель угроз. Ключи + источник обязательны. Не считайте скрытый порт заменой firewall.

Нужен ли Fail2Ban если пароль выключен?

Полезен против сканеров и против других jail (web). Не обязателен, если 22 только с /32 jump-хоста. Не оставляйте пароль «потому что есть fail2ban».

PasswordAuthentication no, но WinSCP просит пароль

Клиент не отдал ключ (Pageant, ppk vs openssh, не тот пользователь). На сервере смотрите Failed publickey затем keyboard-interactive.

Можно ли оставить пароль и сложный 32-символьный секрет?

Боты всё равно заполнят диск логами. Сложность не уменьшает объём попыток. Выключайте пароль.

Чем AllowUsers лучше firewall?

Разные слои. AllowUsers режет имя на принятом TCP. Firewall режет кто дошёл до sshd. Нужны оба, если 22 когда-либо светился в интернет.