Организация безопасного SSH-доступа для нескольких пользователей

Nov 10, 2025 мин чтения

Задача

Сервер использует Debian 12 в качестве операционной системы. Необходимо организовать безопасный доступ для нескольких пользователей, работающих по SSH. Требуется реализовать усиленную аутентификацию (двухэтапную или многофакторную), а также защиту от перебора паролей. Необходимо также защитить сервер от ботов, сканирующих сети с целью поиска открытых портов, в данном случае порта SSH. У каждого пользователя должна быть отдельная учётная запись на сервере.

Пояснение

Данная задача может быть решена несколькими способами. Будет рассмотрено 3 варианта:

  • Отдельный пользователь и вход по SSH-ключам с использованием firewall и ограниченного shell
  • Отдельный пользователь и вход по паролю с использованием firewall, Fail2Ban и ограниченного shell
  • Использование TOTP-ключей в сочетании с firewall и Fail2Ban

Приведённые варианты являются примерами решений конкретной задачи.
В зависимости от требований и модели угроз такие настройки могут быть как избыточными, так и недостаточными.
Перед внедрением всегда оценивайте риски и требования к безопасности.

Практическая реализация

Вариант 1

Отдельный пользователь, вход по ключам, firewall и ограниченный shell.

Для повышения безопасности на сервере был создан отдельный пользователь, предназначенный исключительно для SSH-подключений.

adduser ssh-user

Для данного пользователя был написан скрипт, который при авторизации позволяет только выполнить смену пользователя (su - user). Это исключает возможность выполнения произвольных команд сразу после подключения.
Файл restricted-shell:

#!/bin/bash

echo "Это ограниченный shell."
echo "Введите: su <user>  или  su - [user]"

while echo -n "restricted> " && read -r c a b; do
    [[ "$c" == "su" ]] || { echo "Ошибка: разрешена только команда 'su'"; continue; }

    [[ "$a" != "-" && -n "$a" ]] && exec su - "$a"
    [[ "$a" == "-" && -n "$b" ]] && exec su - "$b"
    [[ "$a" == "-" && -z "$b" ]] && exec su - root
done

Ограничение оболочки

Чтобы при входе по SSH автоматически запускался данный скрипт, он был установлен в качестве login shell для пользователя ssh-user с помощью команды usermod.

chmod +x /usr/bin/restricted-shell
usermod -s /usr/bin/restricted-shell ssh-user

Таким образом, вместо стандартной оболочки bash используется минимальная оболочка, не предоставляющая полноценного доступа к системе.

Аутентификация по ключам

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

ssh-keygen -t ed25519 -c "user1 key"

Открытые ключи были добавлены на сервер в файл:

/home/ssh-user/.ssh/authorized_keys

Это можно сделать вручную либо с помощью команды ssh-copy-id.

ssh-copy-id -i путь_к_ключу -p порт_SSH ssh-user@IP_адрес_сервера 

Для каждого пользователя рекомендуется использовать отдельную пару ключей и добавлять к ним комментарии (например, имя сотрудника). Такой подход позволяет:

  • точно определить, какой ключ использовался при входе
  • оперативно отозвать скомпрометированный ключ
  • не нарушать доступ остальных пользователей

Конфигурация SSH

Конфигурация sshd_config
На данном фрагменте часть конфигурации, остальное можно оставить стандартно

Ключевые параметры конфигурации:

  • ForceCommand — принудительный запуск ограниченного скрипта
  • Port 2025 — изменение стандартного порта SSH
  • LogLevel VERBOSE — расширенное логирование (в production лучше использовать INFO)
  • AllowUsers ssh-user — разрешение входа только для специального пользователя
  • PermitRootLogin no — запрет входа под root
  • MaxAuthTries — ограничение количества попыток аутентификации
  • PasswordAuthentication no — запрет входа по паролю
  • PubkeyAuthentication yes — разрешение аутентификации по ключам

После изменения конфигурации SSH-сервис необходимо перезапустить.

systemctl restart ssh

Аудит подключений

Для просмотра информации о входах по ключам можно использовать:

journalctl -u ssh | grep "Accepted publickey"

Команда выводит IP-адреса и отпечатки использованных ключей.

Чтобы определить, какому пользователю принадлежит отпечаток ключа:

ssh-keygen -lf <(cat /home/ssh-user/.ssh/authorized_keys)

Это позволяет сопоставить ключи из логов с ключами в authorized_keys.

Настройка firewall

Для дополнительной защиты был настроен firewall (ufw). Открыт только один порт — SSH:

ufw allow from 192.168.136.1 to any port 2025 proto tcp

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

Результат

В результате реализована двухфакторная аутентификация:

  • наличие приватного SSH-ключа пользователя
  • знание пароля от своей учетной записи

Подключение возможно только через пользователя ssh-user, оболочка которого не позволяет выполнять команды напрямую.

Вариант 2

Отдельный пользователь, вход по паролю, firewall, Fail2Ban и ограниченный shell.

Данный вариант аналогичен первому по структуре, но использует парольную аутентификацию вместо SSH-ключей.

Для входа также используется отдельный пользователь с ограниченной оболочкой и отключённым доступом под root.

Особенности безопасности

Данный подход менее безопасен, так как для аутентификации достаточно знания пароля пользователя ssh-user и собственного пароля. Компенсацией этого недостатка служит настройка Fail2Ban, который защищает сервер от перебора паролей.

Создание пользователя, предназначенного исключительно для SSH-подключений:

adduser ssh-user

Для данного пользователя был написан скрипт, который при авторизации позволяет только выполнить смену пользователя (su - user). Это исключает возможность выполнения произвольных команд сразу после подключения. Файл restricted-shell:

#!/bin/bash

echo "Это ограниченный shell."
echo "Введите: su <user>  или  su - [user]"

while echo -n "restricted> " && read -r c a b; do
    [[ "$c" == "su" ]] || { echo "Ошибка: разрешена только команда 'su'"; continue; }

    [[ "$a" != "-" && -n "$a" ]] && exec su - "$a"
    [[ "$a" == "-" && -n "$b" ]] && exec su - "$b"
    [[ "$a" == "-" && -z "$b" ]] && exec su - root
done

Ограничение оболочки

Чтобы при входе по SSH автоматически запускался данный скрипт, он был установлен в качестве login shell для пользователя ssh-user с помощью команды usermod.

chmod +x /usr/bin/restricted-shell
usermod -s /usr/bin/restricted-shell ssh-user

Таким образом, вместо стандартной оболочки bash используется минимальная оболочка, не предоставляющая полноценного доступа к системе.

Конфигурация SSH

Конфигурация отличается от предыдущей только тем, что в ней разрешено использование входа по паролю

Конфигурация sshd_config
На данном фрагменте часть конфигурации, остальное можно оставить стандартно

Ключевые параметры конфигурации:

  • ForceCommand — принудительный запуск ограниченного скрипта
  • Port 2025 — изменение стандартного порта SSH
  • LogLevel VERBOSE — расширенное логирование (в production лучше использовать INFO)
  • AllowUsers ssh-user — разрешение входа только для специального пользователя
  • PermitRootLogin no — запрет входа под root
  • MaxAuthTries — ограничение количества попыток аутентификации
  • PasswordAuthentication yes — разрешение входа по паролю

После изменения конфигурации SSH-сервис необходимо перезапустить.

systemctl restart ssh

Настройка Fail2Ban

Конфигурация Fail2Ban:

Конфигурация Fail2Ban

При превышении заданного количества неудачных попыток входа (в данном случае — 3) IP-адрес автоматически блокируется.

Fail2Ban test

Блокировка реализуется через iptables. Fail2Ban добавляет правило, запрещающее доступ с подозрительного IP на заданное время (например, 1 час).

Fail2Ban iptables

Управление блокировками

Ручная разблокировка IP-адреса:

fail2ban-client set sshd unbanip 192.168.136.1

Ручная блокировка:

fail2ban-client set sshd banip 192.168.136.1

Результат

В результате реализован доступ по SSH с двухэтапной аутентификацией:

  • пароль от пользователя ssh-user
  • пароль от собственной учетной записи

Дополнительно сервер защищён от brute-force атак с помощью Fail2Ban.

Вариант 3

Google Authenticator (2FA) + firewall + Fail2Ban

В данном варианте реализована полноценная двухфакторная аутентификация с использованием одноразовых кодов (TOTP) от Google Authenticator.

Настройка Google Authenticator

Установка:

apt install libpam-google-authenticator

Для генерации необходимо использовать команду:

google-authenticator

Пример:
Генерация totp

Секретный ключ нужно добавить в приложение на телефоне:

Добавление totp

После этого необходимо донастроить конфигурацию Google Authenticator

Генерация totp 2

Конфигурация SSH

Основное отличие от предыдущих способов в добавлении строки:

ChallengeResponseAuthentication yes

Конфигурация SSH

На данном фрагменте часть конфигурации, остальное можно оставить стандартно

Ключевые параметры конфигурации:

  • Port 2025 — изменение стандартного порта SSH
  • LogLevel VERBOSE — расширенное логирование (в production лучше использовать INFO)
  • PermitRootLogin no — запрет входа под root
  • MaxAuthTries — ограничение количества попыток аутентификации
  • PasswordAuthentication yes — разрешение входа по паролю
  • ChallengeResponseAuthentication yes - включает интерактивный режим аутентификации в SSH, необходимый для работы PAM-модулей

Настройка PAM-модуля

Настроен таким образом что сначала нужно ввести пароль от пользователя, а затем код из приложения

Pam config

Как это выглядит в терминале:

Pam config

Настройка Fail2Ban

Конфигурация Fail2Ban:

Конфигурация Fail2Ban

При превышении заданного количества неудачных попыток входа (в данном случае — 3) IP-адрес автоматически блокируется.

Fail2Ban test

Блокировка реализуется через iptables. Fail2Ban добавляет правило, запрещающее доступ с подозрительного IP на заданное время (например, 1 час).

Fail2Ban iptables

Управление блокировками

Ручная разблокировка IP-адреса:

fail2ban-client set sshd unbanip 192.168.136.1

Ручная блокировка:

fail2ban-client set sshd banip 192.168.136.1

Дополнительно

Для автоматизации создания пользователей и генерации кодов был написан скрипт:

#!/bin/bash
USER="$1"
PASSWORD=$(< /dev/urandom tr -dc A-Za-z0-9 | head -c10;)
if [[ -n "$USER" ]]; then
        id "$USER" &>/dev/null || useradd -m -s /bin/bash "$USER"
        echo "$USER:$PASSWORD" | chpasswd
        su -l "$USER" -c "google-authenticator -t -d -r 3 -R 30 -w 3 -Q ansi -f -C > /dev/null"
        SECRET=$(head -n1 /home/"$USER"/.google_authenticator)
        echo "────────────────────────────────────"
        echo "Сотрудник : $USER"
        echo "Ключ TOTP   : $SECRET"
        echo "Пароль    : $PASSWORD"
        echo "────────────────────────────────────"
else
        echo "Задайте USERNAME, например: new-user.sh user1"
fi

При его запуске требуется указать нового пользователя, в данном случае test1:

totp test

В результате скрипт выводит логин пользователя, ключ TOTP который нужно добавить на устройство, а также сгенерированный пароль для пользователя

Результат

В данном варианте реализована полноценная многофакторная аутентификация для доступа по SSH.

Для успешного входа пользователю необходимо:

  • знать пароль своей учетной записи
  • иметь доступ к устройству с приложением Google Authenticator
  • ввести корректный одноразовый TOTP-код

Дополнительно сервер защищён от перебора паролей и одноразовых кодов с помощью Fail2Ban, который временно блокирует IP-адреса при превышении допустимого количества неудачных попыток входа.

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

Недостатком данного подхода является повышенная сложность администрирования и внедрения такого способа, так как большинство пользователей негативно относятся к долгой аутентификации и привыкли использовать простые и короткие пароли.

Сравнение вариантов

Критерий Вариант 1 Вариант 2 Вариант 3
Тип аутентификации SSH-ключ + пароль Пароль + пароль Пароль + TOTP
Уровень безопасности Высокий Средний Очень высокий
Устойчивость к brute-force Высокая Средняя Очень высокая
Защита при утечке пароля Частичная Отсутствует Полная
Требования к пользователям SSH-ключ Только пароль Пароль + мобильное устройство
Удобство для пользователей Среднее Среднее Среднее / низкое
Сложность администрирования Средняя Низкая Высокая
Масштабируемость Хорошая Средняя Средняя
Рекомендуемый сценарий Администраторы, DevOps Внутренние сервисы Критически важные системы

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