Skip to main content

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:

  1. Web Services → Add Site
  2. Заполните:
    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
    
  3. Protection Settings:
    ☑ Enable WAF
    ☑ SQL Injection Protection
    ☑ XSS Protection
    Mode: Balanced
    
  4. 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

Доступ к статистике:

  1. Dashboard → Overview

    • Total Requests (сегодня/неделя/месяц)
    • Blocked Attacks
    • Top Attacked Sites
    • Attack Types Distribution
  2. Attacks → Attack Logs

    • Real-time attack log
    • Фильтры: по типу атаки, IP, домену
    • Export в CSV
  3. 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"

Причины:

  1. Backend сервис не запущен
  2. Неправильный upstream адрес
  3. 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

💡 Главные уроки этого проекта:

  1. Peer review спасает жизни

    • Не бойтесь просить feedback
    • Критика ≠ личная атака
    • Ошибки - это обучение
  2. Production ≠ Development

    • Resource limits обязательны
    • Ulimits критичны
    • Security by default
  3. Автоматизация - не опция

    • SSL renewal ДОЛЖЕН быть автоматизирован
    • Backup ДОЛЖЕН быть автоматизирован
    • Monitoring ДОЛЖЕН быть настроен
  4. Documentation saves time

    • Документируйте ПОЧЕМУ, не только ЧТО
    • Будущий ты скажет спасибо
    • Команда скажет спасибо
  5. Никогда не прекращайте учиться

    • Технологии меняются
    • 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


🎯 Конец статьи. Спасибо за чтение!