Instalación de BunkerWeb WAF en un VPS: protección de sitios, SSL, Docker y CrowdSec
TL;DR
BunkerWeb es un firewall web con proxy inverso que se coloca delante de los sitios, recibe tráfico HTTPS, aplica reglas WAF y reenvía las solicitudes a las aplicaciones. En esta guía instalaremos BunkerWeb en Docker sobre Ubuntu Server 24.04 LTS, conectaremos un sitio mediante reverse proxy, configuraremos TLS automático con Let's Encrypt, protección básica contra bots e integración con CrowdSec.
- Usaremos Ubuntu Server 24.04 LTS, Docker Engine 28.x y Docker Compose v2.
- BunkerWeb será el único punto de entrada público en los puertos 80 y 443.
- La aplicación permanecerá en una red interna de Docker y no estará disponible directamente desde Internet.
- Los certificados de Let's Encrypt se emitirán y renovarán automáticamente.
- CrowdSec analizará los registros y bloqueará las direcciones IP con mala reputación.
- Las configuraciones, los secretos y los datos se mantendrán en archivos separados y se incluirán en las copias de seguridad.
3. Qué configuramos y por qué
El esquema habitual de publicación de un sitio es el siguiente: un cliente de Internet se conecta directamente al servidor web de la aplicación. Si la aplicación contiene una vulnerabilidad, procesa las solicitudes de forma incorrecta o sufre un ataque de fuerza bruta contra las contraseñas, el ataque llega hasta ella sin ningún filtro adicional. BunkerWeb cambia esta arquitectura: se convierte en un proxy inverso y WAF entre el cliente y la aplicación.
Un WAF, o Web Application Firewall, analiza las solicitudes HTTP antes de reenviarlas al backend. Puede descartar URI sospechosas, intentos de inyección SQL, XSS, traversal de directorios, ataques automatizados y otros ataques habituales. Esto no sustituye la actualización del CMS, el framework y el sistema operativo, pero reduce la superficie de ataque y añade una capa de protección independiente.
Esquema final
Интернет
|
| TCP 80/443
v
[BunkerWeb WAF]
|
| внутренняя Docker-сеть
v
[web-приложение: nginx, Node.js, PHP-FPM, контейнер SaaS]
|
v
[База данных и хранилище]
En nuestro ejemplo, el dominio será example.com y la aplicación estará disponible dentro de Docker con el nombre app en el puerto 8080. Sustituya este dominio y estos parámetros por los suyos. Los registros DNS deben apuntar a la dirección IPv4 pública del servidor antes de emitir el certificado.
Qué obtendremos después de la configuración
- Los puertos de la aplicación no se publicarán mediante Docker en la interfaz externa.
- Las solicitudes HTTP serán recibidas por BunkerWeb y el certificado HTTPS se gestionará automáticamente.
- El sitio tendrá activadas las reglas básicas de ModSecurity/OWASP CRS mediante la plantilla de BunkerWeb.
- Los registros de solicitudes se guardarán en un volumen de Docker y estarán disponibles para el análisis de CrowdSec.
- El firewall del sistema solo permitirá SSH, HTTP y HTTPS.
- La actualización de los contenedores y las copias de seguridad podrán realizarse independientemente de la aplicación.
Self-hosted o WAF en la nube
Los servicios WAF en la nube reciben el tráfico en su propia infraestructura y lo envían mediante proxy al servidor origin. Esto resulta conveniente con grandes volúmenes, protección distribuida contra DDoS y la necesidad de minimizar el mantenimiento propio. Sin embargo, será necesario transferir el DNS y parte del control de red a un operador externo, mientras que el coste y las limitaciones dependerán del plan contratado.
El BunkerWeb self-hosted es adecuado cuando se necesita un control total sobre la configuración, los registros y el lugar donde se procesan los datos. El propietario del VPS es responsable de las actualizaciones, la disponibilidad, las copias de seguridad y la protección del origin. Para uno o varios sitios en un servidor pequeño, esta opción suele ser más sencilla y transparente que montar por cuenta propia Nginx, ModSecurity, rate limiting y un servicio independiente de gestión de certificados.
Es importante comprender los límites de la solución: BunkerWeb no ofrece una protección completa contra un potente ataque DDoS volumétrico, no corrige las vulnerabilidades de la aplicación y no sustituye el control de acceso a los paneles administrativos. Para un servicio crítico se necesitan monitorización, copias de seguridad independientes y un plan de recuperación.
4. Qué configuración de VPS se necesita para esta tarea
BunkerWeb funciona como reverse proxy, por lo que sus requisitos dependen no solo del propio WAF, sino también de la cantidad de sitios, el tamaño de las solicitudes, la carga TLS, las reglas activadas y el rendimiento del backend. Para un sitio pequeño, el WAF consume pocos recursos, pero se necesita memoria RAM adicional para Docker, CrowdSec, el registro y las actualizaciones.
| Escenario | CPU | RAM | Disco | Red |
|---|---|---|---|---|
| Sitio de prueba | 1 vCPU | 2 GB | 25 GB SSD | 100 Mbit/s |
| Sitio production pequeño | 2 vCPU | 4 GB | 40–60 GB NVMe | 100–500 Mbit/s |
| Varios sitios y CrowdSec | 4 vCPU | 8 GB | 80–120 GB NVMe | 500 Mbit/s o más |
La configuración práctica mínima para production es de 2 vCPU, 4 GB de RAM y 40 GB de SSD. Si en el mismo VPS también funcionarán una base de datos, un CMS, CI/CD, un panel de control o varias aplicaciones, es mejor elegir desde el principio 4 vCPU y 8 GB de RAM. Para Docker, conviene dejar al menos 1 GB de memoria libre después de iniciar todos los contenedores.
Como opción concreta, puede contratar un VPS adecuado con 2–4 vCPU, 4–8 GB de RAM, un disco NVMe de al menos 40 GB y una IPv4 pública. Compruebe que el proveedor permita conexiones entrantes en TCP 80 y 443, proporcione una consola de recuperación y permita crear snapshots o conectar almacenamiento externo.
Cuándo se necesita un dedicated
Un servidor dedicado está justificado si a través del WAF pasa un flujo constante y elevado de solicitudes, se necesitan varias decenas de sitios, se realiza un cifrado TLS intensivo o la máquina aloja servicios backend que consumen muchos recursos. Un dedicated también resulta conveniente cuando se requieren IOPS garantizados, aislamiento de otros usuarios y equipos de red propios.
Para un sitio, un panel de administración, una API o un equipo pequeño, un dedicated suele ser excesivo. Primero mida la carga de CPU, RAM, disco y red. Si la carga media es inferior al 50 % y los picos no provocan swap ni latencia, pasar a un servidor físico no aportará un beneficio apreciable.
Elección de la ubicación
La ubicación influye en la latencia entre los visitantes y el WAF, la ruta hasta el backend y los requisitos de almacenamiento de datos. Elija una región cercana a su audiencia principal, pero tenga en cuenta la disponibilidad de IPv4, la calidad de las conexiones y las normas de tratamiento de datos personales. Si la aplicación y la base de datos se encuentran en regiones diferentes, la latencia entre ellas puede ser más notable que la latencia entre el usuario y el WAF.
5. Preparación del servidor
A continuación se supone un VPS limpio con Ubuntu Server 24.04 LTS y acceso mediante el usuario root. Todos los comandos se ejecutan en el servidor. Sustituya el ejemplo por su propia clave SSH pública y no cierre la sesión SSH actual hasta comprobar el nuevo usuario.
Creación del usuario e instalación de la clave 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
Compruebe el acceso en otra ventana de terminal:
# Выполняется на локальном компьютере
ssh deploy@SERVER_IP
Después de comprobarlo correctamente, puede prohibir el acceso de root y la autenticación mediante contraseña. Antes de hacerlo, asegúrese de que la clave funciona y de que la consola web está disponible en el panel del proveedor:
# Открываем конфигурацию SSH
sudoedit /etc/ssh/sshd_config.d/ hardening.conf
En el comando anterior no debe haber un espacio en el nombre del archivo. Utilice la variante correcta:
# Создаём отдельный файл настроек 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
Actualización del sistema y paquetes básicos
# Обновляем индекс репозиториев и пакеты 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 y Fail2ban
Abra SSH antes de activar UFW. Si SSH funciona en otro puerto, sustituya la regla 22/tcp. Los puertos 80 y 443 serán necesarios para que BunkerWeb gestione HTTP y 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 protegerá SSH contra ataques de fuerza bruta simples. BunkerWeb y CrowdSec se encargarán del tráfico web, por lo que no es necesario añadir sin criterio decenas de filtros y bloquear a usuarios legítimos.
# Включаем стандартную защиту SSH
sudo systemctl enable --now fail2ban
# Проверяем состояние jail
sudo fail2ban-client status
sudo fail2ban-client status sshd
6. Instalación del software — paso a paso
6. Instalación del software — paso a paso
En 2026, utilice Docker Engine desde el repositorio oficial de Docker, no el paquete obsoleto del repositorio base de Ubuntu. Es preferible fijar la versión de BunkerWeb en una etiqueta estable concreta de la serie 1.6.x en lugar de utilizar latest. Antes del despliegue en producción, compruebe la etiqueta estable actualizada en la documentación oficial del proyecto y sustituya el valor de la variable BUNKERWEB_VERSION.
Paso 1. Instalación de 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
No añada el usuario al grupo docker sin necesidad: un miembro de este grupo obtiene de facto privilegios de root. En adelante, ejecute los comandos de Docker mediante sudo o acepte conscientemente este riesgo.
Paso 2. Creación del directorio del proyecto
# Создаём каталог конфигурации BunkerWeb
sudo install -d -m 750 -o deploy -g deploy /opt/bunkerweb
# Переключаемся на администратора проекта
sudo -iu deploy
# Переходим в рабочий каталог
cd /opt/bunkerweb
# Создаём каталоги для данных и логов
mkdir -p data logs backups
Paso 3. Creación del archivo de secretos
# Создаём файл переменных окружения с закрытыми правами
touch /opt/bunkerweb/.env
chmod 600 /opt/bunkerweb/.env
# Генерируем случайный пароль для административной базы
openssl rand -base64 32
Guarde el resultado en el gestor de contraseñas. Más adelante, esta contraseña se utilizará únicamente para los servicios internos. No publique el archivo .env en Git ni introduzca secretos reales en el archivo compose.
Paso 4. Preparación de Docker Compose
Para una instalación sencilla utilizamos BunkerWeb y scheduler, y representamos la aplicación independiente mediante el contenedor app. En producción, sustituya la imagen de demostración por su propio backend. BunkerWeb debe encontrarse en la misma red de Docker que la aplicación.
# Переходим в каталог проекта
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
El contenedor de demostración whoami escucha en el puerto 80, por lo que el valor de REVERSE_PROXY_URL en el archivo compose real debe sustituirse por http://app:80. Esto es importante: el nombre del servicio y el puerto deben coincidir con la dirección real del backend.
Paso 5. Inicio de los contenedores
# Загружаем переменные и проверяем итоговую модель 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
Paso 6. Instalación de CrowdSec
CrowdSec consta de un analizador de comportamiento y un componente de toma de decisiones. Lee los registros, relaciona los eventos con escenarios y crea decisiones de bloqueo de IP. En este esquema, BunkerWeb escribe registros JSON en un volumen, mientras que CrowdSec se instala en el host. En producción, es más conveniente montar el registro en un directorio independiente disponible únicamente en modo de solo lectura para el agente de 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
Instale las colecciones para el servidor web y Linux. Los nombres de las colecciones pueden cambiar, por lo que primero realice una búsqueda:
# Обновляем индекс коллекций
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
En el siguiente paso debe indicar a CrowdSec la ruta real de los registros de BunkerWeb. Si se utiliza un volumen de Docker, determine primero su punto de montaje:
# Показываем имя volume с логами
sudo docker volume ls | grep bunkerweb
# Получаем путь volume на хосте
sudo docker volume inspect bunkerweb_bunkerweb_logs
Añada la ruta del campo Mountpoint a la configuración de la fuente de CrowdSec. Los nombres concretos de los archivos de registro dependen de la versión y de la plantilla de BunkerWeb, por lo que primero debe localizarlos:
# Показываем файлы логов внутри контейнера
sudo docker compose exec bunkerweb sh -c 'find /var/log/bunkerweb -maxdepth 2 -type f -print'
# Проверяем наличие событий
sudo docker compose logs --since=10m bunkerweb