Today

SSH на практике: от ключей до туннелей

Каждый раз, когда вы пишете git push, деплоите контейнер или подключаетесь к удалённому серверу через терминал — за кулисами работает SSH. Протокол, который появился тридцать лет назад как ответ на массовое воровство паролей, сегодня стал невидимым фундаментом серверной инфраструктуры. Он настолько привычен, что о нём не думают — как о воздухе.

И в этом проблема. Многие разработчики обращаются с SSH как с чёрным ящиком: копируют команды со Stack Overflow, жмут yes на все вопросы терминала и надеются, что всё как-нибудь сработает. До первого инцидента это даже работает.

Этот пост — подробный разбор SSH для практиков. Я расскажу, как устроен протокол изнутри, почему пароль — плохая идея, как работают ProxyJump и SSH-туннели, и дам готовую конфигурацию hardening для продакшена. Материал пригодится разработчикам, DevOps-инженерам и системным администраторам — всем, кто работает с серверами и хочет делать это осознанно и безопасно.

От Telnet к SSH: история одной революции

В начале 1990-х удалённое управление серверами выглядело пугающе просто. Администратор подключался через Telnet, вводил логин и пароль — и все эти данные летели по сети в открытом виде. Любой, кто мог перехватить трафик (коллега в той же сети, провайдер, злоумышленник в кафе), видел всё: пароли, команды, конфиденциальные данные. Безопасность держалась на честном слове и на предположении, что «в нашей сети никто не подслушивает».

В 1995 году финский исследователь Тату Юлёнен (Tatu Ylönen) из Хельсинкского технологического университета столкнулся с атакой на университетскую сеть, в ходе которой были украдены тысячи паролей — именно через снифинг Telnet-трафика. Масштаб утечки был таким, что Юлёнен не стал ждать, пока кто-то другой решит проблему. За три месяца он написал первую версию SSH — протокола, который шифрует всё: от момента подключения до разрыва соединения. Уже в июле 1995 года SSH-1 был опубликован как freeware и начал стремительно распространяться.

Первая версия протокола (SSH-1) содержала ряд криптографических слабостей, и в 2006 году ей на смену пришёл SSH-2 — полностью переработанный протокол с модульной архитектурой, более сильными алгоритмами и защитой от известных атак. Именно SSH-2 мы используем сегодня, а его открытая реализация — OpenSSH — стоит на подавляющем большинстве серверов в мире.

Сегодня SSH — это фундамент инфраструктурной безопасности. Каждый раз, когда вы выполняете git push, подключаетесь к серверу через терминал или настраиваете CI/CD pipeline, вы используете SSH. Протоколу тридцать лет, но он по-прежнему актуален — и, судя по всему, будет актуален ещё долго.

Что умеет SSH

SSH решает три фундаментальные задачи:

Конфиденциальность. Весь трафик между клиентом и сервером шифруется. Даже если злоумышленник перехватит пакеты, он увидит лишь случайный набор байтов — без ключей расшифровать их невозможно.

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

Целостность. Каждое сообщение защищено кодом аутентификации (MAC). Если кто-то попытается изменить данные «на лету» — подменить команду, подкорректировать ответ сервера — получатель это мгновенно обнаружит, и соединение будет разорвано.

Эти три свойства — ровно то, чего так не хватало Telnet. И именно они делают SSH незаменимым инструментом для любой работы с удалёнными системами.

Где живёт SSH в современном мире

SSH настолько вездесущ, что его часто не замечают — как электричество в розетке. DevOps-инженер, деплоящий приложение на сервер, использует SSH. Разработчик, отправляющий код на GitHub командой git push, использует SSH. Системный администратор, копирующий файлы через rsync, использует SSH. Kubernetes-оператор, подключающийся к ноде для дебага — и тот использует SSH.

Вот типичные сценарии:

Удалённое управление. Подключение к серверу через терминал — классический use case. Вы получаете shell на удалённой машине и работаете так, будто сидите перед ней. Именно так администрируются миллионы серверов по всему миру.

Git и контроль версий. Большинство Git-хостингов (GitHub, GitLab, Bitbucket) поддерживают SSH для аутентификации. Вместо ввода пароля при каждом push вы используете криптографический ключ — это и безопаснее, и удобнее.

Передача файлов. Утилиты scp, sftp и rsync работают поверх SSH, обеспечивая защищённую передачу. Нужно скопировать конфиг, забрать лог-файл или синхронизировать директорию — всё это делается через SSH.

Туннелирование. SSH умеет «пробрасывать» порты — вы можете безопасно подключиться к базе данных, находящейся за файрволом, создав зашифрованный туннель. Об этом мы подробно поговорим в разделе про port forwarding.

Bastion hosts. В корпоративных сетях серверы часто недоступны из интернета напрямую. SSH позволяет «прыгать» через промежуточный сервер (bastion) и попадать во внутреннюю сеть — при этом весь трафик остаётся зашифрованным.

Архитектура SSH: три слоя защиты

SSH спроектирован как слоёный пирог, где каждый слой решает свою задачу. Такая архитектура описана в трёх отдельных RFC и позволяет гибко комбинировать алгоритмы, добавлять новые функции и при этом не ломать совместимость.

Transport Layer: фундамент безопасности

Самый нижний слой (RFC 4253) отвечает за то, чтобы никто не мог прослушать или подменить данные. Когда клиент подключается к серверу, первым делом они договариваются о криптографических алгоритмах и устанавливают защищённый канал. Этот процесс называется key exchange (обмен ключами) и происходит до того, как будет передан хоть один байт пользовательских данных.

На этом этапе происходит аутентификация сервера. Клиенту нужно убедиться, что он подключился к правильному серверу, а не к злоумышленнику. Для этого используется host key — криптографический ключ, уникальный для каждого сервера.

Современные реализации SSH поддерживают десятки алгоритмов, но рекомендуется использовать лишь несколько проверенных:

# Key Exchange — согласование общего секрета
curve25519-sha256            # Рекомендуется: быстрый, безопасный
ecdh-sha2-nistp256           # Для FIPS-совместимости

# Encryption — шифрование данных
chacha20-poly1305@openssh.com  # Рекомендуется: AEAD, быстрый на CPU без AES-NI
aes256-gcm@openssh.com         # Для серверов с аппаратным ускорением AES

# MAC — целостность (только для non-AEAD шифров)
hmac-sha2-512-etm@openssh.com  # Encrypt-then-MAC — правильный порядок
hmac-sha2-256-etm@openssh.com

Суффикс -etm означает Encrypt-then-MAC — сначала шифруем данные, потом вычисляем MAC по шифротексту. Это криптографически правильный порядок: если сделать наоборот (MAC-then-Encrypt), атакующий может эксплуатировать так называемые padding oracle уязвимости. AEAD-шифры вроде chacha20-poly1305 и aes256-gcm решают эту проблему элегантнее — они объединяют шифрование и аутентификацию в одну операцию, и отдельный MAC им не нужен.

User Authentication Layer: кто вы?

После установки защищённого канала сервер хочет узнать, кто к нему подключился. Слой аутентификации (RFC 4252) предлагает несколько методов, и их безопасность сильно различается.

Аутентификация по публичному ключу (publickey) — золотой стандарт. Пользователь доказывает владение приватным ключом, не передавая сам ключ по сети. Даже если злоумышленник перехватит весь трафик, он не сможет авторизоваться — потому что перехватывать нечего.

Парольная аутентификация (password) — наследие прошлого. Да, пароль передаётся по зашифрованному каналу, но это не спасает от слабых паролей и brute-force атак. На продакшен-серверах её следует отключать.

Интерактивная аутентификация (keyboard-interactive) — гибкий механизм, который позволяет реализовать многофакторную аутентификацию: сначала пароль, потом код из TOTP-приложения. Этот метод часто используется в корпоративных средах, где одного фактора недостаточно.

Connection Layer: много каналов в одном соединении

Верхний слой (RFC 4254) занимается мультиплексированием. Внутри одного SSH-соединения может существовать несколько независимых каналов: интерактивная сессия, проброс порта, передача файла — и всё это одновременно, через одно TCP-соединение.

Это архитектурное решение экономит ресурсы: не нужно устанавливать новое соединение (и проходить полный хэндшейк с обменом ключами) для каждой операции. Вы подключились один раз — и дальше можете открывать сколько угодно каналов внутри этого соединения.

Host Keys: как не подключиться к поддельному серверу

Представьте ситуацию: вы в кафе, подключаетесь к рабочему серверу. Злоумышленник в той же Wi-Fi сети перехватывает ваше соединение и подсовывает свой сервер вместо настоящего (классическая man-in-the-middle атака). Вы вводите пароль — и он у злоумышленника.

SSH защищает от этой атаки с помощью host keys. У каждого сервера есть уникальный криптографический ключ, и при первом подключении клиент показывает его «отпечаток» (fingerprint):

$ ssh user@server.example.com

The authenticity of host 'server.example.com (192.0.2.10)' can't be established.
ED25519 key fingerprint is SHA256:AbC123xYz789...
Are you sure you want to continue connecting (yes/no/[fingerprint])?

Этот момент — критическая точка безопасности. SSH честно говорит: «Я не знаю этот сервер. Вот его fingerprint. Ты уверен, что это тот сервер, который тебе нужен?» И от вашего ответа зависит, будете ли вы защищены дальше.

TOFU: Trust On First Use

На практике большинство пользователей просто нажимают yes, не проверяя fingerprint. Это называется TOFU — Trust On First Use, доверие при первом использовании. Подход не идеален (если именно в момент первого подключения кто-то перехватил трафик — вы в беде), но работает: после первого раза вы защищены.

Почему? После подтверждения fingerprint сохраняется в файле ~/.ssh/known_hosts. При последующих подключениях SSH автоматически проверяет, что fingerprint совпадает с сохранённым. Если сервер подменят — вы узнаете об этом немедленно.

Правильный подход к проверке fingerprint:

  1. Получите fingerprint из доверенного источника: панель управления хостингом, документация, звонок администратору.
  2. Сравните его с тем, что показывает SSH.
  3. Только после проверки подтвердите подключение.

Для тех, кто управляет собственной DNS-инфраструктурой, есть элегантная альтернатива TOFU — SSHFP-записи. Это специальный тип DNS-записей, в которых публикуется fingerprint сервера. Если DNS защищён DNSSEC, SSH-клиент может автоматически проверить fingerprint через DNS, вообще не задавая вопросов пользователю. Настраивается директивой VerifyHostKeyDNS yes в ~/.ssh/config.

Когда fingerprint меняется

Иногда SSH показывает устрашающее предупреждение:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!

Выглядит как хоррор — и это намеренно. SSH кричит на вас, потому что ситуация потенциально опасная: fingerprint сервера изменился с момента последнего подключения. Причины могут быть легитимными (сервер переустановили, ключи перегенерировали) или зловещими (кто-то пытается перехватить соединение).

Правильная реакция — не игнорировать предупреждение. Свяжитесь с администратором сервера и уточните, менялись ли ключи. Если да — удалите старый fingerprint командой ssh-keygen -R hostname и подключитесь заново. Если нет — у вас серьёзная проблема безопасности, и подключаться к этому серверу нельзя.

Public Key Authentication: забудьте о паролях

Парольная аутентификация — это атавизм, который тянется из эпохи Telnet. Даже если пароль передаётся по зашифрованному каналу, он остаётся слабым звеном: его можно подобрать, выманить или украсть.

Проблема brute-force. Откройте логи любого сервера с публичным IP — и вы увидите тысячи попыток угадать пароль. Боты круглосуточно перебирают комбинации root/password123, admin/admin, ubuntu/ubuntu. Если ваш пароль хоть немного предсказуем, рано или поздно его подберут.

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

Проблема переиспользования. Люди используют одинаковые пароли везде. Утечка базы данных какого-нибудь форума — и ваш пароль от сервера скомпрометирован.

Аутентификация по публичному ключу решает все эти проблемы разом. Приватный ключ никогда не покидает ваш компьютер. Сервер хранит только публичный ключ, по которому нельзя восстановить приватный. Даже если злоумышленник перехватит весь трафик, он не получит ничего, что позволит ему авторизоваться.

Генерация SSH-ключей: практика

Теория — это прекрасно, но давайте создадим реальную пару ключей. Весь процесс занимает одну команду, однако за этой простотой скрываются важные решения.

Выбор алгоритма — первое, о чём стоит подумать. Если вы работаете с современными системами (OpenSSH 6.5 и новее, то есть практически всё после 2014 года), выбирайте Ed25519. Это алгоритм на эллиптических кривых Curve25519, разработанный Дэниэлом Бернштейном. Ключи Ed25519 компактны (всего 68 символов в публичной части), операции выполняются молниеносно, а безопасность не зависит от качества генератора случайных чисел — в отличие от ECDSA, где плохой RNG приводил к реальным взломам.

Самый известный пример — взлом PlayStation 3: Sony использовала фиксированное значение вместо случайного nonce при подписи ECDSA, что позволило хакерам из fail0verflow восстановить приватный ключ консоли и запускать на ней произвольный код. Ed25519 спроектирован так, чтобы подобные ошибки были невозможны в принципе — алгоритм детерминированно вычисляет nonce из сообщения и приватного ключа.

Для генерации Ed25519-ключа выполните:

ssh-keygen -t ed25519 -C "alice@company.com"

Флаг -C добавляет комментарий — обычно туда пишут email или имя машины. Это не влияет на безопасность, но помогает понять, какой ключ для чего, когда у вас их накопится десяток.

Если по какой-то причине Ed25519 недоступен (древний сервер, экзотическая встраиваемая система), используйте RSA с длиной не менее 4096 бит:

ssh-keygen -t rsa -b 4096 -C "alice@company.com"

RSA-2048 формально ещё безопасен, но 4096 даёт запас на будущее без заметных накладных расходов. А вот DSA и RSA-1024 — категорически нет: они устарели и не должны использоваться ни при каких обстоятельствах. OpenSSH отключил поддержку DSA по умолчанию начиная с версии 7.0.

Диалог генерации выглядит примерно так:

Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/alice/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/alice/.ssh/id_ed25519
Your public key has been saved in /home/alice/.ssh/id_ed25519.pub

На вопрос о passphrase настоятельно рекомендую ввести надёжный пароль. Почему это важно — расскажу чуть ниже.

В результате появляются два файла. Приватный ключ ~/.ssh/id_ed25519 — это ваш секрет, который никогда не должен покидать вашу машину. Публичный ключ ~/.ssh/id_ed25519.pub можно свободно раздавать — именно его вы будете копировать на серверы и добавлять в GitHub.

Права доступа критически важны. SSH откажется работать с ключом, если права слишком открытые — и правильно сделает:

chmod 600 ~/.ssh/id_ed25519      # Только владелец может читать и писать
chmod 644 ~/.ssh/id_ed25519.pub  # Публичный ключ может читать кто угодно
chmod 700 ~/.ssh                  # Директория доступна только владельцу

Аппаратные ключи: следующий уровень защиты. Начиная с OpenSSH 8.2, появилась поддержка ключей на основе стандарта FIDO2. Если у вас есть аппаратный токен (YubiKey, SoloKey и т. п.), вы можете сгенерировать ключ типа ed25519-sk, где sk означает security key:

ssh-keygen -t ed25519-sk -C "alice@company.com"

Приватный ключ при этом хранится на самом аппаратном устройстве и не может быть скопирован. Для каждой SSH-операции требуется физическое подтверждение — прикоснуться к токену. Это самый надёжный вариант аутентификации на сегодняшний день: даже полная компрометация вашего компьютера не даст злоумышленнику доступ к ключу.

Для тех, кому важна российская сертификация, есть Рутокен MFA — первый отечественный USB-токен с полноценной поддержкой FIDO2 (CTAP 2.1). Он работает с OpenSSH, но поддерживает только алгоритм ES256 на кривой P-256, поэтому генерировать нужно ecdsa-sk, а не ed25519-sk:

ssh-keygen -t ecdsa-sk

Если хотите, чтобы ключ хранился целиком на токене и его можно было использовать на любом компьютере (резидентный ключ с проверкой PIN), добавьте флаги:

ssh-keygen -t ecdsa-sk -O resident -O verify-required

При подключении потребуется ввести PIN и коснуться токена — два фактора в одном устройстве. Подробные инструкции по настройке — в документации Рутокен.

Установка ключа на сервер

Публичный ключ нужно доставить на сервер и добавить в файл ~/.ssh/authorized_keys. OpenSSH проверяет этот файл при каждом подключении: если приватный ключ клиента соответствует одному из публичных ключей в authorized_keys — доступ разрешён.

Самый простой способ — утилита ssh-copy-id, которая входит в стандартную поставку OpenSSH:

ssh-copy-id user@server.example.com

При выполнении команда запросит пароль (в последний раз!), подключится к серверу, создаст директорию ~/.ssh, если её нет, добавит ваш публичный ключ в authorized_keys и выставит корректные права. Всё автоматически.

Если ssh-copy-id недоступен (macOS без Homebrew, минимальные контейнеры), можно обойтись однострочником:

cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && \
    chmod 700 ~/.ssh && \
    cat >> ~/.ssh/authorized_keys && \
    chmod 600 ~/.ssh/authorized_keys"

После установки проверьте, что всё работает:

ssh user@server.example.com

Если ключ защищён парольной фразой, система спросит его. Если нет — вы сразу окажетесь на сервере. Пароль учётной записи больше не нужен.

Парольная фраза и ssh-agent: удобство без компромиссов

Приватный ключ — это файл на диске. Если злоумышленник получит доступ к вашему компьютеру (украдут ноутбук, взломают через уязвимость, или вы случайно закоммитите ключ в публичный репозиторий), он сможет использовать этот ключ для доступа ко всем серверам, где он установлен.

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

Но вводить пароль при каждом подключении утомительно. Здесь приходит на помощь ssh-agent — демон, который хранит расшифрованные ключи в памяти на время сессии:

# Запуск агента (обычно уже работает в современных дистрибутивах)
eval "$(ssh-agent -s)"

# Добавление ключа в агент (спросит passphrase один раз)
ssh-add ~/.ssh/id_ed25519

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

На macOS можно интегрировать ssh-agent с Keychain, чтобы passphrase запоминалась между перезагрузками:

ssh-add --apple-use-keychain ~/.ssh/id_ed25519

И добавить в ~/.ssh/config:

Host *
    UseKeychain yes
    AddKeysToAgent yes

Это разумный компромисс между безопасностью и удобством для личных машин с полнодисковым шифрованием.

ProxyJump: безопасный доступ к изолированным серверам

В реальных инфраструктурах серверы редко доступны из интернета напрямую. Базы данных, внутренние сервисы, системы мониторинга — всё это живёт в приватных сетях, недоступных извне. И это правильно: чем меньше поверхность атаки, тем лучше. Но администраторам и разработчикам нужен доступ. Как его организовать безопасно?

Классическое решение — bastion host (его также называют jump host или jump box). Это единственный сервер в инфраструктуре, который имеет публичный IP-адрес и принимает SSH-подключения из интернета. Все остальные серверы доступны только через него:

┌──────────────┐      SSH       ┌──────────────┐      SSH       ┌──────────────┐
│    Ваш ПК    │ ─────────────> │   Bastion    │ ─────────────> │   Private    │
│  (интернет)  │  (публичный)   │   (DMZ)      │  (приватная    │   Server     │
│              │                │              │      сеть)     │ (10.0.0.x)   │
└──────────────┘                └──────────────┘                └──────────────┘

Преимущества такой архитектуры очевидны: вместо того чтобы защищать десятки серверов от атак из интернета, вы защищаете только один. На нём можно настроить жёсткие правила аутентификации, вести детальное логирование и мониторинг. А приватные серверы вообще не видят внешний мир — атаковать их напрямую невозможно.

ProxyJump: современный способ

Начиная с OpenSSH 7.3 (август 2016) появился элегантный механизм ProxyJump. Флаг -J позволяет указать промежуточный сервер прямо в командной строке:

ssh -J bastion.example.com user@private-server.internal

SSH установит соединение с bastion, затем через него откроет соединение с private-server. Вся цепочка работает прозрачно — вы вводите одну команду и оказываетесь на целевом сервере. При этом bastion выступает лишь транзитным узлом: он не видит содержимое вашего трафика с целевым сервером, потому что шифрование работает от вашего клиента до конечной точки.

Но набирать длинные команды каждый раз утомительно. Гораздо удобнее описать инфраструктуру в файле ~/.ssh/config:

# Bastion host — единственная точка входа
Host bastion
    HostName bastion.example.com
    User admin
    IdentityFile ~/.ssh/bastion_key

# Шаблон для всех серверов в приватной сети 10.0.0.0/24
Host 10.0.0.*
    ProxyJump bastion
    User deploy
    IdentityFile ~/.ssh/deploy_key

# Конкретный сервер с удобным именем
Host db-primary
    HostName 10.0.0.5
    ProxyJump bastion
    User postgres

Теперь подключение к базе данных выглядит так:

ssh db-primary

Две буквы и Enter — вместо длинной команды с IP-адресами и флагами. SSH читает конфигурацию, видит, что нужен ProxyJump через bastion, автоматически строит цепочку соединений. Вы можете даже использовать scp и rsync — они тоже работают через ProxyJump без дополнительной настройки.

ProxyCommand: для сложных случаев

До ProxyJump существовал более низкоуровневый механизм ProxyCommand. Он всё ещё полезен в ситуациях, которые ProxyJump не покрывает:

Host private-server
    HostName 10.0.0.5
    User deploy
    ProxyCommand ssh -W %h:%p bastion.example.com

Директива ProxyCommand говорит SSH: «вместо прямого TCP-соединения выполни эту команду и используй её stdin/stdout как транспорт». Флаг -W %h:%p создаёт туннель к указанному хосту (%h) и порту (%p).

Зачем это нужно, если есть ProxyJump? Вот несколько сценариев:

Цепочка из нескольких bastion. Иногда архитектура требует прохода через два-три промежуточных сервера. ProxyJump поддерживает цепочки (-J bastion1,bastion2), но ProxyCommand даёт больше контроля над каждым звеном.

Подключение через SOCKS-прокси. Если у вас есть только SOCKS-прокси (например, Tor или корпоративный прокси), ProxyCommand позволяет использовать netcat:

Host server-via-tor
    HostName abcd1234.onion
    ProxyCommand nc -X 5 -x 127.0.0.1:9050 %h %p

Старые версии OpenSSH. Если вы работаете с системой, где OpenSSH старше 7.3, ProxyJump просто недоступен, и ProxyCommand — единственный вариант.

SSH Hardening: превращаем сервер в крепость

Настройки SSH по умолчанию рассчитаны на совместимость, а не на безопасность. Свежеустановленный OpenSSH принимает пароли, разрешает устаревшие алгоритмы и позволяет root-логин. Для боевого сервера такая конфигурация — приглашение к взлому. Давайте это исправим.

Философия защиты

Главный принцип SSH hardening — минимизация поверхности атаки. Всё, что не нужно явно, должно быть отключено. Все алгоритмы, которые не являются современными и проверенными, должны быть убраны. Все пользователи, кроме тех, кому действительно нужен доступ, должны быть заблокированы.

Прежде чем менять конфигурацию, полезно оценить текущее состояние. Утилита ssh-audit анализирует конфигурацию SSH-сервера и показывает, какие алгоритмы безопасны, какие устарели, а какие откровенно опасны:

# Установка
pip install ssh-audit

# Аудит сервера
ssh-audit server.example.com

Результат — цветной отчёт с рекомендациями. Удобный способ убедиться, что ваши изменения действительно улучшили безопасность.

Конфигурация /etc/ssh/sshd_config

Вот конфигурация, которую можно использовать как отправную точку для продакшен-сервера. Каждая директива осознана и обоснована:

# === Аутентификация ===
# Пароли отключаем категорически — они подбираются брутфорсом
PasswordAuthentication no
PubkeyAuthentication yes

# Root может входить только по ключу (или запретите полностью: PermitRootLogin no)
PermitRootLogin prohibit-password

# Явный список пользователей, которым разрешён SSH
AllowUsers deploy admin

# === Криптография ===
# Только современные алгоритмы обмена ключами
KexAlgorithms curve25519-sha256,ecdh-sha2-nistp256

# Только AEAD-шифры
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com

# Только современные MAC (encrypt-then-MAC)
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

# Предпочтительные алгоритмы host key
HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512

# === Защита от атак ===
# Максимум 3 попытки аутентификации за сессию
MaxAuthTries 3

# Отключать неактивные соединения (300 сек без активности × 2 проверки)
ClientAliveInterval 300
ClientAliveCountMax 2

# === Отключаем ненужное ===
# X11 forwarding — потенциальный вектор атаки
X11Forwarding no

# TCP forwarding — отключить, если не нужен (осторожно: ломает туннели!)
# AllowTcpForwarding no

# === Аудит ===
# Подробное логирование для расследования инцидентов
LogLevel VERBOSE

Обратите внимание на закомментированную директиву AllowTcpForwarding no. Если вы используете SSH-туннели (port forwarding), её включение сломает эту функциональность. Включайте только если уверены, что туннели не нужны.

За подробными рекомендациями по криптографическим алгоритмам обращайтесь к Mozilla OpenSSH Guidelines — это один из лучших публичных ресурсов по теме, который регулярно обновляется.

Применение изменений без самовыпиливания

Редактируя конфигурацию, легко заблокировать себя навсегда. Одна опечатка, одна неверная директива — и сервер перестаёт пускать кого-либо. Чтобы этого избежать, следуйте железному правилу: всегда держите открытую сессию SSH, пока тестируете новые настройки.

Алгоритм безопасного применения:

# 1. Проверить синтаксис конфигурации (это не проверит логические ошибки!)
sudo sshd -t

# 2. Перезагрузить sshd БЕЗ закрытия текущих сессий
sudo systemctl reload sshd

# 3. В ДРУГОМ терминале попробовать новое подключение
ssh user@server

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

Fail2Ban: автоматическая защита от брутфорса

Даже с отключёнными паролями боты будут стучаться в SSH. Тысячи попыток в час — обычное дело для сервера с публичным IP. Fail2Ban автоматически блокирует IP-адреса, с которых идут подозрительные запросы:

sudo apt install fail2ban

Создайте файл /etc/fail2ban/jail.local (не редактируйте jail.conf — он перезапишется при обновлении):

[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log
maxretry = 3      # После 3 неудачных попыток...
findtime = 600    # ...в течение 10 минут...
bantime = 3600    # ...заблокировать IP на 1 час

Fail2Ban парсит логи SSH и добавляет правила iptables/nftables, блокирующие нарушителей. После перезапуска (sudo systemctl restart fail2ban) проверьте статус:

sudo fail2ban-client status sshd

Мониторинг: знать, что происходит

Защита — это не только предотвращение, но и обнаружение. Бессмысленно строить крепость, если вы не знаете, что происходит у её стен. Регулярно проверяйте, кто подключается к серверу:

# Кто сейчас залогинен?
who

# Более детальная информация (что делают, сколько простаивают)
w

# Активные SSH-соединения на уровне сокетов
ss -tnp | grep sshd

Анализ логов помогает выявить атаки и подозрительную активность:

# Успешные входы за последние сутки
grep "Accepted" /var/log/auth.log | tail -50

# Неудачные попытки (индикатор брутфорса)
grep "Failed password" /var/log/auth.log | tail -50

# Попытки входа под несуществующими пользователями
grep "Invalid user" /var/log/auth.log | tail -50

# На системах с systemd — удобный просмотр логов
journalctl -u sshd --since "1 hour ago"

Если вы хотите получать уведомления о каждом входе, создайте скрипт /etc/profile.d/ssh-alert.sh:

#!/bin/bash
if [ -n "$SSH_CLIENT" ]; then
    IP=$(echo $SSH_CLIENT | awk '{print $1}')
    HOSTNAME=$(hostname)
    USER=$(whoami)
    DATE=$(date '+%Y-%m-%d %H:%M:%S')

    echo "SSH login detected:
    User: $USER
    From: $IP
    Server: $HOSTNAME
    Time: $DATE" | mail -s "SSH Alert: $USER@$HOSTNAME from $IP" security@example.com
fi

Теперь при каждом SSH-входе на почту будет приходить уведомление. Это не заменяет полноценный SIEM, но для небольших инфраструктур — простое и эффективное решение.

Port Forwarding: туннели на все случаи жизни

Мы уже говорили о ProxyJump — способе добраться до серверов через bastion. Но SSH умеет гораздо больше: он может превратить любое соединение в зашифрованный туннель. Эта возможность называется port forwarding, и она невероятно полезна в повседневной работе.

Представьте: у вас есть база данных, которая по соображениям безопасности принимает соединения только с localhost. Или веб-сервис, работающий на порту, заблокированном корпоративным файрволом. Или вы сидите в кафе с открытым Wi-Fi и хотите защитить весь свой трафик. SSH port forwarding решает все эти задачи.

Local Port Forwarding (-L)

Сценарий: вы хотите подключиться к PostgreSQL на сервере db-server, который доступен только из внутренней сети. У вас есть SSH-доступ к bastion.

Синтаксис: ssh -L [локальный_порт]:[целевой_хост]:[целевой_порт] [ssh_сервер]

ssh -L 5432:db-server:5432 user@bastion

Что происходит:

┌─────────────┐      SSH туннель     ┌─────────────┐      ┌─────────────┐
│ Ваш ПК      │ ===================> │   Bastion   │ ---> │  db-server  │
│ :5432       │     (зашифровано)    │             │      │  :5432      │
└─────────────┘                      └─────────────┘      └─────────────┘
  1. SSH открывает порт 5432 на вашем компьютере.
  2. Всё, что придёт на localhost:5432, шифруется и отправляется через SSH на bastion.
  3. Bastion пересылает трафик на db-server:5432.

Использование:

psql -h localhost -p 5432 -U dbuser mydatabase
# Подключение идёт через туннель к db-server

Если вам нужен туннель в фоновом режиме (без интерактивной сессии на bastion), добавьте флаги -N (не выполнять удалённые команды) и -f (уйти в фон после аутентификации):

ssh -N -f -L 5432:db-server:5432 user@bastion

Remote Port Forwarding (-R)

Сценарий: у вас локально запущен веб-сервер на порту 8000. Вы хотите показать его коллеге, но вы за NAT и ваш компьютер недоступен из интернета. Зато у вас есть SSH-доступ к публичному серверу.

Синтаксис: ssh -R [удалённый_порт]:[локальный_хост]:[локальный_порт] [ssh_сервер]

ssh -R 8080:localhost:8000 user@public-server

Что происходит:

┌─────────────┐      SSH туннель     ┌─────────────┐
│ Ваш ПК      │ <=================== │ public-srv  │ <--- Интернет
│ :8000       │     (зашифровано)    │ :8080       │
└─────────────┘                      └─────────────┘

Теперь http://public-server:8080 перенаправляется на ваш localhost:8000. Коллеги могут открыть ваш сайт в браузере, а вы при этом не выставляете свой компьютер в интернет.

Примечание: на сервере может потребоваться директива GatewayPorts yes в /etc/ssh/sshd_config, чтобы порт был доступен не только с localhost сервера, но и извне.

Dynamic Port Forwarding (-D) — SOCKS Proxy

Сценарий: вы хотите направить весь браузерный трафик через SSH-сервер — для защиты в публичной Wi-Fi сети или для доступа к ресурсам, доступным только из сети сервера.

Синтаксис: ssh -D [локальный_порт] [ssh_сервер]

ssh -D 1080 user@proxy-server

Что происходит:

SSH создаёт SOCKS5 прокси на localhost:1080. Любое приложение, настроенное на этот прокси, будет отправлять трафик через SSH-туннель. В отличие от -L, где вы пробрасываете конкретный порт к конкретному серверу, -D создаёт универсальный прокси — через него можно обращаться к любому адресу.

Настройка браузера (Firefox):

  1. Настройки → Сеть → Параметры соединения.
  2. Ручная настройка прокси.
  3. SOCKS Host: 127.0.0.1, Port: 1080.
  4. Выбрать SOCKS v5.

Или через командную строку:

# curl через SOCKS
curl --socks5 localhost:1080 https://ifconfig.me

# Должен показать IP proxy-server, а не ваш

Автоматическое поддержание туннелей

SSH-туннели имеют неприятную особенность: они разрываются при нестабильном соединении. Если вы используете туннели на постоянной основе (например, для доступа к базе данных), стоит обратить внимание на autossh — утилиту, которая мониторит SSH-соединение и автоматически перезапускает его при обрыве:

autossh -M 0 -N -f -L 5432:db-server:5432 user@bastion

Флаг -M 0 отключает встроенный мониторинг autossh в пользу механизма ServerAliveInterval/ServerAliveCountMax самого SSH — это более надёжный подход в современных версиях OpenSSH. Добавьте в ~/.ssh/config:

Host bastion
    ServerAliveInterval 30
    ServerAliveCountMax 3

И ваши туннели будут жить столько, сколько нужно.

Вывод

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

Повторю главное:

  1. Используйте ключи, а не пароли. Ed25519 — оптимальный выбор. Защищайте приватный ключ passphrase и используйте ssh-agent для удобства. Если безопасность критична — переходите на аппаратные FIDO2-ключи.
  2. Не игнорируйте fingerprint. Предупреждение SSH о смене host key — это не «назойливое сообщение», а сигнал о потенциальной атаке.
  3. Настройте ~/.ssh/config. ProxyJump, алиасы серверов, разные ключи для разных хостов — конфиг-файл превращает SSH из утилиты командной строки в удобный инструмент управления инфраструктурой.
  4. Hardening — не опция, а необходимость. Отключите пароли, ограничьте алгоритмы, настройте Fail2Ban, мониторьте логи. Используйте ssh-audit, чтобы проверить результат.
  5. Освойте туннели. Local, remote и dynamic port forwarding — это три инструмента, которые решают десятки практических задач: от безопасного доступа к базам данных до защиты трафика в публичных сетях.

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

Материалы для изучения

  • OpenSSH — официальный сайт проекта — документация, release notes, man pages. Первоисточник по всем вопросам, связанным с реализацией и конфигурацией.
  • SSH Academy — образовательный ресурс от компании Тату Юлёнена, создателя SSH. Хорошо структурированная база знаний по протоколу, ключам, туннелям и лучшим практикам.
  • Mozilla OpenSSH Guidelines — рекомендации Mozilla по безопасной настройке OpenSSH-сервера и клиента. Конкретные конфигурации с обоснованием каждого выбора алгоритма. Регулярно обновляется.
  • ssh-audit — утилита для аудита безопасности SSH-сервера. Показывает поддерживаемые алгоритмы, выявляет слабые места и даёт рекомендации по улучшению.
  • Securing SSH with FIDO2 (Yubico) — руководство по настройке SSH-аутентификации с аппаратными ключами. Актуально для OpenSSH 8.2 и новее.
  • Рутокен MFA — настройка SSH с FIDO2 — пошаговая инструкция по генерации ecdsa-sk ключей с резидентным хранением и проверкой PIN. Для тех, кому нужен отечественный аппаратный токен с поддержкой FIDO2.
  • Fail2Ban — официальный сайт системы защиты от брутфорса. Документация по настройке jail-файлов, фильтров и интеграции с различными сервисами.
  • autossh — утилита для автоматического поддержания SSH-соединений. Незаменима для persistent-туннелей, которые должны работать круглосуточно.
  • RFC 4253, RFC 4252, RFC 4254 — спецификации трёх слоёв SSH-2: транспорт, аутентификация и соединение. Для тех, кто хочет понять протокол на уровне байтов.