SafeLine WAF: Полное руководство - Часть 3
Заключительная часть статьи
Часть 1: Введение, ошибки, требования
Часть 2: Установка и конфигурация
Часть 3: Backend, мониторинг, troubleshooting ← ВЫ ЗДЕСЬ
7. Добавление Backend Сервисов
7.1 Правильный паттерн добавления сервисов
🎯 Главный принцип: Backend сервисы используют
expose, НЕports!
Анатомия правильного сервиса:
services:
# ✅ ПРАВИЛЬНО
my-webapp:
container_name: my-webapp
image: nginx:alpine
restart: always
# КЛЮЧЕВОЕ: expose, НЕ ports!
expose:
- "80" # Доступен ТОЛЬКО внутри Docker network
# Та же сеть что и SafeLine
networks:
- kodalinet
volumes:
- ./my-site:/usr/share/nginx/html:ro
# Best practice: resource limits
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
# ❌ НЕПРАВИЛЬНО
bad-service:
ports:
- "8080:80" # Порт торчит наружу! НЕТ защиты WAF!
7.2 Практический пример: WordPress
docker-compose.yml (добавить в конец):
# WordPress + MySQL
wordpress-db:
container_name: wordpress-db
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${WP_DB_ROOT_PASSWORD}
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: ${WP_DB_PASSWORD}
volumes:
- ${SAFELINE_DIR}/data/wordpress-db:/var/lib/mysql
networks:
- kodalinet
deploy:
resources:
limits: {cpus: '2.0', memory: 2G}
wordpress:
container_name: wordpress
image: wordpress:latest
restart: always
depends_on:
- wordpress-db
environment:
WORDPRESS_DB_HOST: wordpress-db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${WP_DB_PASSWORD}
WORDPRESS_DB_NAME: wordpress
volumes:
- ${SAFELINE_DIR}/data/wordpress:/var/www/html
# ВАЖНО: expose, не ports!
expose:
- "80"
networks:
- kodalinet
deploy:
resources:
limits: {cpus: '2.0', memory: 1G}
В .env добавить:
# WordPress passwords
WP_DB_ROOT_PASSWORD=<strong-password>
WP_DB_PASSWORD=<strong-password>
Запуск:
# Создаём директории
mkdir -p /app/safeline/data/{wordpress,wordpress-db}
# Запускаем
docker compose up -d wordpress wordpress-db
# Проверяем
docker compose ps | grep wordpress
Настройка в SafeLine UI:
- Web Services → Add Site
- Заполните:
Site Name: WordPress Domain: blog.quick-fly-space.shop Port: 443 SSL: Enabled SSL Certificate: [Let's Encrypt cert] Upstream Servers: - Protocol: HTTP - Address: wordpress ← Container name! - Port: 80 - Protection Settings:
☑ Enable WAF ☑ SQL Injection Protection ☑ XSS Protection Mode: Balanced - Save
Проверка:
# Должен открыться WordPress
curl -I https://blog.quick-fly-space.shop
# Проверка WAF (должен заблокировать)
curl "https://blog.quick-fly-space.shop/?id=1' OR '1'='1"
# Должен вернуть SafeLine block page!
7.3 Пример: API Server (Node.js)
docker-compose.yml:
api-server:
container_name: api-server
build: ./api # Или используйте готовый образ
restart: always
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PASSWORD=${API_DB_PASSWORD}
expose:
- "3000"
networks:
- kodalinet
depends_on:
- postgres
deploy:
resources:
limits: {cpus: '1.0', memory: 512M}
SafeLine UI:
Domain: api.quick-fly-space.shop
Upstream: api-server:3000
Protection:
☑ API Protection Mode
☑ Rate Limiting: 1000 req/min
☑ Bot Protection
7.4 Пример: Static Site (Hugo/Jekyll)
docker-compose.yml:
static-site:
container_name: static-site
image: nginx:alpine
restart: always
volumes:
- ./static-site/public:/usr/share/nginx/html:ro
expose:
- "80"
networks:
- kodalinet
deploy:
resources:
limits: {cpus: '0.5', memory: 256M}
SafeLine UI:
Domain: www.quick-fly-space.shop
Upstream: static-site:80
Advanced → Caching:
☑ Enable Caching
Cache TTL: 3600 seconds
Cache Key: $scheme$request_method$host$request_uri
8. Мониторинг и Логирование
8.1 Мониторинг через SafeLine Dashboard
Доступ к статистике:
-
Dashboard → Overview
- Total Requests (сегодня/неделя/месяц)
- Blocked Attacks
- Top Attacked Sites
- Attack Types Distribution
-
Attacks → Attack Logs
- Real-time attack log
- Фильтры: по типу атаки, IP, домену
- Export в CSV
-
Web Services → Sites → [Your Site] → Statistics
- Requests per minute (график)
- Response time
- Status codes distribution
- Bandwidth usage
8.2 Логи Docker контейнеров
Просмотр логов:
# Все контейнеры (real-time)
docker compose logs -f
# Конкретный контейнер
docker logs safeline-tengine -f
# Последние 100 строк
docker logs safeline-tengine --tail 100
# С timestamp
docker logs safeline-tengine -f --timestamps
# За период
docker logs safeline-tengine --since 1h # Последний час
docker logs safeline-tengine --since 2026-01-27T10:00:00
Важные логи:
# Tengine (HTTP access & errors)
docker logs safeline-tengine | grep -E "GET|POST|error"
# Detector (WAF events)
docker logs safeline-detector | grep -i attack
# PostgreSQL (database)
docker logs safeline-pg | grep -i error
# Management (UI operations)
docker logs safeline-mgt
8.3 Анализ логов на диске
# Access logs (все HTTP запросы)
tail -f /app/safeline/logs/nginx/access.log
# Error logs (ошибки Nginx)
tail -f /app/safeline/logs/nginx/error.log
# Detector logs (WAF events)
tail -f /app/safeline/logs/detector/*.log
# Grep паттерны:
# SQL injection попытки
grep -i "sql" /app/safeline/logs/nginx/access.log
# XSS попытки
grep -i "<script" /app/safeline/logs/nginx/access.log
# 5xx errors (проблемы на backend)
grep " 50[0-9] " /app/safeline/logs/nginx/access.log
8.4 External мониторинг (опционально)
Prometheus + Grafana:
# docker-compose.yml (добавить)
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
expose:
- "9090"
networks:
- kodalinet
grafana:
image: grafana/grafana
volumes:
- grafana-data:/var/lib/grafana
expose:
- "3000"
networks:
- kodalinet
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
volumes:
prometheus-data:
grafana-data:
Metrics endpoints:
SafeLine НЕ экспортирует Prometheus metrics нативно, но можно использовать:
- nginx-prometheus-exporter для Tengine metrics
- postgres-exporter для PostgreSQL metrics
- node-exporter для системных метрик
8.5 Alerts (уведомления)
Email уведомления (в скрипте ssl-renew.sh):
send_notification() {
local subject="$1"
local body="$2"
if [ -n "$NOTIFY_EMAIL" ]; then
echo "$body" | mail -s "$subject" "$NOTIFY_EMAIL"
fi
}
# Использование:
send_notification "SafeLine Alert" "SSL certificate expires in 7 days!"
Telegram уведомления:
# telegram-notify.sh
#!/bin/bash
BOT_TOKEN="your-bot-token"
CHAT_ID="your-chat-id"
send_telegram() {
local message="$1"
curl -s -X POST \
"https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d chat_id="${CHAT_ID}" \
-d text="${message}"
}
# Использование:
send_telegram "⚠️ SafeLine: SSL certificate renewal failed!"
Slack webhook:
# slack-notify.sh
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
send_slack() {
local message="$1"
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"${message}\"}" \
"${WEBHOOK_URL}"
}
9. Backup и Disaster Recovery
9.1 Что нужно бэкапить
┌─────────────────────────────────────────────┐
│ Критичные данные для бэкапа │
├─────────────────────────────────────────────┤
│ │
│ 🔴 CRITICAL (бэкапить ОБЯЗАТЕЛЬНО): │
│ • PostgreSQL database │
│ (/app/safeline/resources/postgres/) │
│ • SSL сертификаты │
│ (/app/safeline/resources/nginx/certs/)│
│ • SafeLine конфигурация │
│ (/app/safeline/resources/mgt/) │
│ │
│ 🟡 IMPORTANT (рекомендуется): │
│ • docker-compose.yml │
│ • .env (ЗАШИФРОВАННЫЙ!) │
│ • Backend данные (WordPress, etc) │
│ │
│ 🟢 OPTIONAL: │
│ • Логи (если нужна forensics) │
│ • Cache (можно не бэкапить) │
│ │
└─────────────────────────────────────────────┘
9.2 Backup скрипт
Автоматический бэкап:
#!/bin/bash
# backup-safeline.sh
set -euo pipefail
BACKUP_DIR="/app/safeline/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="safeline-backup-${DATE}"
echo "=== SafeLine Backup Started: $DATE ==="
# 1. PostgreSQL dump
echo "Backing up PostgreSQL..."
docker exec safeline-pg pg_dump -U safeline-ce safeline-ce > \
"${BACKUP_DIR}/${BACKUP_NAME}-postgres.sql"
# 2. Конфигурация
echo "Backing up configuration..."
tar -czf "${BACKUP_DIR}/${BACKUP_NAME}-config.tar.gz" \
/app/safeline/resources/mgt \
/app/safeline/resources/nginx/certs \
~/safeline-deployment/docker-compose.yml
# 3. Шифруем .env (содержит пароли!)
echo "Encrypting .env..."
gpg -c --batch --yes --passphrase-file /root/.backup-passphrase \
-o "${BACKUP_DIR}/${BACKUP_NAME}-env.gpg" \
~/safeline-deployment/.env
# 4. Удаляем старые бэкапы (>7 дней)
echo "Cleaning old backups..."
find ${BACKUP_DIR} -name "safeline-backup-*" -mtime +7 -delete
echo "=== Backup Complete ==="
echo "Files:"
ls -lh ${BACKUP_DIR}/${BACKUP_NAME}-*
Настройка:
# Копируем скрипт
sudo cp backup-safeline.sh /usr/local/bin/safeline-backup
sudo chmod +x /usr/local/bin/safeline-backup
# Создаём passphrase для шифрования
echo "your-strong-passphrase" | sudo tee /root/.backup-passphrase
sudo chmod 600 /root/.backup-passphrase
# Добавляем в cron (каждый день в 2:00)
sudo crontab -e
# Добавить:
0 2 * * * /usr/local/bin/safeline-backup >> /var/log/safeline-backup.log 2>&1
9.3 Восстановление из бэкапа
Scenario: Полное восстановление
#!/bin/bash
# restore-safeline.sh
BACKUP_DATE="20260127_140000" # Измените на нужную дату
BACKUP_DIR="/app/safeline/backups"
echo "=== SafeLine Restore Started ==="
# 1. Останавливаем SafeLine
cd ~/safeline-deployment
docker compose down
# 2. Восстанавливаем PostgreSQL
echo "Restoring PostgreSQL..."
cat "${BACKUP_DIR}/safeline-backup-${BACKUP_DATE}-postgres.sql" | \
docker exec -i safeline-pg psql -U safeline-ce safeline-ce
# 3. Восстанавливаем конфигурацию
echo "Restoring configuration..."
tar -xzf "${BACKUP_DIR}/safeline-backup-${BACKUP_DATE}-config.tar.gz" -C /
# 4. Расшифровываем и восстанавливаем .env
echo "Restoring .env..."
gpg --decrypt --batch --passphrase-file /root/.backup-passphrase \
"${BACKUP_DIR}/safeline-backup-${BACKUP_DATE}-env.gpg" > \
~/safeline-deployment/.env
# 5. Запускаем SafeLine
echo "Starting SafeLine..."
docker compose up -d
echo "=== Restore Complete ==="
echo "Check status:"
docker compose ps
9.4 Offsite Backup (S3, rsync)
AWS S3:
# Установка AWS CLI
sudo apt install awscli -y
# Настройка
aws configure
# Синхронизация бэкапов
aws s3 sync /app/safeline/backups/ s3://your-bucket/safeline-backups/
# Автоматизация (в cron после локального бэкапа):
0 3 * * * aws s3 sync /app/safeline/backups/ s3://your-bucket/safeline-backups/
rsync на другой сервер:
# Синхронизация
rsync -avz --delete \
/app/safeline/backups/ \
backup-server:/backups/safeline/
# Автоматизация (SSH key без пароля):
ssh-keygen -t ed25519
ssh-copy-id backup-server
# Cron:
0 3 * * * rsync -avz /app/safeline/backups/ backup-server:/backups/safeline/
10. Troubleshooting
10.1 Диагностика проблем
Checklist первой помощи:
# 1. Проверка статуса контейнеров
docker compose ps
# 2. Проверка логов
docker compose logs --tail 50
# 3. Проверка сети
docker network inspect kodalinet
# 4. Проверка портов
sudo netstat -tlnp | grep -E ':80|:443'
# 5. Проверка ресурсов
docker stats --no-stream
# 6. Проверка диска
df -h /app/safeline
10.2 Типовые проблемы
Проблема: "502 Bad Gateway"
Причины:
- Backend сервис не запущен
- Неправильный upstream адрес
- Backend упал из-за OOM
Диагностика:
# Проверка backend
docker ps | grep my-webapp
# Проверка логов backend
docker logs my-webapp
# Проверка connectivity из tengine
docker exec safeline-tengine ping my-webapp
# Проверка memory
docker stats my-webapp --no-stream
Решение:
# Если упал - перезапустить
docker restart my-webapp
# Если OOM - увеличить limits
# В docker-compose.yml:
deploy:
resources:
limits:
memory: 1G # Было 512M
Проблема: "Too many open files"
Симптомы:
[error] 123#123: accept() failed (24: Too many open files)
Причина: ulimits не установлен или слишком мал
Решение:
# Проверка текущего значения
docker exec safeline-tengine sh -c "ulimit -n"
# Если меньше 65536 - исправить в docker-compose.yml:
tengine:
ulimits:
nofile:
soft: 65536
hard: 131072
# Перезапустить
docker compose up -d --force-recreate safeline-tengine
Проблема: "SSL certificate expired"
Симптомы: Браузер показывает "NET::ERR_CERT_DATE_INVALID"
Проверка:
# Срок действия сертификата
echo | openssl s_client -connect quick-fly-space.shop:443 2>/dev/null | \
openssl x509 -noout -dates
# Проверка файлов
ls -la /app/safeline/resources/nginx/certs/
Решение:
# Обновить вручную
sudo /usr/local/bin/safeline-ssl-renew
# Проверить cron
sudo crontab -l | grep safeline-ssl-renew
# Проверить логи renewal
tail -f /var/log/safeline-ssl-renew.log
Проблема: "Out of memory"
Симптомы: Контейнеры падают, в логах хоста:
Out of memory: Killed process 1234 (docker-containerd)
Диагностика:
# Использование memory
docker stats --no-stream
# System memory
free -h
# Какие процессы жрут RAM
ps aux --sort=-%mem | head -20
Решение:
# Временно: Увеличить swap
sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# Долгосрочно: Добавить RAM или уменьшить limits
Проблема: "Database connection failed"
Симптомы: SafeLine UI недоступен, в логах mgt:
Error: Connection refused postgres:5432
Диагностика:
# PostgreSQL запущен?
docker ps | grep safeline-pg
# PostgreSQL healthy?
docker inspect safeline-pg | grep -A5 Health
# Логи PostgreSQL
docker logs safeline-pg
Решение:
# Если unhealthy - рестарт
docker restart safeline-pg
# Если проблема с данными - восстановить из бэкапа
10.3 Emergency Recovery
Сценарий: Полный крах системы
# 1. STOP PANIC! Дышим глубоко 😤
# 2. Собираем информацию
docker compose ps > /tmp/docker-status.txt
docker compose logs > /tmp/docker-logs.txt
df -h > /tmp/disk-usage.txt
free -h > /tmp/memory-usage.txt
# 3. Пробуем soft restart
docker compose restart
# 4. Если не помогло - hard restart
docker compose down
docker compose up -d
# 5. Если и это не помогло - восстановление из бэкапа
# См. раздел 9.3
# 6. Последняя надежда - переустановка
docker compose down -v # УДАЛЯЕТ данные!
# Восстановить из бэкапа
# Запустить заново
11. Production Checklist
11.1 Перед деплоем
### Инфраструктура
- [ ] Docker >= 24.0 установлен
- [ ] Docker Compose >= 2.20 установлен
- [ ] Порты 80/443 свободны
- [ ] Сеть kodalinet создана
- [ ] Директории /app/safeline созданы
- [ ] Firewall настроен (80/443 открыты)
### Конфигурация
- [ ] .env файл создан
- [ ] POSTGRES_PASSWORD изменён (>16 символов)
- [ ] .env защищён (chmod 600)
- [ ] docker-compose.yml проверен
- [ ] Resource limits установлены
- [ ] Ulimits для tengine = 131072
### DNS и SSL
- [ ] DNS A record настроен
- [ ] DNS резолвится (dig проверка)
- [ ] SSL сертификат получен
- [ ] SSL renewal скрипт установлен
- [ ] Cron для SSL настроен
### Безопасность
- [ ] MGT порт НЕ открыт наружу
- [ ] SSH доступен только для вашего IP
- [ ] Fail2ban установлен
- [ ] UFW включен
- [ ] Backend сервисы используют expose
### Мониторинг
- [ ] Логирование настроено
- [ ] Бэкап скрипт установлен
- [ ] Cron для бэкапа настроен
- [ ] Offsite бэкап настроен (опционально)
- [ ] Alerts настроены (email/telegram)
11.2 После деплоя
### Функциональность
- [ ] Все контейнеры UP (docker compose ps)
- [ ] Все healthchecks HEALTHY
- [ ] MGT UI доступен (через SSH tunnel)
- [ ] HTTP → HTTPS редирект работает
- [ ] SSL сертификат валидный (browser check)
- [ ] Backend сервисы доступны через SafeLine
### WAF Testing
- [ ] SQL injection блокируется
curl "https://site.com/?id=1' OR '1'='1"
- [ ] XSS блокируется
curl "https://site.com/?q=<script>alert('xss')</script>"
- [ ] Path traversal блокируется
curl "https://site.com/../../etc/passwd"
- [ ] Attack logs появляются в Dashboard
### Производительность
- [ ] Latency приемлемая (<50ms overhead)
- [ ] CPU usage нормальный (<50% idle)
- [ ] Memory usage стабильный
- [ ] Disk I/O не упирается
### Бэкапы
- [ ] Ручной бэкап успешен
- [ ] Автобэкап работает (cron)
- [ ] Restore из бэкапа протестирован
- [ ] Offsite бэкап работает
11.3 Регулярное обслуживание
Ежедневно (автоматически):
✓ SSL renewal check (cron 3:00)
✓ Backup (cron 2:00)
✓ Offsite sync (cron 3:00)
Еженедельно:
- Проверка логов на ошибки
- Проверка disk usage
- Проверка attack statistics
- Обновление Docker images (при наличии)
Ежемесячно:
- Проверка SSL срока действия
- Тест восстановления из бэкапа
- Review и cleanup старых логов
- Security audit (проверка exposed ports)
- Обновление системы (apt update && upgrade)
Ежеквартально:
- Обновление SafeLine до новой версии
- Performance review
- Capacity planning
- Disaster recovery drill (полный тест)
12. Заключение
12.1 Что мы достигли
✅ Production-ready SafeLine WAF развёрнут
✅ Все критические ошибки исправлены
✅ SSL автоматизирован (Let's Encrypt)
✅ Security hardened (firewall, no exposed MGT)
✅ Monitoring настроен
✅ Backup strategy реализована
✅ Troubleshooting guide доступен
12.2 Key Takeaways
💡 Главные уроки этого проекта:
-
Peer review спасает жизни
- Не бойтесь просить feedback
- Критика ≠ личная атака
- Ошибки - это обучение
-
Production ≠ Development
- Resource limits обязательны
- Ulimits критичны
- Security by default
-
Автоматизация - не опция
- SSL renewal ДОЛЖЕН быть автоматизирован
- Backup ДОЛЖЕН быть автоматизирован
- Monitoring ДОЛЖЕН быть настроен
-
Documentation saves time
- Документируйте ПОЧЕМУ, не только ЧТО
- Будущий ты скажет спасибо
- Команда скажет спасибо
-
Никогда не прекращайте учиться
- Технологии меняются
- Best practices эволюционируют
- Непрерывное обучение = успех
12.3 Дополнительные ресурсы
Официальная документация:
Community:
Блоги и статьи:
12.4 Обратная связь
Нашли ошибку? Создайте issue в репозитории.
Есть вопросы? Задайте в комментариях или Discord.
Помогло? Поделитесь статьёй с коллегами!
12.5 Личное послесловие
К читателям этой статьи:
Если вы дочитали до сюда - спасибо за внимание и терпение! 🙏
Эта статья родилась из реальной ошибки и критического feedback. Без вопроса коллеги "почему два reverse proxy?" я бы развернул сломанное решение.
Не бойтесь ошибаться. Бойтесь не учиться на ошибках.
Не бойтесь просить помощи. Даже senior инженеры не знают всего.
Документируйте свой опыт. Эта статья - не только техническое руководство, но и история обучения.
Надеюсь, этот материал сэкономит вам часы debugging и поможет избежать production инцидентов.
Stay curious, stay humble, keep learning! 🚀
— [Ваше имя], DevOps Engineer
Версия: 2.0
Последнее обновление: 27 января 2026
Статус: ✅ Production-Ready
Навигация по статье
📖 Часть 1: Введение, ошибки, требования
📖 Часть 2: Установка и конфигурация
📖 Часть 3: Backend, мониторинг, troubleshooting ← ВЫ ЗДЕСЬ
📎 Дополнительные файлы и скрипты
❓ FAQ
🆘 Quick Reference
🎯 Конец статьи. Спасибо за чтение!