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

sshsecuritylinuxnetworking

SSH-безопасность: ключи, туннели и хардеринг

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/

Чеклист хардеринга

Подводя итог, вот минимальный чеклист для каждого нового сервера:

  1. Сгенерировать ed25519-ключ с passphrase
  2. Скопировать публичный ключ на сервер
  3. Отключить парольную аутентификацию (PasswordAuthentication no)
  4. Отключить вход root (PermitRootLogin no)
  5. Ограничить пользователей (AllowUsers)
  6. Сменить порт (опционально, но снижает шум)
  7. Ограничить алгоритмы (KexAlgorithms, Ciphers, MACs)
  8. Установить fail2ban
  9. Настроить ~/.ssh/config на клиенте
  10. Включить ControlMaster для мультиплексирования
  11. Проверить доступ в новой сессии перед закрытием старой

SSH — инструмент, который стоит настроить один раз и правильно. Потраченный час на хардеринг и настройку конфига сэкономит десятки часов в будущем и закроет один из самых частых векторов атак на серверную инфраструктуру.

© 2026 Terminal Notes. Built with SvelteKit.