Встановлення 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
[вебзастосунок: 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. Встановлення ПЗ — покроково
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-вузлів, балансування й окремого резервного сервера.