Установка BunkerWeb WAF на VPS: защита сайтов, SSL, Docker и CrowdSec
TL;DR
BunkerWeb — это веб-файрволл с обратным прокси, который размещается перед сайтами, принимает HTTPS-трафик, применяет правила WAF и передаёт запросы приложениям. В этом руководстве мы установим BunkerWeb в Docker на Ubuntu Server 24.04 LTS, подключим сайт через reverse proxy, настроим автоматический TLS Let's Encrypt, базовую защиту от ботов и интеграцию с CrowdSec.
- Используем Ubuntu Server 24.04 LTS, Docker Engine 28.x и Docker Compose v2.
- BunkerWeb будет единственной публичной точкой входа на портах 80 и 443.
- Приложение останется во внутренней Docker-сети и не будет напрямую доступно из интернета.
- Сертификаты Let's Encrypt будут выпускаться и продлеваться автоматически.
- CrowdSec будет анализировать логи и блокировать IP-адреса с плохой репутацией.
- Конфигурации, секреты и данные будут вынесены в отдельные файлы и включены в резервное копирование.
3. Что мы настраиваем и зачем
Обычная схема публикации сайта выглядит так: интернет-клиент обращается непосредственно к веб-серверу приложения. Если приложение содержит уязвимость, неправильно обрабатывает запросы или подвергается перебору паролей, атака доходит до него без дополнительного фильтра. BunkerWeb меняет эту архитектуру: он становится обратным прокси и WAF между клиентом и приложением.
WAF, или Web Application Firewall, анализирует HTTP-запросы до передачи их на backend. Он может отбрасывать подозрительные URI, попытки SQL-инъекций, XSS, обхода каталогов, автоматизированный перебор и другие типовые атаки. Это не заменяет обновление CMS, фреймворка и операционной системы, но уменьшает поверхность атаки и добавляет отдельный защитный слой.
Итоговая схема
Интернет
|
| TCP 80/443
v
[BunkerWeb WAF]
|
| внутренняя Docker-сеть
v
[web-приложение: nginx, Node.js, PHP-FPM, контейнер SaaS]
|
v
[База данных и хранилище]
В нашем примере будет домен example.com, а приложение будет доступно внутри Docker по имени app на порту 8080. Замените этот домен и параметры на свои. DNS-записи должны указывать на публичный IPv4-адрес сервера ещё до выпуска сертификата.
Что получится после настройки
- Порты приложения не публикуются через Docker на внешний интерфейс.
- HTTP-запросы принимаются BunkerWeb, а HTTPS-сертификат обслуживается автоматически.
- Для сайта включаются базовые правила ModSecurity/OWASP CRS через шаблон BunkerWeb.
- Логи запросов сохраняются в Docker volume и доступны для анализа CrowdSec.
- Системный firewall разрешает только SSH, HTTP и HTTPS.
- Обновление контейнеров и резервное копирование можно выполнять независимо от приложения.
Self-hosted или облачный WAF
Облачные WAF-сервисы принимают трафик на своей стороне и проксируют его на origin-сервер. Это удобно при больших объёмах, распределённой защите от DDoS и необходимости минимизировать собственное обслуживание. Однако придётся передать DNS и часть сетевого контроля внешнему оператору, а стоимость и ограничения зависят от тарифа.
Self-hosted BunkerWeb подходит, когда нужен полный контроль над конфигурацией, логами и местом обработки данных. Владелец VPS сам отвечает за обновления, доступность, резервное копирование и защиту origin. Для одного или нескольких сайтов на небольшом сервере такой вариант обычно проще и прозрачнее, чем самостоятельная сборка Nginx, ModSecurity, rate limiting и отдельного сервиса управления сертификатами.
Важно понимать границы решения: BunkerWeb не является полноценной защитой от мощной volumetric DDoS-атаки, не исправляет уязвимости приложения и не заменяет контроль доступа к административным панелям. Для критичного сервиса нужны мониторинг, отдельные резервные копии и план восстановления.
4. Какой VPS-конфиг нужен под эту задачу
BunkerWeb работает как reverse proxy, поэтому его требования зависят не только от самого WAF, но и от количества сайтов, размера запросов, TLS-нагрузки, включённых правил и производительности backend. Для одного небольшого сайта WAF потребляет мало ресурсов, но запас RAM нужен Docker, CrowdSec, журналированию и обновлениям.
| Сценарий | CPU | RAM | Диск | Сеть |
|---|---|---|---|---|
| Тестовый сайт | 1 vCPU | 2 ГБ | 25 ГБ SSD | 100 Мбит/с |
| Небольшой production-сайт | 2 vCPU | 4 ГБ | 40–60 ГБ NVMe | 100–500 Мбит/с |
| Несколько сайтов и CrowdSec | 4 vCPU | 8 ГБ | 80–120 ГБ NVMe | 500 Мбит/с и выше |
Минимальная практичная конфигурация для production — 2 vCPU, 4 ГБ RAM и 40 ГБ SSD. Если на том же VPS будут работать база данных, CMS, CI/CD, панель управления или несколько приложений, лучше сразу взять 4 vCPU и 8 ГБ RAM. Для Docker полезно оставить не менее 1 ГБ свободной памяти после запуска всех контейнеров.
В качестве конкретного варианта можно взять подходящий VPS с 2–4 vCPU, 4–8 ГБ RAM, NVMe-диском от 40 ГБ и публичным IPv4. Проверьте, что провайдер разрешает входящие соединения на TCP 80 и 443, предоставляет консоль восстановления и позволяет делать снимки или подключать внешнее хранилище.
Когда нужен dedicated
Выделенный сервер оправдан, если через WAF проходит большой постоянный поток запросов, нужно несколько десятков сайтов, выполняется тяжёлое TLS-шифрование или на машине размещаются ресурсоёмкие backend-сервисы. Dedicated также удобен для требований к гарантированным IOPS, изоляции от соседей и собственного сетевого оборудования.
Для одного сайта, панели администрирования, API или небольшой команды dedicated обычно избыточен. Сначала измерьте загрузку CPU, RAM, диска и сети. Если средняя загрузка ниже 50%, а пики не приводят к swap и задержкам, переход на физический сервер не даст заметной пользы.
Выбор локации
Локация влияет на задержку между посетителем и WAF, маршрут до backend и требования к хранению данных. Выбирайте регион, близкий к основной аудитории, но учитывайте доступность IPv4, качество каналов и правила обработки персональных данных. Если приложение и база находятся в разных регионах, задержка между ними может стать заметнее, чем задержка от пользователя до WAF.
5. Подготовка сервера
Ниже предполагается чистый VPS с Ubuntu Server 24.04 LTS и доступом под пользователем root. Все команды выполняются на сервере. Подставьте свой публичный SSH-ключ вместо примера и не отключайте текущую SSH-сессию до проверки нового пользователя.
Создание пользователя и установка SSH-ключа
# Создаём отдельного администратора
adduser deploy
# Добавляем его в группу sudo
usermod -aG sudo deploy
# Создаём каталог для SSH-ключей
install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
# Добавляем публичный ключ; замените значение на свой ключ
printf '%s\n' 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... admin@workstation' \
> /home/deploy/.ssh/authorized_keys
# Исправляем владельца и права
chown deploy:deploy /home/deploy/.ssh/authorized_keys
chmod 600 /home/deploy/.ssh/authorized_keys
Проверьте вход в отдельном окне терминала:
# Выполняется на локальном компьютере
ssh deploy@SERVER_IP
После успешной проверки можно запретить вход root и авторизацию по паролю. Перед этим убедитесь, что ключ работает, а в панели провайдера доступна web-консоль:
# Открываем конфигурацию SSH
sudoedit /etc/ssh/sshd_config.d/ hardening.conf
В команде выше не должно быть пробела в имени файла. Используйте корректный вариант:
# Создаём отдельный файл настроек SSH
sudo tee /etc/ssh/sshd_config.d/hardening.conf > /dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
AllowUsers deploy
EOF
# Проверяем синтаксис перед перезапуском
sudo sshd -t
# Применяем настройки SSH
sudo systemctl reload ssh
Обновление системы и базовые пакеты
# Обновляем индекс репозиториев и пакеты Ubuntu
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt full-upgrade -y
# Устанавливаем утилиты для администрирования и репозиториев
sudo apt install -y \
ca-certificates curl gnupg jq git unzip vim htop tmux \
ufw fail2ban unattended-upgrades apt-transport-https
# Перезагружаем сервер, если обновлялось ядро
sudo reboot
Firewall и Fail2ban
Откройте SSH до включения UFW. Если SSH работает на другом порту, замените правило 22/tcp. Порты 80 и 443 будут нужны BunkerWeb для HTTP и HTTPS.
# Разрешаем SSH, HTTP и HTTPS
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Включаем политику запрета входящих соединений
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Включаем firewall
sudo ufw --force enable
# Проверяем активные правила
sudo ufw status verbose
Fail2ban защитит SSH от простого перебора. BunkerWeb и CrowdSec будут отвечать за веб-трафик, поэтому не нужно бездумно добавлять десятки фильтров и блокировать легитимных пользователей.
# Включаем стандартную защиту SSH
sudo systemctl enable --now fail2ban
# Проверяем состояние jail
sudo fail2ban-client status
sudo fail2ban-client status sshd
6. Установка ПО — пошагово
В 2026 году используйте Docker Engine из официального репозитория Docker, а не устаревший пакет из базового репозитория Ubuntu. Версию BunkerWeb лучше фиксировать на конкретном стабильном теге серии 1.6.x, а не использовать latest. Перед production-развёртыванием проверьте актуальный стабильный тег в официальной документации проекта и замените значение переменной BUNKERWEB_VERSION.
Шаг 1. Установка Docker Engine
# Удаляем конфликтующие пакеты старых Docker-реализаций
sudo apt remove -y docker.io docker-doc docker-compose podman-docker containerd runc || true
# Создаём каталог ключей APT
sudo install -m 0755 -d /etc/apt/keyrings
# Загружаем официальный ключ Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Разрешаем APT читать ключ
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Добавляем официальный репозиторий Docker для Ubuntu 24.04
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu noble stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Устанавливаем Docker Engine и Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
# Запускаем Docker после загрузки системы
sudo systemctl enable --now docker
# Проверяем версии
docker --version
docker compose version
Не добавляйте пользователя в группу docker без необходимости: участник этой группы фактически получает права root. В дальнейшем команды Docker выполняйте через sudo или осознанно примите этот риск.
Шаг 2. Создание каталога проекта
# Создаём каталог конфигурации BunkerWeb
sudo install -d -m 750 -o deploy -g deploy /opt/bunkerweb
# Переключаемся на администратора проекта
sudo -iu deploy
# Переходим в рабочий каталог
cd /opt/bunkerweb
# Создаём каталоги для данных и логов
mkdir -p data logs backups
Шаг 3. Создание файла секретов
# Создаём файл переменных окружения с закрытыми правами
touch /opt/bunkerweb/.env
chmod 600 /opt/bunkerweb/.env
# Генерируем случайный пароль для административной базы
openssl rand -base64 32
Сохраните результат в менеджере паролей. Позже этот пароль будет использоваться только для внутренних сервисов. Не публикуйте файл .env в Git и не вставляйте реальные секреты в compose-файл.
Шаг 4. Подготовка Docker Compose
Для простой установки мы используем BunkerWeb и scheduler, а отдельное приложение представляем контейнером app. В production вместо демонстрационного образа поставьте свой backend. BunkerWeb должен находиться в той же Docker-сети, что и приложение.
# Переходим в каталог проекта
cd /opt/bunkerweb
# Создаём compose-файл
cat > compose.yml <<'EOF'
services:
bunkerweb:
image: bunkerity/bunkerweb:${BUNKERWEB_VERSION}
restart: unless-stopped
ports:
- "80:8080"
- "443:8443"
environment:
SERVER_NAME: "${SERVER_NAME}"
MULTISITE: "yes"
HTTP_PORT: "8080"
HTTPS_PORT: "8443"
DISABLE_DEFAULT_SERVER: "yes"
AUTO_LETS_ENCRYPT: "yes"
EMAIL_LETS_ENCRYPT: "${LETSENCRYPT_EMAIL}"
USE_REVERSE_PROXY: "yes"
REVERSE_PROXY_URL: "http://app:8080"
USE_MODSECURITY: "yes"
USE_ANTIBOT: "yes"
ANTIBOT_METHOD: "cookie"
LOG_FORMAT: "json"
volumes:
- bunkerweb_data:/data
- bunkerweb_logs:/var/log/bunkerweb
networks:
- edge
depends_on:
- app
app:
image: traefik/whoami:v1.10
restart: unless-stopped
expose:
- "80"
networks:
- edge
networks:
edge:
volumes:
bunkerweb_data:
bunkerweb_logs:
EOF
Демонстрационный контейнер whoami слушает порт 80, поэтому значение REVERSE_PROXY_URL в реальном compose-файле нужно заменить на http://app:80. Это важно: имя сервиса и порт должны совпадать с фактическим адресом backend.
Шаг 5. Запуск контейнеров
# Загружаем переменные и проверяем итоговую модель Compose
cd /opt/bunkerweb
sudo docker compose --env-file .env config
# Скачиваем образы
sudo docker compose --env-file .env pull
# Запускаем BunkerWeb и backend
sudo docker compose --env-file .env up -d
# Проверяем состояние контейнеров
sudo docker compose ps
# Просматриваем последние сообщения BunkerWeb
sudo docker compose logs --tail=100 bunkerweb
Шаг 6. Установка CrowdSec
CrowdSec состоит из анализатора поведения и компонента принятия решения. Он читает логи, сопоставляет события с сценариями и создаёт решения о блокировке IP. В этой схеме BunkerWeb пишет JSON-логи в volume, а CrowdSec устанавливается на хосте. Для production удобнее монтировать лог в отдельный каталог, доступный только для чтения агенту CrowdSec.
# Добавляем официальный репозиторий CrowdSec
curl -fsSL https://packagecloud.io/crowdsec/crowdsec/gpgkey \
| sudo gpg --dearmor -o /usr/share/keyrings/crowdsec.gpg
echo "deb [signed-by=/usr/share/keyrings/crowdsec.gpg] \
https://packagecloud.io/crowdsec/crowdsec/ubuntu/ noble main" \
| sudo tee /etc/apt/sources.list.d/crowdsec.list > /dev/null
# Устанавливаем CrowdSec
sudo apt update
sudo apt install -y crowdsec
# Включаем сервис
sudo systemctl enable --now crowdsec
# Проверяем состояние
sudo systemctl status crowdsec --no-pager
sudo cscli version
Установите коллекции для веб-сервера и Linux. Названия коллекций могут меняться, поэтому сначала выполните поиск:
# Обновляем индекс коллекций
sudo cscli hub update
# Ищем доступные коллекции nginx и базовую Linux-коллекцию
sudo cscli collections list | grep -E 'nginx|linux'
# Устанавливаем подходящие коллекции
sudo cscli collections install crowdsecurity/linux
sudo cscli collections install crowdsecurity/nginx
# Перезапускаем анализатор
sudo systemctl restart crowdsec
На следующем этапе нужно указать CrowdSec реальный путь к логам BunkerWeb. Если используется Docker volume, сначала определите его точку монтирования:
# Показываем имя volume с логами
sudo docker volume ls | grep bunkerweb
# Получаем путь volume на хосте
sudo docker volume inspect bunkerweb_bunkerweb_logs
Путь из поля Mountpoint добавьте в конфигурацию источника CrowdSec. Конкретные имена лог-файлов зависят от версии и шаблона BunkerWeb, поэтому сначала найдите их:
# Показываем файлы логов внутри контейнера
sudo docker compose exec bunkerweb sh -c 'find /var/log/bunkerweb -maxdepth 2 -type f -print'
# Проверяем наличие событий
sudo docker compose logs --since=10m bunkerweb
7. Конфигурация
Файл переменных окружения
Создайте /opt/bunkerweb/.env. Здесь нет секретов приложения, но файл всё равно должен быть закрыт от других пользователей. Для примера используется стабильная ветка BunkerWeb 1.6.x; перед установкой проверьте актуальный patch-релиз.
# Записываем основные переменные проекта
cat > /opt/bunkerweb/.env <<'EOF'
BUNKERWEB_VERSION=1.6
SERVER_NAME=example.com
[email protected]
EOF
# Запрещаем чтение файла другим пользователям
chmod 600 /opt/bunkerweb/.env
# Проверяем, что переменные подставляются
sudo docker compose --env-file .env config | sed -n '1,120p'
Для нескольких сайтов в BunkerWeb включается multisite-режим. В зависимости от выбранного шаблона отдельные параметры задаются через переменные с префиксом имени сайта или через UI/API. Не копируйте конфигурацию одного сайта на другой без проверки доменных имён и upstream-адресов.
Проверка DNS и портов
# Проверяем DNS-запись с локального компьютера
dig +short A example.com
# Проверяем, что сервер слушает HTTP и HTTPS
sudo ss -lntp | grep -E ':80|:443'
# Проверяем HTTP-ответ по IP или домену
curl -I http://example.com
# Проверяем HTTPS и цепочку сертификата
curl -Iv https://example.com
Для выпуска Let's Encrypt домен должен разрешаться в публичный IPv4-адрес VPS, а TCP 80 должен быть доступен из интернета. Если используется AAAA-запись, убедитесь, что IPv6 действительно настроен на сервере и firewall разрешает нужные порты. Иначе временно удалите неверную AAAA-запись или настройте IPv6 полностью.
Проверка reverse proxy
# Проверяем backend из контейнера BunkerWeb
sudo docker compose exec bunkerweb sh -c \
'wget -qO- http://app:80 || true'
# Проверяем сетевые подключения контейнеров
sudo docker network inspect bunkerweb_edge
# Смотрим последние ошибки проксирования
sudo docker compose logs --since=15m bunkerweb | \
grep -Ei 'error|upstream|certificate|failed'
Если в ответе backend видны служебные заголовки, это нормально для тестового контейнера. В production передавайте приложению исходный протокол и адрес клиента. BunkerWeb обычно формирует стандартные прокси-заголовки, но приложение должно быть настроено доверять им только от адреса reverse proxy, а не от произвольного клиента.
Настройка ограничений запросов
Rate limiting особенно полезен для страниц входа, API и восстановления пароля. Не задавайте агрессивные глобальные ограничения: они могут заблокировать NAT-пользователей, мобильные сети и поисковых роботов. Начните с умеренного лимита и наблюдайте логи.
# Просматриваем доступные переменные BunkerWeb в установленном образе
sudo docker run --rm bunkerity/bunkerweb:${BUNKERWEB_VERSION} \
sh -c 'env | sort | grep -Ei "limit|rate|modsecurity|antibot"'
Названия переменных и доступные настройки зависят от версии BunkerWeb. Для production сверяйте их с документацией конкретного релиза. Веб-интерфейс BunkerWeb или API удобны для управления несколькими сайтами, но административный интерфейс нельзя публиковать без дополнительной аутентификации и ограничения по IP.
Подключение CrowdSec к веб-логам
Сначала убедитесь, что формат логов содержит как минимум время, IP клиента, HTTP-метод, URI и код ответа. Если перед VPS стоит внешний CDN или балансировщик, CrowdSec должен анализировать реальный адрес клиента из доверенного заголовка, иначе он будет блокировать адрес прокси.
# Проверяем решения CrowdSec
sudo cscli decisions list
# Проверяем метрики сбора и сценариев
sudo cscli metrics
# Смотрим журналы сервиса
sudo journalctl -u crowdsec -n 100 --no-pager
Для блокировки на уровне веб-трафика используйте официальный BunkerWeb bouncer или другой совместимый компонент, поддерживаемый вашей версией BunkerWeb. Общий принцип такой: CrowdSec выдаёт API-ключ, а bouncer периодически получает список решений. Не вставляйте этот ключ в публичный compose-файл и не передавайте его в клиентский JavaScript.
# Создаём API-ключ для отдельного bouncer
sudo cscli bouncers add bunkerweb-bouncer
# Показываем активные bouncers без раскрытия ключа
sudo cscli bouncers list
Скопируйте ключ в защищённый файл переменных только на основании инструкции выбранного bouncer. Если ключ раскрыт, немедленно удалите его и создайте новый. Проверяйте работу интеграции на тестовом IP и не включайте постоянную блокировку до того, как убедитесь, что сценарии не дают ложных срабатываний.
Проверка WAF
Тестируйте WAF безопасными запросами на тестовом домене. Цель — проверить, что запрос проходит через BunkerWeb и правила действительно применяются, а не подобрать эксплуатационный payload для production.
# Проверяем обычный запрос
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/
# Проверяем служебные заголовки и редиректы
curl -sSI https://example.com/
# Проверяем, что явно подозрительная строка фиксируется в логах
curl -k -i 'https://example.com/?q=%3Cscript%3Etest%3C%2Fscript%3E'
# Смотрим решения и журналы после теста
sudo cscli decisions list
sudo docker compose logs --since=5m bunkerweb
Код ответа 403 не всегда означает ошибку: запрос мог быть остановлен WAF. Важно сопоставить время запроса, домен, IP и правило в журнале. Не переводите ModSecurity в режим блокировки на только что опубликованном сайте без предварительного анализа режима обнаружения и тестов на реальных страницах.
8. Бэкапы и обслуживание
Что необходимо сохранять
- Конфигурации:
compose.yml,.env, настройки BunkerWeb, CrowdSec и firewall. - Данные сайтов: загруженные файлы, пользовательские документы, медиа и приватные ключи.
- Базы данных: логический дамп PostgreSQL, MariaDB или другой используемой СУБД.
- Сертификаты и настройки TLS: только если они не могут быть восстановлены автоматически.
- Логи: обычно достаточно хранить их ограниченное время, если они не нужны для аудита.
Docker image повторно скачивается из registry, поэтому его обычно не нужно архивировать. Резервная копия должна храниться вне VPS: на отдельном сервере, в S3-совместимом объектном хранилище или в другой физической локации. Snapshot диска удобен, но не заменяет логические дампы базы данных и независимую копию.
Пример бэкапа конфигураций через Restic
Ниже используется S3-совместимое хранилище. Значения доступа передаются через переменные окружения и не записываются в скрипт. Установите Restic из репозитория Ubuntu либо используйте официальный пакет подходящей версии.
# Устанавливаем Restic
sudo apt install -y restic
# Создаём закрытый файл переменных бэкапа
sudo install -m 600 /dev/null /etc/restic-bunkerweb.env
# Пример содержимого; замените значения на свои
sudo tee /etc/restic-bunkerweb.env > /dev/null <<'EOF'
export AWS_ACCESS_KEY_ID='CHANGE_ME'
export AWS_SECRET_ACCESS_KEY='CHANGE_ME'
export RESTIC_REPOSITORY='s3:https://s3.example.net/bunkerweb-backups'
export RESTIC_PASSWORD='CHANGE_ME_LONG_RANDOM_PASSWORD'
EOF
# Инициализируем repository один раз
sudo bash -c 'source /etc/restic-bunkerweb.env && restic init'
Создайте скрипт, который сначала архивирует конфигурацию, а затем отправляет её в хранилище. Для базы данных добавьте отдельную команду дампа: копирование файлов работающей базы почти всегда некорректно.
# Создаём каталог временных архивов
sudo install -d -m 700 /var/backups/bunkerweb
# Создаём скрипт резервного копирования
sudo tee /usr/local/sbin/backup-bunkerweb > /dev/null <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
source /etc/restic-bunkerweb.env
STAMP="$(date -u +%Y-%m-%dT%H-%M-%SZ)"
WORK="/var/backups/bunkerweb/${STAMP}"
mkdir -p "$WORK"
# Копируем конфигурацию и список контейнеров
install -m 600 /opt/bunkerweb/.env "$WORK/.env"
cp -a /opt/bunkerweb/compose.yml "$WORK/compose.yml"
docker compose -f /opt/bunkerweb/compose.yml ps > "$WORK/containers.txt"
# Архивируем конфигурацию в удалённый repository
restic backup "$WORK" --tag bunkerweb-config
# Удаляем локальные временные файлы
rm -rf "$WORK"
# Оставляем разумную историю
restic forget --tag bunkerweb-config \
--keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
EOF
# Делаем скрипт исполняемым
sudo chmod 750 /usr/local/sbin/backup-bunkerweb
Запланируйте запуск через root-cron:
# Открываем cron для root
sudo crontab -e
Добавьте строку:
# Ежедневный бэкап в 03:15 по времени сервера
15 3 * /usr/local/sbin/backup-bunkerweb >> /var/log/backup-bunkerweb.log 2>&1
Периодически проверяйте восстановление. Бэкап, который ни разу не тестировали, нельзя считать гарантированно пригодным.
# Показываем резервные точки
sudo bash -c 'source /etc/restic-bunkerweb.env && restic snapshots'
# Проверяем целостность repository
sudo bash -c 'source /etc/restic-bunkerweb.env && restic check'
Обновления
Перед обновлением сохраните конфигурацию и прочитайте release notes BunkerWeb. Для одного сайта допустимо короткое maintenance window: скачать новый образ, пересоздать контейнер и проверить HTTPS. Для нескольких production-сайтов лучше использовать отдельный тестовый экземпляр, healthcheck и поэтапное переключение.
# Проверяем текущие образы
cd /opt/bunkerweb
sudo docker compose images
# Загружаем обновления после проверки версии
sudo docker compose pull
# Перезапускаем сервисы с новой версией
sudo docker compose up -d
# Проверяем состояние и логи
sudo docker compose ps
sudo docker compose logs --tail=100 bunkerweb
# Проверяем внешний HTTPS
curl -fsS https://example.com/ > /dev/null
echo "HTTPS OK"
Не обновляйте одновременно Ubuntu, Docker, BunkerWeb, CrowdSec и приложение без необходимости. Если после обновления возникла проблема, так проще определить источник. Старые Docker-образы удаляйте только после проверки, что откат не понадобится.
9. Troubleshooting и FAQ
Почему сайт не открывается, а curl возвращает connection refused?
Сначала проверьте, запущены ли контейнеры командой sudo docker compose ps, и слушает ли хост порты через sudo ss -lntp. Затем проверьте UFW, правила сетевой группы провайдера и DNS. Если контейнер перезапускается, выполните sudo docker compose logs --tail=200 bunkerweb. Частая причина — занятый порт 80/443 другим Nginx или Caddy. Остановите старый сервис либо перенесите его за BunkerWeb.
Почему Let's Encrypt не выпускает сертификат?
Наиболее частые причины — домен указывает не на тот IP, закрыт TCP 80, присутствует неправильная AAAA-запись или включён внешний прокси с неподходящим режимом TLS. Проверьте dig A example.com, dig AAAA example.com и доступность HTTP извне. Посмотрите логи BunkerWeb и убедитесь, что email задан корректно. Не запрашивайте сертификат много раз подряд: у Let's Encrypt есть ограничения частоты.
Почему BunkerWeb возвращает 502 Bad Gateway?
Код 502 означает, что reverse proxy не получил корректный ответ от upstream. Проверьте имя Docker-сервиса, внутренний порт и общую сеть контейнеров. Команда docker compose exec bunkerweb wget -qO- http://app:80 должна получить ответ backend. Если приложение слушает только 127.0.0.1 внутри собственного контейнера, оно не будет доступно по Docker-сети. Настройте его на прослушивание 0.0.0.0.
Какой VPS-конфиг минимально подойдёт?
Для одного небольшого сайта технический минимум — 1 vCPU, 2 ГБ RAM и 25 ГБ SSD, но для production лучше использовать 2 vCPU и 4 ГБ RAM. Такой запас нужен для Docker, BunkerWeb, CrowdSec, обновлений и кратковременных пиков. Если на сервере работает база данных или несколько приложений, выбирайте от 4 vCPU и 8 ГБ RAM. Диск должен быть SSD, а сеть — с публичным IPv4 и доступными портами 80/443.
Что выбрать — VPS или dedicated для этой задачи?
VPS подходит большинству проектов с одним или несколькими сайтами: BunkerWeb и CrowdSec потребляют умеренное количество ресурсов, а масштабирование выполняется увеличением тарифа или добавлением экземпляров. Dedicated стоит выбирать при постоянной высокой нагрузке, большом количестве доменов, требованиях к выделенным IOPS или строгой изоляции. Решение принимайте по фактическим метрикам CPU, RAM, диска и сети, а не только по предполагаемому числу посетителей.
Как понять, что CrowdSec действительно блокирует атаки?
Проверьте sudo cscli metrics, список решений через sudo cscli decisions list и журналы journalctl -u crowdsec. Если метрики пустые, агент не читает нужный лог или формат не соответствует parser. Если решения создаются, но запросы проходят, не настроен bouncer или он не подключён к BunkerWeb. Начинайте с тестовой блокировки собственного временного IP и сразу удаляйте её после проверки.
Почему реальные IP пользователей отображаются как адрес прокси?
Если перед VPS есть CDN или балансировщик, BunkerWeb получает TCP-соединение от него. Нужно настроить доверенные proxy-сети и корректный заголовок, например CF-Connecting-IP или X-Forwarded-For, только для известных адресов прокси. Нельзя безусловно доверять этим заголовкам от любого клиента: злоумышленник сможет подменить IP. После настройки проверьте access log и убедитесь, что адрес клиента определяется правильно.
После включения WAF легитимные запросы получают 403. Что делать?
Найдите конкретное правило и URI в журнале BunkerWeb, затем воспроизведите запрос на тестовом окружении. Не отключайте весь WAF: добавьте точечное исключение для конкретного правила, параметра или маршрута. Частые причины — JSON-запросы API, загрузка файлов, нестандартные cookie и административные формы. Перед изменением исключения проверьте, что приложение не отправляет действительно опасный payload и что лимиты размера запроса настроены ожидаемым образом.
10. Выводы и следующие шаги
В результате BunkerWeb принимает внешний HTTP/HTTPS-трафик, автоматически обслуживает TLS, фильтрует запросы WAF и проксирует их к приложению во внутренней Docker-сети. Ubuntu firewall, Fail2ban и CrowdSec добавляют защиту хоста и автоматическую реакцию на подозрительную активность.
Дальше стоит настроить мониторинг доступности и сроков сертификатов, проверить восстановление из Restic-бэкапа и вынести базу данных либо тяжёлые приложения на отдельные ресурсы. При росте нагрузки измеряйте p95-задержку, количество 4xx/5xx, загрузку CPU и объём логов, а затем переходите к нескольким WAF-узлам, балансировке и отдельному резервному серверу.