SSH: ключи, туннели, ProxyJump и хардеринг

SSH — это фундамент удалённого администрирования. Каждый, кто работает с Linux-серверами, использует его ежедневно. Но между “ssh root@server” и правильно настроенным SSH — пропасть. В этой статье разберём всё: от генерации ключей до продвинутого туннелирования и хардеринга, после которого ваш сервер перестанет быть лёгкой мишенью.
Типы ключей: ed25519 vs RSA
Первое, что нужно сделать — отказаться от паролей и перейти на ключи. Но какой алгоритм выбрать?
Ed25519 — современный стандарт. Основан на эллиптических кривых (Curve25519). Ключи короткие (256 бит), быстрые, устойчивые к side-channel атакам. Если нет специфических ограничений — используйте ed25519.
RSA — проверенный временем алгоритм. Всё ещё нужен для совместимости со старыми системами (RHEL 7, древние роутеры, некоторые Git-хостинги). Минимальная длина сегодня — 4096 бит. RSA-2048 формально ещё безопасен, но запас прочности тает.
ECDSA — компромисс, но с подвохом. Использует кривые NIST (P-256, P-384), к которым есть вопросы у криптографического сообщества. Если у вас есть выбор — лучше ed25519.
DSA — мёртв. OpenSSH отключил поддержку DSA по умолчанию начиная с версии 7.0. Не используйте.
Генерация ключей: ssh-keygen
Базовая генерация ed25519:
# Генерация ключа ed25519 с комментарием
ssh-keygen -t ed25519 -C "alex@workstation"
# Ключ с кастомным именем файла
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_production -C "alex@production"
# RSA 4096 бит (для совместимости со старыми системами)
ssh-keygen -t rsa -b 4096 -C "alex@legacy-systems" Рекомендации по passphrase:
- Всегда ставьте passphrase на ключи. Ключ без passphrase — как сейф с открытой дверью
- Используйте длинную фразу (минимум 20 символов), а не короткий пароль
- Для автоматизации (Ansible, CI/CD) допустимо использовать ключи без passphrase, но храните их отдельно и с минимальными правами
Смена passphrase на существующем ключе:
# Изменить passphrase без перегенерации ключа
ssh-keygen -p -f ~/.ssh/id_ed25519 Копирование публичного ключа на сервер:
# Стандартный способ
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# Ручной способ (если ssh-copy-id недоступен)
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" ~/.ssh/config: порядок в подключениях
Файл ~/.ssh/config — это то, что отделяет новичка от опытного администратора. Вместо запоминания IP, портов и пользователей — понятные алиасы.
# Общие настройки для всех хостов
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
IdentitiesOnly yes
# Продакшн-сервер
Host production
HostName 194.87.104.208
Port 6622
User alex
IdentityFile ~/.ssh/id_ed25519_production
# Стейджинг через jump-хост
Host staging
HostName 10.0.1.50
User deploy
IdentityFile ~/.ssh/id_ed25519_staging
ProxyJump production
# Группа серверов с wildcard
Host db-*
User postgres
IdentityFile ~/.ssh/id_ed25519_databases
ProxyJump production
Host db-master
HostName 10.0.2.10
Host db-replica-1
HostName 10.0.2.11
Host db-replica-2
HostName 10.0.2.12 Ключевые директивы:
- IdentitiesOnly yes — предлагать серверу только указанный ключ, а не все подряд. Без этого ssh-agent будет перебирать все ключи, что может привести к блокировке из-за MaxAuthTries
- ServerAliveInterval — keepalive-пакеты каждые 60 секунд, чтобы соединение не рвалось через NAT
- AddKeysToAgent yes — автоматически добавлять ключ в ssh-agent после первого ввода passphrase
Теперь вместо длинных команд:
# Было
ssh -i ~/.ssh/id_ed25519_production -p 6622 alex@194.87.104.208
# Стало
ssh production ProxyJump: доступ через jump-хосты
Типичная ситуация: продакшн-серверы находятся в приватной сети и доступны только через бастион-хост (jump host). Раньше для этого использовали ProxyCommand с netcat, но ProxyJump гораздо удобнее.
# Один jump-хост
Host internal-server
HostName 10.0.1.100
User admin
ProxyJump bastion
Host bastion
HostName bastion.example.com
User jump-user
IdentityFile ~/.ssh/id_ed25519_bastion
# Цепочка из двух jump-хостов
Host deep-server
HostName 10.10.1.50
User admin
ProxyJump bastion,internal-jump Из командной строки (без конфига):
# Прыжок через один хост
ssh -J bastion.example.com user@10.0.1.100
# Цепочка хостов
ssh -J user1@bastion:22,user2@internal-jump:22 admin@10.10.1.50 ProxyJump работает через stdio forwarding (ssh -W), поэтому не требует netcat или socat на промежуточных хостах. Всё, что нужно — OpenSSH 7.3+ на клиенте.
SSH-туннели: локальные, удалённые, динамические
Туннелирование — одна из самых мощных возможностей SSH. Три типа туннелей закрывают почти все сценарии.
Local forwarding (-L)
Пробрасывает порт с удалённого сервера на ваш localhost. Классический пример — доступ к базе данных, которая слушает только на localhost сервера:
# Синтаксис: ssh -L локальный_порт:целевой_хост:целевой_порт сервер
ssh -L 5432:localhost:5432 production
# Теперь psql подключается к продакшн-базе через localhost
psql -h 127.0.0.1 -p 5432 -U myapp
# Доступ к веб-интерфейсу, который слушает только на 127.0.0.1 сервера
ssh -L 8080:localhost:3000 production
# Открываем http://localhost:8080 в браузере Если целевой сервис находится не на SSH-сервере, а на другой машине в той же сети:
# Доступ к Redis на 10.0.1.20 через production-сервер
ssh -L 6379:10.0.1.20:6379 production Remote forwarding (-R)
Обратное направление: пробрасывает порт с вашей машины на удалённый сервер. Полезно, когда нужно показать локальный сервис через публичный сервер (аналог ngrok):
# Делаем локальный dev-сервер доступным через VPS
ssh -R 8080:localhost:3000 vps-server
# Теперь http://vps-server:8080 ведёт на ваш localhost:3000 По умолчанию remote forwarding слушает только на localhost удалённого сервера. Чтобы слушать на всех интерфейсах, нужно включить GatewayPorts yes в sshd_config (или GatewayPorts clientspecified):
# Слушать на всех интерфейсах удалённого сервера
ssh -R 0.0.0.0:8080:localhost:3000 vps-server Dynamic forwarding (-D / SOCKS)
Создаёт SOCKS5-прокси через SSH. Весь трафик, направленный через этот прокси, идёт через SSH-туннель:
# Создать SOCKS5-прокси на localhost:1080
ssh -D 1080 production
# В другом терминале: curl через SOCKS-прокси
curl --socks5 127.0.0.1:1080 https://api.ipify.org
# Покажет IP вашего сервера, а не вашей машины Удобно для обхода гео-ограничений и безопасного просмотра через удалённый сервер. Настройте браузер на SOCKS5-прокси 127.0.0.1:1080 — и весь браузерный трафик пойдёт через SSH-туннель.
Фоновые туннели
Запуск туннеля в фоне без открытия интерактивного шелла:
# -f -- уйти в фон, -N -- не выполнять команды, -T -- не создавать TTY
ssh -f -N -T -L 5432:localhost:5432 production
# Найти и завершить фоновый туннель
ps aux | grep "ssh -f -N" | grep -v grep
kill <PID> Для долгоживущих туннелей лучше использовать autossh, который автоматически переподключается при разрыве:
# Установить autossh
sudo apt install autossh
# Запуск с автопереподключением
autossh -M 0 -f -N -T -L 5432:localhost:5432 production Хардеринг sshd_config
Дефолтный sshd_config слишком мягок для продакшна. Вот минимальный набор изменений, который значительно повышает безопасность.

Основные параметры безопасности
# /etc/ssh/sshd_config
# Отключить вход по паролю (только ключи)
PasswordAuthentication no
ChallengeResponseAuthentication no
# Отключить вход root
PermitRootLogin no
# Ограничить пользователей, которым разрешён SSH
AllowUsers alex deploy
# Или по группам:
# AllowGroups ssh-users
# Изменить порт (не безопасность, а уменьшение шума в логах)
Port 6622
# Ограничить количество попыток аутентификации
MaxAuthTries 3
# Таймаут бездействия
ClientAliveInterval 300
ClientAliveCountMax 2
# Отключить пустые пароли
PermitEmptyPasswords no
# Только протокол 2 (протокол 1 давно мёртв, но на всякий случай)
Protocol 2
# Отключить X11 forwarding (если не нужен)
X11Forwarding no
# Отключить TCP forwarding, если не используете туннели
# AllowTcpForwarding no
# Баннер (юридическое предупреждение)
Banner /etc/ssh/banner Усиленная криптография
Ограничиваем алгоритмы только современными и проверенными:
# Разрешённые алгоритмы обмена ключами
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
# Разрешённые шифры
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
# Разрешённые MAC
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# Разрешённые типы ключей хоста
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256 Проверка и применение
Перед перезапуском sshd — всегда проверяйте конфиг:
# Проверить синтаксис конфигурации
sudo sshd -t
# Если ошибок нет -- перезапустить
sudo systemctl reload sshd
# ВАЖНО: не закрывайте текущую SSH-сессию!
# Откройте новую сессию в отдельном терминале и проверьте вход.
# Только после успешного входа в новой сессии можно закрыть старую. Золотое правило: никогда не закрывайте текущую SSH-сессию до проверки нового конфига. Одна опечатка — и вы потеряете доступ к серверу.
Fail2ban: автоматическая блокировка
Fail2ban сканирует логи и банит IP-адреса, с которых идёт слишком много неудачных попыток входа. Даже с отключённой парольной аутентификацией fail2ban полезен — он снижает нагрузку от ботов, которые перебирают ключи.
# Установка
sudo apt install fail2ban
# Создать локальный конфиг (не редактировать jail.conf!)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local Минимальная конфигурация /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
banaction = nftables-multiport
[sshd]
enabled = true
port = 6622
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h Полезные команды:
# Статус jail для SSH
sudo fail2ban-client status sshd
# Разбанить IP (если случайно заблокировали себя)
sudo fail2ban-client set sshd unbanip 203.0.113.50
# Посмотреть текущие баны
sudo fail2ban-client get sshd banned SSH agent forwarding
Agent forwarding позволяет использовать ваш локальный ssh-agent на удалённом сервере. Это нужно, например, для git pull на сервере с вашим ключом, не копируя приватный ключ на сервер.
# Добавить ключ в агент
ssh-add ~/.ssh/id_ed25519
# Подключиться с пробросом агента
ssh -A production
# На сервере -- ваш ключ доступен
git clone git@github.com:your/repo.git # работает! В ~/.ssh/config:
Host production
ForwardAgent yes Предупреждение о безопасности: agent forwarding опасен на ненадёжных серверах. Администратор (root) удалённого сервера может использовать ваш проброшенный агент для подключения к другим серверам от вашего имени. Включайте forwarding только для доверенных хостов.
Более безопасная альтернатива — ProxyJump. Вместо проброса агента через промежуточный сервер, ProxyJump устанавливает прямое end-to-end соединение, и ваш ключ никогда не покидает локальную машину.
Multiplexing: ControlMaster
Мультиплексирование позволяет использовать одно TCP-соединение для нескольких SSH-сессий. Это ускоряет повторные подключения (нет handshake) и экономит ресурсы.
# ~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600 # Создать директорию для сокетов
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets Как это работает:
- ControlMaster auto — первое подключение создаёт master-соединение, последующие используют его
- ControlPath — путь к Unix-сокету для мультиплексирования
- ControlPersist 600 — master-соединение живёт 600 секунд (10 минут) после закрытия последней сессии
На практике: первый ssh production устанавливает полноценное соединение (TCP handshake, аутентификация). Все последующие ssh production подключаются мгновенно через Unix-сокет. Это особенно заметно при работе с Ansible, который открывает десятки SSH-сессий.
Управление мультиплексированными соединениями:
# Проверить статус
ssh -O check production
# Закрыть master-соединение
ssh -O exit production
# Запросить forwarding на существующем соединении
ssh -O forward -L 5432:localhost:5432 production scp и rsync через SSH
scp — простое копирование
# Скопировать файл на сервер
scp -P 6622 backup.tar.gz alex@production:/tmp/
# Скопировать файл с сервера
scp production:/var/log/app.log ./
# Скопировать директорию рекурсивно
scp -r ./configs/ production:/etc/myapp/ rsync — умное копирование
rsync работает через SSH по умолчанию и превосходит scp во всём: дельта-синхронизация, прогресс, исключения, сжатие.
# Синхронизация директории на сервер
rsync -avz --progress ./deploy/ production:/var/www/myapp/
# С исключениями
rsync -avz --exclude='node_modules' --exclude='.git'
./project/ production:/opt/apps/project/
# Только скопировать изменённые файлы (dry-run для проверки)
rsync -avzn ./data/ production:/backup/data/
# Удалить на сервере файлы, которых нет локально
rsync -avz --delete ./static/ production:/var/www/static/
# Через нестандартный порт
rsync -avz -e "ssh -p 6622" ./files/ alex@194.87.104.208:/home/alex/files/ Если у вас настроен ~/.ssh/config, rsync автоматически использует его:
# Использует порт 6622, ключ и все настройки из ~/.ssh/config
rsync -avz ./deploy/ production:/var/www/app/ Чеклист хардеринга
Подводя итог, вот минимальный чеклист для каждого нового сервера:
- Сгенерировать ed25519-ключ с passphrase
- Скопировать публичный ключ на сервер
- Отключить парольную аутентификацию (
PasswordAuthentication no) - Отключить вход root (
PermitRootLogin no) - Ограничить пользователей (
AllowUsers) - Сменить порт (опционально, но снижает шум)
- Ограничить алгоритмы (KexAlgorithms, Ciphers, MACs)
- Установить fail2ban
- Настроить
~/.ssh/configна клиенте - Включить ControlMaster для мультиплексирования
- Проверить доступ в новой сессии перед закрытием старой
SSH — инструмент, который стоит настроить один раз и правильно. Потраченный час на хардеринг и настройку конфига сэкономит десятки часов в будущем и закроет один из самых частых векторов атак на серверную инфраструктуру.