В практике каждого вебмастера, тимлида или системного администратора наступает момент, когда к файлам одного конкретного сайта на VDS требуется предоставить доступ стороннему исполнителю: верстальщику, контент-менеджеру, SEO-специалисту или внешнему подрядчику. Классическая ошибка начинающих администраторов — создать стандартного пользователя Linux с паролем и отправить данные для входа по SSH.
Такой подход создает колоссальную брешь в безопасности сервера. Получив стандартный SSH-терминал, пользователь может свободно перемещаться по файловой системе вверх по дереву каталогов (cd ..), просматривать соседние проекты в /var/www/, читать конфигурационные файлы .env и wp-config.php с открытыми паролями к боевым базам данных, анализировать список активных процессов через ps aux и даже компилировать сторонний софт. С другой стороны, установка устаревших демонов вроде vsftpd или ProFTPD усложняет стек, требует открытия дополнительных диапазонов пассивных портов и нередко передает учетные данные в открытом незашифрованном виде.
Стандартом надежной изоляции в современных дистрибутивах Linux (Ubuntu 22.04 / 24.04 LTS, Debian 12) является использование встроенного механизма OpenSSH Chroot Jail с подсистемой internal-sftp. В этом руководстве мы разберем правильную настройку изолированного окружения: без доступа к шеллу, с корректными правами под веб-сервер Nginx/Apache, поддержкой SSH-ключей Ed25519 и разбором распространенных ошибок конфигурации.
1. Риски обычного SSH-доступа: почему подрядчикам нельзя давать терминал сервера
Когда вы создаете обычную учетную запись в Linux через команду adduser или useradd со стандартной оболочкой /bin/bash, операционная система предоставляет пользователю права на чтение большинства системных директорий. Права POSIX по умолчанию позволяют любому непривилегированному пользователю видеть то, что ему категорически не положено знать в среде shared-хостинга нескольких проектов:
- Компрометация соседних сайтов: Достаточно выполнить команду
cat /var/www/client2/.envили прочитатьwp-config.php, чтобы получить реквизиты доступа к MySQL/PostgreSQL, секретные ключи сессий, API-токены эквайринга и Telegram-ботов. - Разведка инфраструктуры: Через команды
ss -tulpn,cat /etc/passwdиip aзлоумышленник или недобросовестный подрядчик может собрать полный профиль сервера: внутренние порты контейнеров, имена баз данных и конфигурацию локальных сервисов. - Неконтролируемый запуск процессов: Доступ к шеллу позволяет запускать фоновые скрипты, туннели, утилиты сканирования сети или скрытые майнеры в каталоге
/tmp.
| Критерий безопасности | Обычный доступ по SSH | Классический FTP (vsftpd) | OpenSSH Chroot Jail |
|---|---|---|---|
| Шифрование трафика | Да (SSHv2) | Нет по умолчанию (FTP — открытый текст; FTPS требует сложной настройки TLS и портов) | Да (SSHv2 / SFTP из коробки) |
| Доступ к терминалу (Shell) | Полный bash/sh | Отсутствует | Заблокирован (/usr/sbin/nologin) |
| Выход за пределы каталога | Свободный (видит всю ФС) | Зависит от конфигурации | Исключен (для непривилегированного пользователя без шелла) |
| Сетевые порты | Порт 22 | Порт 21 + диапазон пассивных портов | Только штатный порт SSH (22) |
| Нагрузка на стек | Штатный sshd | Отдельный тяжелый сервис | Минимальная (используется штатный демон sshd без доп. сервисов) |
2. Механизм Chroot Jail в OpenSSH: преимущества internal-sftp перед сторонними демонами
Концепция Chroot (Change Root) — это системный вызов ядра Linux, который изменяет корневую директорию для текущего процесса и всех его потомков. Когда клиент авторизуется в системе, его корневой каталог / принудительно перенаправляется, например, в /var/www/site_user. Все системные папки (/bin, /etc, /home, /var/log) для него перестают существовать — пользователь физически не может подняться выше точки своего чрута.
Исторически настройка Chroot для SSH требовала копирования системных библиотек, создания фиктивных устройств /dev/null, /dev/zero и наличия бинарника /usr/lib/openssh/sftp-server внутри каждой изолированной директории. Если обновлялась системная библиотека glibc, чруты ломались.
Современный OpenSSH полностью решил эту проблему внедрением механизма internal-sftp. В отличие от внешнего бинарника, internal-sftp исполняется непосредственно внутри адресного пространства самого демона sshd. Ему не нужны никакие служебные файлы, библиотеки или интерпретаторы команд внутри изолированного каталога. Демон выполняет системный вызов chroot() сразу после успешной аутентификации и переходит к обслуживанию файловых запросов SFTP.
💡 Совет инженера: фундаментальное правило безопасности OpenSSH
Чтобы исключить возможность локального повышения привилегий через замену бинарников или создание жестких ссылок, OpenSSH на уровне исходного кода требует жесткого соблюдения правила: каталог chroot и абсолютно все родительские папки в пути обязаны принадлежать пользователю root и иметь права не более 755. Ни группа, ни сам пользователь не должны иметь права на запись в корневую папку чрута.
3. Создание группы sftpusers и учетной записи без шелла входа
Для удобного администрирования разграничение доступа строится на базе отдельной системной группы (например, sftpusers). Все пользователи, входящие в эту группу, автоматически подпадают под изолирующие правила в конфигурации SSH.
Подключитесь к вашему серверу по SSH под пользователем с привилегиями sudo и создайте системную группу:
# Создаем группу для изолированных пользователей SFTP
sudo groupadd sftpusers
Теперь создадим пользователя (например, devclient) с блокировкой командной оболочки. В качестве шелла входа укажем /usr/sbin/nologin, а домашней директорией назначим будущий корень его чрута /var/www/devclient:
⚠️ Почему флаг -m категорически нельзя использовать
При использовании стандартного флага useradd -m система автоматически создает домашний каталог с владельцем devclient:devclient и копирует шаблоны .bashrc из /etc/skel. Если такой каталог указать в качестве ChrootDirectory, подключение будет мгновенно заблокировано с ошибкой fatal: bad ownership or modes, так как корень чрута обязан принадлежать строго root:root. Поэтому каталог создается и настраивается вручную на шаге 5.
# Создаем пользователя без интерактивного шелла
# Флаг -g задает основную группу sftpusers
# Флаг -G добавляет вторичную группу www-data (для веб-сервера)
# Флаг -s блокирует bash/sh оболочку
# Флаг -d задает домашнюю директорию изолированного окружения (без -m!)
sudo useradd -g sftpusers -G www-data -d /var/www/devclient -s /usr/sbin/nologin devclient
# Задаем надежный пароль для учетной записи
sudo passwd devclient
💡 Совет инженера: криптостойкие пароли
При использовании парольной аутентификации для SFTP категорически избегайте простых комбинаций, словарных слов и дат. Брутфорс-боты непрерывно сканируют порт 22. Сгенерируйте случайный 20-значный пароль через онлайн-инструмент Генератор надежных паролей SysKit и обязательно используйте Fail2ban.
4. Конфигурация sshd_config: директивы Match Group, ChrootDirectory и запрет туннелей
В современных версиях дистрибутивов Ubuntu 22.04 / 24.04 LTS и Debian 12 конфигурация OpenSSH поддерживает модульную структуру каталога /etc/ssh/sshd_config.d/. Это позволяет вынести настройки изолированных SFTP-клиентов в отдельный файл, не затрагивая основной файл /etc/ssh/sshd_config.
Сначала убедитесь, что в качестве подсистемы SFTP указан встроенный обработчик internal-sftp. Откройте или проверьте основной конфиг:
# Проверяем директиву Subsystem в /etc/ssh/sshd_config
sudo grep -i "Subsystem sftp" /etc/ssh/sshd_config
Если там указан внешний путь Subsystem sftp /usr/lib/openssh/sftp-server, закомментируйте его и укажите Subsystem sftp internal-sftp. Затем создайте отдельный конфигурационный файл для группы sftpusers:
sudo nano /etc/ssh/sshd_config.d/sftp-chroot.conf
Вставьте следующий блок директив:
# Изоляция группы пользователей SFTP
Match Group sftpusers
# Директория Chroot: переменная %u подставляет имя текущего пользователя
ChrootDirectory /var/www/%u
# Принудительный запуск internal-sftp:
# -u 0002: маска прав (файлы 664, каталоги 775)
# -d /public_html: автоматический переход в рабочий каталог сайта при входе
ForceCommand internal-sftp -u 0002 -d /public_html
# Блокировка интерактивного терминала (PTY)
PermitTTY no
# Комплексный запрет всех видов туннелей (TCP, X11, Agent, StreamLocal)
DisableForwarding yes
# Сохраняем парольную аутентификацию для доступа без терминала
PasswordAuthentication yes
⚠️ Важное предупреждение: порядок секций Match
Директива Match в OpenSSH действует до конца конфигурационного файла либо до следующей секции Match. Все общие директивы демона должны располагаться строго до блоков Match, иначе они будут применены исключительно к соответствующей группе или пользователю.
Перед применением настроек обязательно протестируйте синтаксис конфигурации. Затем примените изменения через перечитывание конфигурации (reload), которое безопасно для активных соединений:
# 1. Тестирование синтаксиса SSH (вывод должен быть пустым)
sudo sshd -t
# 2. Перезагрузка конфигурации без разрыва существующих сессий
sudo systemctl reload ssh
# При необходимости полного перезапуска службы:
sudo systemctl restart ssh
💡 Почему reload надежнее restart
Команда systemctl reload ssh отправляет главному процессу сигнал SIGHUP. OpenSSH мгновенно перечитывает конфигурационные файлы и применяет новые правила к каждому новому подключению без разрыва вашей текущей административной сессии. В Ubuntu 24.04 LTS это также исключает рассинхронизацию с сокетом ssh.socket.
5. Архитектура каталогов: жесткое разделение корня root:root и папки public_html
Основная сложность настройки Chroot в Linux заключается в архитектурном противоречии: OpenSSH требует, чтобы точка входа в чрут принадлежала root:root и была закрыта на запись для всех остальных, но пользователю требуется возможность загружать файлы на сервер.
Решением является двухуровневая структура каталогов:
/var/www/devclient— корень Chroot Jail. Принадлежит строгоroot:rootс правами755. Пользователь не может создавать или удалять файлы в этой папке./var/www/devclient/public_html— рабочий каталог сайта. Принадлежитdevclient:www-dataс правами2775. Сюда пользователь загружает код, а веб-сервер имеет права на чтение и запись.
Создадим правильную структуру и выставим корректные права владения:
# 1. Создаем корневой каталог чрута и вложенную папку веб-документов
sudo mkdir -p /var/www/devclient/public_html
# 2. Назначаем владельцем корня суперпользователя root (КРИТИЧЕСКИ ВАЖНО)
sudo chown root:root /var/www/devclient
sudo chmod 755 /var/www/devclient
# 3. Назначаем рабочий каталог сайта пользователю и группе веб-сервера
sudo chown devclient:www-data /var/www/devclient/public_html
# 4. Устанавливаем права 2775 с битом SGID (chmod g+s)
sudo chmod 2775 /var/www/devclient/public_html
💡 Совет инженера: зачем нужен SGID-бит (права 2775)
Бит SGID (Special Group ID, первая цифра 2 в chmod 2775) заставляет операционную систему автоматически присваивать группу www-data абсолютно всем новым файлам и вложенным подкаталогам, которые создаст пользователь через SFTP-клиент. Без SGID новые файлы создавались бы с первичной группой пользователя (sftpusers), из-за чего Nginx или Apache не смогли бы получить к ним доступ.
6. Совместная работа с Nginx и Apache: права группы www-data и флаг umask
Чтобы сайт корректно открывался в браузере, а CMS (WordPress, Bitrix, Laravel) могла создавать кэш и сохранять загруженные медиафайлы, веб-сервер и SFTP-пользователь должны работать слаженно без конфликта прав доступа.
Пример корректного виртуального хоста Nginx для изолированного пользователя:
server {
listen 80;
server_name client.example.com;
# Указываем путь к рабочей папке public_html внутри чрута
root /var/www/devclient/public_html;
index index.php index.html;
access_log /var/log/nginx/devclient_access.log;
error_log /var/log/nginx/devclient_error.log;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
}
# Разрешаем запросы для SSL-сертификатов Let's Encrypt (ACME challenge)
location ^~ /.well-known/ {
allow all;
}
# Запрет доступа к скрытым системным файлам (.env, .git)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
Обратите внимание на значение флага -u 0002 в директиве ForceCommand internal-sftp -u 0002: маска создания файлов 0002 гарантирует, что создаваемые клиентом файлы будут получать права 664 (чтение и запись для владельца и группы www-data), а папки — права 775. Если веб-сервер попытается обновить кэш или плагин, он не столкнется с ошибкой 403 Forbidden.
7. Требования к серверу и выбор VDS для клиентских сайтов
При одновременной работе нескольких изолированных SFTP-пользователей на одном сервере ключевое значение приобретают аппаратные параметры дисковой подсистемы и процессора:
- Дисковая подсистема и IOPS: Синхронизация каталогов CMS (WordPress, Bitrix) включает тысячи мелких файлов. Высокие показатели произвольного чтения/записи (IOPS) на NVMe-накопителях исключают задержки и блокировки потоков при передаче данных.
- Нагрузка на шифрование (CPU): Каждая активная SFTP-сессия осуществляет шифрование трафика алгоритмами AES-GCM или ChaCha20-Poly1305. Для серверов с несколькими параллельными сессиями разработчиков рекомендуется запас по тактовой частоте процессора и минимум 2 vCPU.
- Резервное копирование: Изоляция в Chroot предотвращает выход за пределы домашнего каталога, но не защищает от случайного удаления данных внутри
public_htmlсамим пользователем. Наличие регулярных снапшотов на уровне хостинга позволяет быстро восстановить сайт в случае ошибок подрядчика.
Для надежного развертывания веб-проектов и стабильной работы служб можно ориентироваться на проверенные конфигурации виртуальных серверов:
Рекомендуемые VDS-провайдеры
Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с
Timeweb Cloud — Быстрые NVMe VDS
Высокочастотные процессоры 3.3+ ГГц для быстрой компиляции и отдачи страниц, NVMe-накопители и отказоустойчивая сеть 1 Гбит/с.
Selectel — Надежные облачные серверы
Корпоративная инфраструктура в сертифицированных ЦОД уровня Tier III с гарантированными ресурсами и гибким управлением приватными сетями.
Beget — Простота для веб-разработчиков
Мгновенное развертывание чистой Ubuntu 24.04 LTS, удобный файловый менеджер в панели, автоматические ежедневные бэкапы и поддержка 24/7.
8. Авторизация по ключам Ed25519 в изолированной Chroot-среде
Хотя надежный пароль достаточен для большинства задач контент-менеджеров, подключение по криптографическим SSH-ключам алгоритма Ed25519 обеспечивает максимальную защиту от перехвата и брутфорс-атак. Однако при использовании Chroot возникает вопрос: куда положить открытый ключ клиента?
Так как корень чрута /var/www/devclient принадлежит root:root и закрыт на запись, сам пользователь не может создать папку .ssh или загрузить туда ключ. Для решения этой задачи в production-средах применяются два подхода:
Способ 1: Централизованное хранение ключей в /etc/ssh/authorized_keys (Рекомендуемый)
В секцию Match Group sftpusers конфигурации OpenSSH добавляется директива, указывающая системный каталог для открытых ключей:
Match Group sftpusers
ChrootDirectory /var/www/%u
ForceCommand internal-sftp -u 0002
# Путь к открытым ключам изолированных пользователей
AuthorizedKeysFile /etc/ssh/authorized_keys/%u
Создайте директорию на сервере и добавьте открытый ключ клиента:
# Создаем системную директорию для хранения ключей
sudo mkdir -p /etc/ssh/authorized_keys
sudo chmod 755 /etc/ssh/authorized_keys
# Создаем файл ключа для пользователя devclient
sudo nano /etc/ssh/authorized_keys/devclient
# Вставляем открытый ключ клиента (начинается на ssh-ed25519 ...)
# Выставляем строгие права (только чтение суперпользователем)
sudo chown root:root /etc/ssh/authorized_keys/devclient
sudo chmod 644 /etc/ssh/authorized_keys/devclient
Этот метод исключает засорение рабочей папки сайта служебными каталогами .ssh и предотвращает случайное удаление ключа самим клиентом через графический SFTP-клиент.
Способ 2: Размещение папки .ssh внутри корня чрута
Если вы хотите сохранить стандартный путь, создайте папку .ssh вручную от имени суперпользователя:
sudo mkdir -p /var/www/devclient/.ssh
sudo nano /var/www/devclient/.ssh/authorized_keys
# Устанавливаем строгие права: владельцем ОБЯЗАН оставаться root
sudo chown -R root:root /var/www/devclient/.ssh
sudo chmod 755 /var/www/devclient/.ssh
sudo chmod 644 /var/www/devclient/.ssh/authorized_keys
9. Траблшутинг: решение ошибки bad ownership or modes for chroot directory
Если при попытке подключения в WinSCP, FileZilla или через консольный sftp соединение мгновенно разрывается, первым шагом изучите системный журнал авторизации на сервере:
# Просмотр системного лога службы SSH в реальном времени
sudo journalctl -u ssh -e --no-pager
| Сообщение в логе / Ошибка | Причина сбоя | Способ решения |
|---|---|---|
fatal: bad ownership or modes for chroot directory |
Каталог Chroot (или один из родительских каталогов) принадлежит пользователю, а не root, либо имеет права на запись для группы (775 или 777). |
Выполните:sudo chown root:root /var/www/devclientsudo chmod 755 /var/www/devclient |
subsystem: cannot stat /usr/lib/.../sftp-server |
В конфиге указан внешний бинарник sftp-server, которого нет внутри изолированного чрута. |
Замените директиву в sshd_config на встроенную: Subsystem sftp internal-sftp. |
Permission denied (publickey,password) |
Неверный пароль, отключена директива PasswordAuthentication yes в блоке Match, или заблокирован порт. |
Проверьте открыт ли порт SSH и обновите пароль через sudo passwd devclient. |
| Клиент не может создать файл в корне | Пользователь пытается писать в корень чрута /var/www/devclient, закрытый для записи. |
Загружайте файлы в папку public_html. Для автоматического открытия этой папки укажите флаг -d /public_html в директиве ForceCommand. |
10. Готовый скрипт автоматического создания изолированного пользователя
Чтобы не выполнять команды настройки вручную для каждого нового клиента или подрядчика, создайте надежный bash-скрипт автоматизации create_sftp_user.sh:
#!/usr/bin/env bash
# Скрипт автоматического создания изолированного SFTP-пользователя в Chroot Jail
set -euo pipefail
if [ "$EUID" -ne 0 ]; then
echo "❌ Ошибка: скрипт должен быть запущен с правами root (sudo)!"
exit 1
fi
USERNAME="${1:-}"
if [ -z "$USERNAME" ]; then
echo "Использование: sudo $0 <имя_пользователя>"
exit 1
fi
BASE_DIR="/var/www/$USERNAME"
WEB_DIR="$BASE_DIR/public_html"
# 1. Проверяем наличие группы sftpusers
if ! getent group sftpusers > /dev/null 2>&1; then
groupadd sftpusers
echo "✅ Создана системная группа sftpusers"
fi
# 2. Создаем пользователя, если он еще не существует
if id "$USERNAME" > /dev/null 2>&1; then
echo "⚠️ Пользователь $USERNAME уже существует, обновляем группы и каталоги..."
# Флаг -aG добавляет вторичную группу www-data, не затирая существующие группы пользователя
usermod -g sftpusers -aG www-data -s /usr/sbin/nologin -d "$BASE_DIR" "$USERNAME"
else
useradd -g sftpusers -G www-data -d "$BASE_DIR" -s /usr/sbin/nologin "$USERNAME"
echo "✅ Пользователь $USERNAME успешно зарегистрирован в системе"
fi
# 3. Формируем безопасную структуру каталогов
mkdir -p "$WEB_DIR"
# Корень чрута ОБЯЗАН принадлежать root:root с правами 755
chown root:root "$BASE_DIR"
chmod 755 "$BASE_DIR"
# Рабочий каталог сайта передаем пользователю и веб-серверу с SGID-битом
chown "$USERNAME":www-data "$WEB_DIR"
chmod 2775 "$WEB_DIR"
echo "--------------------------------------------------------"
echo "🎉 Изолированный SFTP Chroot успешно настроен для: $USERNAME"
echo "📂 Корень Chroot: $BASE_DIR (права 755, владелец root:root)"
echo "🌐 Каталог сайта: $WEB_DIR (права 2775, $USERNAME:www-data)"
echo "--------------------------------------------------------"
echo "Установите пароль для пользователя командой:"
echo "sudo passwd $USERNAME"
echo "--------------------------------------------------------"
Сохраните скрипт на сервере (например, в /usr/local/bin/create_sftp_user.sh) и сделайте его исполняемым:
sudo chmod +x /usr/local/bin/create_sftp_user.sh
# Запуск для нового клиента:
sudo /usr/local/bin/create_sftp_user.sh client_shop
11. Резюме и итоговый чек-лист безопасности
Использование встроенного механизма OpenSSH Chroot Jail позволяет полностью изолировать внешних подрядчиков, верстальщиков и клиентов в пределах их собственного сайта, гарантируя конфиденциальность соседних проектов и системных конфигураций VDS.
📋 Чек-лист безопасности администратора перед передачей доступа
- Пользователю назначен шелл
/usr/sbin/nologin(интерактивный вход в консоль заблокирован). - Каталог Chroot (
/var/www/username) принадлежит строгоroot:rootс правами не выше755. - В рабочей папке
public_htmlустановлены права2775с SGID-битом и группойwww-data. - В секции Match Group заданы директивы
ForceCommand internal-sftp -u 0002 -d /public_html,DisableForwarding yesиPermitTTY no. - Синтаксис проверен командой
sudo sshd -t, а служба перезагружена черезsudo systemctl reload ssh. - Учетная запись защищена криптостойким паролем или SSH-ключом Ed25519, а порт закрыт от брутфорса через Fail2ban или CrowdSec.
Настройте защищенный VDS с быстрыми NVMe-дисками
Разверните надежный облачный сервер на базе Ubuntu 24.04 LTS у проверенных провайдеров. Быстрые диски NVMe обеспечат мгновенную передачу файлов по SFTP и комфортную работу клиентов без задержек.