SSH - это не просто способ зайти на удалённый сервер. Это полноценный транспорт, который умеет аутентифицировать стороны по ключам и сертификатам, гонять через себя любой TCP-трафик и работать вместо VPN на скорую руку. На этом уроке мы перестаём пользоваться SSH как телефоном и начинаем настраивать его как инженеры: жёстко закрываем sshd, раздаём доступ по группам и подсетям, заводим ssh-agent, пробрасываем порты во все стороны и переходим на SSH-сертификаты, чтобы не возиться с authorized_keys на сотне машин.

Как это работает
Когда клиент соединяется с сервером, сначала договариваются о ключах шифрования сессии (это host key сервера - его отпечаток вы видите при первом подключении и он лежит в known_hosts). Только потом начинается аутентификация пользователя. Сервер предлагает методы по порядку: publickey, потом password, keyboard-interactive и так далее. Наша задача как админа - оставить только сильные методы и убрать пароли.
Аутентификация по ключу строится на асимметрии. У вас есть пара: приватный ключ на клиенте и публичный на сервере в файле ~/.ssh/authorized_keys. Сервер шлёт случайный вызов, клиент подписывает его приватным ключом, сервер проверяет подпись публичным. Приватный ключ при этом по сети не уходит. В 2026 году дефолт - это ed25519: короткий, быстрый, стойкий. RSA ещё жив, но только от 3072 бит и с SHA-2, а ssh-dss (DSA) давно вырезан и его трогать нельзя.
ssh-agent решает проблему парольной фразы на ключе. Вы один раз вводите пароль от ключа, агент держит расшифрованный ключ в памяти и сам подписывает вызовы. С форвардингом агента (ssh -A) подпись можно делать даже с промежуточного хоста, не копируя туда приватный ключ. Но форвардинг агента опасен на чужих машинах: root на промежуточном узле может через сокет агента подписать что угодно от вашего имени.
Проброс портов превращает SSH-туннель в транспорт. Локальный форвардинг (-L) открывает порт у вас и тащит соединения на адрес, видимый с сервера, - так ходят к закрытой базе через бастион. Удалённый (-R) делает наоборот: открывает порт на сервере и пускает трафик к вам, чем удобно показать локальный сервис наружу. Динамический (-D) поднимает SOCKS-прокси, и тогда любой настроенный на него софт ходит в сеть через сервер.
Сертификаты SSH убирают головную боль с authorized_keys и known_hosts. Вы заводите свой удостоверяющий центр (CA) - обычную ключевую пару, - и подписываете ею публичные ключи пользователей и хостов. Сервер доверяет одному CA, и любой подписанный им ключ проходит, пока не истёк срок. Отзыв делается списком (KRL) или просто коротким сроком жизни сертификата.
Команды и примеры
Генерация современного ключа и добавление в агент:
Код: Выделить всё
ssh-keygen -t ed25519 -C "ops@cyberlake"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh-add -l # что лежит в агенте
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
Код: Выделить всё
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AllowUsers deploy ops
AllowGroups sshusers
Match Group admins Address 10.0.0.0/24
PermitRootLogin prohibit-password
X11Forwarding yes
Проверка и применение по-разному в двух семействах:
Код: Выделить всё
# Debian 13 / Ubuntu 24.04
sshd -t # синтаксис
sshd -T | grep -i permitroot # эффективный конфиг
systemctl reload ssh
# RHEL 10 / Fedora 41+
sshd -t
systemctl reload sshd # юнит называется sshd
Проброс портов:
Код: Выделить всё
ssh -L 5432:db.internal:5432 bastion # к закрытой БД через бастион
ssh -R 8080:localhost:3000 server # показать свой :3000 на сервере
ssh -D 1080 server # SOCKS5-прокси на localhost:1080
Свой SSH-CA и подпись ключей:
Код: Выделить всё
ssh-keygen -t ed25519 -f user_ca -C "user CA"
ssh-keygen -s user_ca -I "ivanov" -n deploy,ops \
-V +8h -z 1 user_key.pub
ssh-keygen -L -f user_key-cert.pub # посмотреть содержимое
Ограничение команды для ключа (точечный доступ под бэкап или git) задаётся префиксом в authorized_keys:
Код: Выделить всё
restrict,command="/usr/local/bin/backup-only" ssh-ed25519 AAAA... backup
Частые грабли
- Заблокировать себя: выставили PasswordAuthentication no и PermitRootLogin no, не проверив свой ключ, и закрыли единственную сессию. Всегда держите вторую открытую сессию до reload.
- Правки тонут в drop-in: дистрибутив кладёт Include /etc/ssh/sshd_config.d/*.conf в начало файла, и файл оттуда переопределяет ваши строки. Проверяйте итог через sshd -T, а не глазами по основному файлу.
- Права на ~/.ssh: при 0777 на каталоге или 0644 на приватном ключе sshd молча игнорирует ключ. Нужно 0700 на каталоге и 0600 на ключах.
- AllowUsers и сертификаты: попавший под Allow-список принципал всё равно должен совпасть с реальным именем логина, иначе доступ закроется, хотя сертификат валиден.
- Форвардинг агента на чужом хосте: ssh -A на скомпрометированном бастионе отдаёт ваш агент атакующему. Используйте ProxyJump (-J) вместо -A там, где можно.
- known_hosts при пересоздании VM: новый host key ломает подключение MITM-предупреждением. Решение - host-сертификаты и @cert-authority, а не ssh-keygen -R в цикле.
- Сгенерируйте пару ed25519 с парольной фразой, загрузите её в ssh-agent и убедитесь через ssh-add -l.
- Разложите публичный ключ на тестовый сервер и проверьте вход без пароля.
- В sshd_config отключите root-логин и пароли, добавьте AllowGroups sshusers, проверьте sshd -t и sshd -T, перезагрузите службу (ssh на Debian, sshd на RHEL), не закрывая запасную сессию.
- Поднимите динамический прокси ssh -D 1080 и проверьте выход через него (curl --socks5 localhost:1080 ifconfig.me).
- Создайте user_ca, подпишите свой ключ на 1 час с принципалом своего логина, пропишите TrustedUserCAKeys и зайдите по сертификату.
- Добавьте ключ с command=... и restrict, убедитесь, что он выполняет только заданную команду.
- Чем директива AllowGroups в sshd_config предпочтительнее перечисления людей в AllowUsers и как они взаимодействуют с Deny-списками?
- Какие условия и какие директивы допустимы внутри Match-блока и в каком порядке sshd их применяет?
- В чём практическая разница между -L, -R и -D и какой опции sshd требует -R, чтобы слушать на внешнем интерфейсе?
- Что именно даёт ssh-agent и почему форвардинг агента считается опасным на промежуточных хостах?
- Как сервер начинает доверять SSH-сертификатам пользователей и хостов и за счёт чего сертификаты убирают возню с authorized_keys и known_hosts?
- Что делает префикс restrict в authorized_keys и зачем дополнительно указывать command=?