Защита SSH-доступа к 200 серверам логистической компании

Обращение клиента и исходная ситуация

В феврале 2026 года к нам обратилась логистическая компания «ЛогиТранс» — крупный оператор грузоперевозок с филиалами в 14 городах России. Инфраструктура компании насчитывала 200 серверов: 120 Linux-машин в трёх дата-центрах, 50 виртуальных серверов в облаке и 30 edge-узлов на складских терминалах.

Проблема была серьёзной. В январе служба безопасности зафиксировала несанкционированный доступ к одному из серверов через SSH. Расследование показало:

  • 87% серверов использовали парольную аутентификацию SSH, причём пароли передавались между администраторами в мессенджерах.
  • Единый root-пароль использовался на 40+ серверах одновременно.
  • Отсутствие журналирования — невозможно установить, кто и когда подключался к серверу.
  • Открытый 22 порт на всех серверах — брутфорс-атаки генерировали до 50 000 попыток в сутки на каждый сервер.

Руководство «ЛогиТранс» поставило задачу: в течение 6 недель полностью перестроить систему SSH-доступа, исключив возможность повторения инцидента.

Аудит и план работ

Наша команда из трёх инженеров провела комплексный аудит SSH-инфраструктуры за первую неделю. Мы проверили каждый сервер по чеклисту из 28 пунктов и составили карту уязвимостей.

Результаты аудита были неутешительными:

ПараметрТекущее состояниеЦелевое состояние
АутентификацияПароли (87% серверов)Только ключи ED25519
Порт SSH22 (100% серверов)Нестандартный + Port Knocking
Root-доступРазрешён (92% серверов)Запрещён, через sudo
ЖурналированиеСтандартное (syslog)Централизованное + алерты
Управление ключамиРучноеЦентрализованное через Ansible
Доступ к edge-узламПрямой из интернетаЧерез jump-хост с ProxyCommand

Мы разбили проект на четыре этапа: генерация и распределение ключей, настройка серверов, построение jump-хост архитектуры, внедрение мониторинга и аудита.

Переход на ключевую аутентификацию

Первым делом мы выстроили систему управления SSH-ключами. Для каждого из 18 администраторов «ЛогиТранс» были сгенерированы персональные ключевые пары ED25519 — более современный и безопасный алгоритм по сравнению с RSA:

# Генерация ключа ED25519 с комментарием для идентификации
ssh-keygen -t ed25519 -C "ivanov@logitrans.ru" -f ~/.ssh/id_ed25519_logitrans

# Установка парольной фразы на существующий ключ
ssh-keygen -p -f ~/.ssh/id_ed25519_logitrans

Для массового развёртывания ключей на 200 серверов мы написали Ansible-плейбук, который выполнял три задачи: копировал публичные ключи авторизованных администраторов, устанавливал корректные права на файлы и отключал парольную аутентификацию:

# deploy_ssh_keys.yml
- hosts: all_servers
  become: yes
  tasks:
    - name: Создаём директорию .ssh
      file:
        path: "/home/{{ item.user }}/.ssh"
        state: directory
        mode: '0700'
        owner: "{{ item.user }}"
      loop: "{{ ssh_users }}"

    - name: Развёртываем авторизованные ключи
      authorized_key:
        user: "{{ item.0.user }}"
        key: "{{ item.1 }}"
        state: present
        exclusive: yes
      loop: "{{ ssh_users | subelements('keys') }}"

    - name: Отключаем парольную аутентификацию
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?PasswordAuthentication'
        line: 'PasswordAuthentication no'
      notify: restart sshd

    - name: Отключаем root-логин
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?PermitRootLogin'
        line: 'PermitRootLogin no'
      notify: restart sshd

Развёртывание прошло поэтапно: сначала тестовая группа из 10 серверов, затем — ежедневно по 30-40 серверов. На каждом этапе мы проверяли, что все администраторы могут подключиться по ключам, прежде чем отключать пароли.

Защита приватных ключей

Мы настроили SSH-агент на рабочих станциях администраторов с автоматической выгрузкой ключей через 8 часов — по окончании рабочего дня:

# Добавление ключа с таймаутом жизни 8 часов (28800 секунд)
ssh-add -t 28800 ~/.ssh/id_ed25519_logitrans

# Проверка загруженных ключей
ssh-add -l

Для каждого администратора был составлен индивидуальный SSH-конфиг, позволяющий подключаться к серверам по коротким алиасам:

# ~/.ssh/config — пример для администратора Иванова

Host *
    IdentityFile ~/.ssh/id_ed25519_logitrans
    ServerAliveInterval 60
    ServerAliveCountMax 3
    Compression yes
    AddKeysToAgent yes

Host jump-msk
    HostName 10.0.1.5
    User ivanov
    Port 2222

Host db-prod-*
    User ivanov
    Port 2222
    ProxyJump jump-msk

Host db-prod-1
    HostName 10.10.1.11

Host db-prod-2
    HostName 10.10.1.12

Теперь вместо запоминания IP-адресов и паролей администратору достаточно набрать ssh db-prod-1.

Архитектура jump-хостов и ProxyCommand

Ключевым архитектурным решением стало внедрение трёх jump-хостов (бастионов) — по одному в каждом дата-центре. Все 200 серверов были закрыты от прямого SSH-доступа из интернета. Единственный путь — через бастион.

Архитектура выглядит так:

# Схема доступа:
# Админ → [Интернет] → Jump-хост (бастион) → [Внутренняя сеть] → Целевой сервер
#
# Jump-хосты:
#   jump-msk.logitrans.ru:2222  — Москва (80 серверов)
#   jump-spb.logitrans.ru:2222  — Санкт-Петербург (70 серверов)
#   jump-nsk.logitrans.ru:2222  — Новосибирск (50 серверов)

Для прозрачного подключения через бастион мы использовали директиву ProxyCommand, которая устанавливает зашифрованный туннель через промежуточный сервер:

# Подключение через бастион — трафик шифруется на всём пути
# Бастион не имеет доступа к содержимому сессии

Host warehouse-*
    User admin
    Port 2222
    ProxyCommand ssh -W %h:%p jump-msk

Host warehouse-01
    HostName 10.20.1.1
Host warehouse-02
    HostName 10.20.1.2
Host warehouse-03
    HostName 10.20.1.3

Принципиальное преимущество ProxyCommand перед agent forwarding (ssh -A): бастион-сервер выступает только транспортом. Даже если бастион скомпрометирован, злоумышленник не сможет перехватить данные сессии или использовать ключ администратора для подключения к другим серверам.

Hardening бастионов

На jump-хостах мы реализовали многоуровневую защиту:

# /etc/ssh/sshd_config на бастионе
Port 2222
ListenAddress 0.0.0.0

# Только ключевая аутентификация
PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes

# Запрет root-доступа
PermitRootLogin no

# Разрешённые пользователи
AllowGroup ssh-admins

# Ограничение forwarding
AllowTcpForwarding yes
X11Forwarding no
AllowStreamLocalForwarding no

# Ограничение по времени
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30
MaxAuthTries 3
MaxSessions 5

# Усиленные алгоритмы
KexAlgorithms curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

Дополнительно мы установили fail2ban с агрессивными правилами: 3 неудачных попытки — бан IP на 24 часа. За первый месяц работы fail2ban заблокировал более 12 000 уникальных IP-адресов.

SSH-туннели для безопасного доступа к сервисам

Помимо доступа к серверам, администраторам «ЛогиТранс» требовался доступ к веб-интерфейсам внутренних сервисов: Grafana, PostgreSQL, внутренний ERP. Ранее эти порты были открыты в интернет. Мы закрыли всё и настроили доступ через SSH-туннели.

# Проброс порта: доступ к Grafana (порт 3000) через туннель
ssh -L 127.0.0.1:3000:grafana.internal:3000 jump-msk
# Теперь Grafana доступна на http://localhost:3000

# Проброс к PostgreSQL
ssh -L 127.0.0.1:5432:db-prod-1.internal:5432 jump-msk
# Подключение: psql -h localhost -p 5432 -U analytics

# SOCKS-прокси для доступа ко всей внутренней сети
ssh -D 127.0.0.1:8080 -C jump-msk
# Настроить браузер на SOCKS5 прокси localhost:8080

Для удобства мы прописали часто используемые туннели прямо в SSH-конфиг:

Host tunnel-grafana
    HostName 10.0.1.5
    User ivanov
    Port 2222
    LocalForward 127.0.0.1:3000 grafana.internal:3000
    LocalForward 127.0.0.1:9090 prometheus.internal:9090
    RequestTTY no
    ExitOnForwardFailure yes

Теперь администратору достаточно набрать ssh tunnel-grafana — и все нужные сервисы доступны на localhost.

Reverse-туннели для складских терминалов

Отдельная задача — 30 edge-серверов на складах. Они находились за NAT без выделенных IP-адресов. Прямое подключение к ним было невозможно. Мы решили проблему через reverse SSH-туннели:

# На edge-сервере (склад) — systemd-сервис для автоподключения
# /etc/systemd/system/ssh-reverse-tunnel.service
[Unit]
Description=Reverse SSH Tunnel to Bastion
After=network-online.target
Wants=network-online.target

[Service]
User=tunnel
ExecStart=/usr/bin/ssh -N -R 127.0.0.1:10001:127.0.0.1:2222 \
    -o ServerAliveInterval=30 \
    -o ServerAliveCountMax=3 \
    -o ExitOnForwardFailure=yes \
    tunnel@jump-msk.logitrans.ru -p 2222
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Каждому складскому серверу был назначен уникальный порт (10001-10030) на бастионе. Для подключения администратор вводит:

# Подключение к складу №7 через reverse-туннель
ssh -p 10007 admin@127.0.0.1 -J jump-msk

Централизованный аудит и мониторинг

Последний этап — внедрение системы аудита SSH-подключений. Мы развернули централизованный syslog-сервер на базе rsyslog и настроили все 200 серверов на отправку SSH-логов:

# /etc/rsyslog.d/ssh-audit.conf на каждом сервере
if $programname == 'sshd' then @@syslog.logitrans.internal:514
& stop

На сервере сбора логов мы написали Python-скрипт, который парсит события в реальном времени и отправляет алерты в Telegram при подозрительной активности:

  • Подключение в нерабочее время (после 21:00 или до 7:00)
  • Подключение с нового IP-адреса
  • Множественные неудачные попытки аутентификации
  • Использование sudo для критичных команд

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

Безопасная передача файлов

Отдельным блоком мы проработали безопасную передачу файлов между серверами. Ранее администраторы использовали FTP (!) для копирования конфигураций и бэкапов. Мы полностью заменили его на SCP и SSHFS.

# Копирование файла на сервер через бастион
scp -o ProxyJump=jump-msk backup.tar.gz admin@db-prod-1:/backups/

# Копирование с сервера
scp -o ProxyJump=jump-msk admin@db-prod-1:/var/log/app.log ./

# Массовое копирование с сжатием
tar -czf - /etc/nginx/ | ssh -J jump-msk admin@web-01 "cd /backup && tar -xzf -"

Для отдела разработки, которому требовался постоянный доступ к логам на серверах, мы настроили SSHFS-монтирование:

# Монтирование удалённой директории через SSHFS
sshfs -o ProxyJump=jump-msk,reconnect,ServerAliveInterval=15 \
    admin@web-01:/var/log/app /mnt/web01-logs

# Размонтирование
fusermount -u /mnt/web01-logs

Подробнее о нашем подходе к безопасности серверной инфраструктуры читайте на itfresh.ru.

Результаты проекта

Проект был завершён за 5 недель — на неделю раньше дедлайна. Итоговые показатели:

ПоказательДо проектаПосле внедрения
Серверы с парольной аутентификацией174 (87%)0 (0%)
Серверы с открытым root-доступом184 (92%)0 (0%)
Серверы с прямым SSH из интернета200 (100%)3 (только бастионы)
Среднее количество брутфорс-попыток в сутки50 000 на сервер~200 на бастион
Время подключения администратора~45 сек (ввод пароля)~3 сек (ключ + алиас)
Инциденты безопасности SSH3 за квартал0 за 2 месяца

Помимо безопасности, администраторы отметили значительное повышение удобства работы. SSH-конфиги с алиасами, туннели одной командой, SSHFS вместо FTP — всё это экономит каждому специалисту около 40 минут в день.

Общая стоимость проекта составила 1.8 млн рублей, включая аудит, внедрение и обучение персонала. Для сравнения: ущерб от январского инцидента оценивался в 4.2 млн рублей.

Часто задаваемые вопросы

ED25519 обеспечивает более высокую криптографическую стойкость при меньшей длине ключа (256 бит против 4096 бит у RSA). Операции с ED25519 выполняются быстрее, а ключи не подвержены атакам на основе слабых случайных чисел, которые могут скомпрометировать RSA.
При использовании ssh -A (agent forwarding) скомпрометированный промежуточный сервер может использовать ваш SSH-агент для подключения к другим серверам от вашего имени. ProxyCommand создаёт сквозной зашифрованный туннель — промежуточный сервер видит только зашифрованный трафик и не имеет доступа к ключам.
Используйте reverse SSH-туннели: сервер за NAT устанавливает исходящее соединение к бастиону и пробрасывает свой SSH-порт. Администратор подключается к бастиону, а оттуда — к проброшенному порту. Для надёжности оформите это как systemd-сервис с автоматическим перезапуском.
Мы рекомендуем порты выше 1024 — например, 2222 или 22222. Некоторые компании используют порт 443 (HTTPS), что позволяет обойти корпоративные файрволы, блокирующие нестандартные порты. Смена порта не является мерой безопасности сама по себе, но значительно снижает объём автоматического сканирования.
Стоимость зависит от масштаба инфраструктуры. Для компании с 200 серверами проект обошёлся в 1.8 млн рублей за 5 недель работы. Для небольшой инфраструктуры (10-30 серверов) подобный проект можно реализовать за 200-400 тыс. рублей и 1-2 недели.

Нужна помощь с проектом?

Специалисты АйТи Фреш помогут с архитектурой, DevOps, безопасностью и разработкой — 15+ лет опыта

📞 Связаться с нами
#безопасность SSH#SSH ключи#SSH туннелирование#ProxyCommand SSH#защита серверов#SSH аудит#управление доступом#SSH конфигурация
Комментарии 0

Оставить комментарий

загрузка...