Skip to main content

SafeLine WAF: Полное руководство - Часть 2

Продолжение статьи
Часть 1: Введение, ошибки, требования
Часть 2: Установка и конфигурация ← ВЫ ЗДЕСЬ
Часть 3: Backend сервисы, мониторинг


5. Установка и конфигурация

5.1 Подготовка конфигурационных файлов

Структура проекта:

# Создаём директорию для проекта
mkdir -p ~/safeline-deployment
cd ~/safeline-deployment

# Структура:
~/safeline-deployment/
├── docker-compose.yml    # Главный compose файл
├── .env                  # Конфигурация (секреты!)
├── .gitignore            # Не коммитим .env!
└── scripts/
    ├── init.sh           # Инициализация
    ├── backup.sh         # Бэкап
    └── ssl-renew.sh      # SSL renewal

5.2 Создание .env файла

🔒 SECURITY: Этот файл содержит пароли! Защитите его!

# Создаём .env
nano .env

Содержимое .env:

# ==================================================================================
# SafeLine Production Configuration
# ==================================================================================

# Базовая директория
SAFELINE_DIR=/app/safeline

# Префикс подсети
SUBNET_PREFIX=172.20.0

# PostgreSQL пароль (ОБЯЗАТЕЛЬНО СМЕНИТЕ!)
# Генерация: openssl rand -base64 32
POSTGRES_PASSWORD=ИЗМЕНИ_МЕНЯ_НА_СЛОЖНЫЙ_ПАРОЛЬ

# Порт Management UI (internal only)
MGT_PORT=9443

# Docker образы
IMAGE_TAG=latest              # Или конкретная версия: 9.2.7
IMAGE_PREFIX=chaitin          # Официальный registry
ARCH_SUFFIX=                  # Пусто для x86_64, "-arm" для ARM64
REGION=-g                     # -g для international
MGT_PROXY=0                   # 0 = SafeLine первый reverse proxy

🔑 Генерация сложного пароля:

# Способ 1: openssl
openssl rand -base64 32

# Способ 2: pwgen (если установлен)
pwgen -sync 32 1

# Способ 3: /dev/urandom
tr -dc 'A-Za-z0-9!@#$%^&*' </dev/urandom | head -c 32; echo

Пример сильного пароля:

POSTGRES_PASSWORD=Kx9#mP2$vL8@nQ4&wR7%tY5^uI3!oE6

🔒 Защита .env файла:

# Только владелец может читать
chmod 600 .env

# Не коммитим в Git
echo ".env" >> .gitignore

5.3 Создание docker-compose.yml

📄 Используйте готовый файл из /safeline-production-v2/docker-compose.yml

Или создайте вручную:

nano docker-compose.yml

Ключевые моменты (подробности в файле):

version: '3.8'

# СЕТЬ: external (создана заранее!)
networks:
  kodalinet:
    external: true

services:
  # PostgreSQL - база данных
  postgres:
    container_name: safeline-pg
    image: ${IMAGE_PREFIX}/safeline-postgres${ARCH_SUFFIX}:15.2
    restart: always
    volumes:
      - ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro
    environment:
      - POSTGRES_USER=safeline-ce
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    networks:
      kodalinet:
        ipv4_address: ${SUBNET_PREFIX}.2
    command: [postgres, -c, max_connections=600]
    healthcheck:
      test: pg_isready -U safeline-ce -d safeline-ce
    deploy:
      resources:
        limits: {cpus: '2.0', memory: 2G}
        reservations: {cpus: '0.5', memory: 512M}

  # Management - Web UI
  mgt:
    container_name: safeline-mgt
    image: ${IMAGE_PREFIX}/safeline-mgt${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
    restart: always
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/mgt:/app/data
      - ${SAFELINE_DIR}/logs/nginx:/app/log/nginx:z
      - ${SAFELINE_DIR}/resources/sock:/app/sock
      - /var/run:/app/run
    # SECURITY: только expose, НЕ ports!
    expose:
      - "1443"
    environment:
      - MGT_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
      - MGT_PROXY=${MGT_PROXY}
    depends_on:
      postgres:
        condition: service_healthy
      fvm:
        condition: service_started
    networks:
      kodalinet:
        ipv4_address: ${SUBNET_PREFIX}.4
    deploy:
      resources:
        limits: {cpus: '1.0', memory: 1G}

  # Detector - AI движок
  detect:
    container_name: safeline-detector
    image: ${IMAGE_PREFIX}/safeline-detector${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
    restart: always
    volumes:
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/logs/detector:/logs/detector
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/detector
    networks:
      kodalinet:
        ipv4_address: ${SUBNET_PREFIX}.5
    deploy:
      resources:
        limits: {cpus: '2.0', memory: 2G}

  # Tengine - REVERSE PROXY + WAF (ВХОДНАЯ ТОЧКА!)
  tengine:
    container_name: safeline-tengine
    image: ${IMAGE_PREFIX}/safeline-tengine${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
    restart: always
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/resolv.conf:/etc/resolv.conf:ro
      - ${SAFELINE_DIR}/resources/nginx:/etc/nginx
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/resources/chaos:/resources/chaos
      - ${SAFELINE_DIR}/logs/nginx:/var/log/nginx:z
      - ${SAFELINE_DIR}/resources/cache:/usr/local/nginx/cache
      - ${SAFELINE_DIR}/resources/sock:/app/sock
    # ЕДИНСТВЕННЫЕ открытые порты!
    ports:
      - "80:80"
      - "443:443"
    environment:
      - TCD_MGT_API=https://${SUBNET_PREFIX}.4:1443/api/open/publish/server
      - TCD_SNSERVER=${SUBNET_PREFIX}.5:8000
      - SNSERVER_ADDR=${SUBNET_PREFIX}.5:8000
      - CHAOS_ADDR=${SUBNET_PREFIX}.10
    # КРИТИЧНО для production!
    ulimits:
      nofile:
        soft: 65536
        hard: 131072
    networks:
      kodalinet:
        ipv4_address: ${SUBNET_PREFIX}.3
    depends_on:
      mgt:
        condition: service_healthy
      detect:
        condition: service_started
    deploy:
      resources:
        limits: {cpus: '4.0', memory: 4G}

  # Luigi, FVM, Chaos...
  # (остальные сервисы - см. полный файл)

5.4 Запуск SafeLine

Шаг 1: Проверка конфигурации

# Проверяем что .env правильный
cat .env | grep POSTGRES_PASSWORD
# НЕ должно быть "ИЗМЕНИ_МЕНЯ"!

# Проверяем compose
docker compose config

Если есть ошибки - исправьте перед продолжением!

Шаг 2: Загрузка образов

# Загружаем все образы
docker compose pull

# Процесс может занять 5-15 минут
# Размер образов: ~2-3 GB

Шаг 3: Запуск

# Запускаем в фоновом режиме
docker compose up -d

# Смотрим логи
docker compose logs -f

Шаг 4: Проверка статуса

# Проверяем статус контейнеров
docker compose ps

Ожидаемый вывод:

NAME                STATUS              PORTS
safeline-pg         Up (healthy)        
safeline-mgt        Up (healthy)        
safeline-detector   Up                  
safeline-tengine    Up                  0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
safeline-luigi      Up                  
safeline-fvm        Up                  
safeline-chaos      Up                  

✅ Все контейнеры должны быть Up (или Up (healthy))


5.5 Troubleshooting начального запуска

Проблема: "network kodalinet not found"

Причина: Сеть не создана

Решение:

docker network create \
  --driver bridge \
  --subnet 172.20.0.0/24 \
  --gateway 172.20.0.1 \
  --opt com.docker.network.bridge.name=kodalinet \
  kodalinet

Проблема: "postgres unhealthy"

Причина: PostgreSQL не запустился

Решение:

# Смотрим логи
docker logs safeline-pg

# Часто помогает перезапуск
docker restart safeline-pg

# Проверяем healthcheck
docker inspect safeline-pg | grep -A10 Health

Проблема: "tengine exited with code 1"

Причина: Порты 80/443 заняты

Решение:

# Проверяем порты
sudo netstat -tlnp | grep -E ':80|:443'

# Останавливаем конфликтующий сервис
sudo systemctl stop nginx
sudo systemctl stop apache2

Проблема: "permission denied"

Причина: Неправильные права на директории

Решение:

# Проверяем владельца
ls -la /app/safeline/

# Исправляем
sudo chown -R $USER:$USER /app/safeline
chmod -R 755 /app/safeline
chmod 700 /app/safeline/resources/postgres/data

5.6 Первый вход в Management UI

Вариант A: SSH Tunnel (РЕКОМЕНДУЕТСЯ!)

На своём компьютере:

ssh -L 9443:172.20.0.4:1443 user@your-server

Затем открыть в браузере:

https://localhost:9443

Вариант B: Временно открыть порт

В docker-compose.yml раскомментировать:

mgt:
  ports:
    - "127.0.0.1:9443:1443"  # Только localhost

Перезапустить:

docker compose up -d --force-recreate mgt

Защитить firewall:

sudo ufw allow from YOUR_IP to any port 9443

Первоначальная настройка:

  1. Браузер покажет предупреждение о сертификате (нормально, self-signed)

    • Chrome: "Advanced" → "Proceed to localhost"
    • Firefox: "Advanced" → "Accept Risk"
  2. Создание аккаунта:

    Email: your-email@company.com  ← ВАЛИДНЫЙ! Для Let's Encrypt
    Password: [сложный пароль]
    
  3. Dashboard откроется:

    • Overview: статистика (пока пусто)
    • Web Services: сайты (пока пусто)
    • Protection: правила WAF
    • Settings: настройки

6. SSL сертификаты (Let's Encrypt)

🔐 Цель: Настроить автоматическое обновление SSL сертификатов

6.1 Механизм SafeLine SSL

SafeLine v7.2.0+ поддерживает автоматическое обновление SSL:

┌──────────────────────────────────────────────┐
│   SafeLine SSL Auto-Reload Mechanism        │
├──────────────────────────────────────────────┤
│                                              │
│  1. Certbot обновляет сертификаты:          │
│     /etc/letsencrypt/live/domain/            │
│                                              │
│  2. Скрипт копирует в SafeLine:              │
│     /app/safeline/resources/nginx/certs/     │
│                                              │
│  3. SafeLine проверяет файлы каждый час      │
│     (автоматически, встроенный механизм)     │
│                                              │
│  4. При изменении - применяет сертификат     │
│     БЕЗ рестарта контейнера!                 │
│                                              │
└──────────────────────────────────────────────┘

Источник: SafeLine SSL Auto-update Guide


6.2 Настройка DNS

КРИТИЧНО: Настройте DNS ПЕРЕД получением SSL!

В вашем DNS провайдере:

Type: A
Name: @                     (для example.com)
      ИЛИ
Name: *                     (для *.example.com - wildcard)
Value: <IP вашего сервера>
TTL: 300 (5 минут)

Проверка DNS:

# Способ 1: dig
dig quick-fly-space.shop +short
# Должен вернуть ваш IP

# Способ 2: nslookup
nslookup quick-fly-space.shop
# Server:		8.8.8.8
# Address:	8.8.8.8#53
#
# Non-authoritative answer:
# Name:	quick-fly-space.shop
# Address: <YOUR_SERVER_IP>  ← Это должен быть ваш IP!

# Способ 3: host
host quick-fly-space.shop

⏱️ Подождите 5-10 минут после изменения DNS!


6.3 Вариант A: SSL через SafeLine UI (Простой)

Шаг 1: Разрешить ACME запросы

⚠️ ВАЖНО: Без этого Let's Encrypt не сможет верифицировать домен!

В SafeLine UI:

  1. Protection → Allow & Deny
  2. Add Rule
  3. Настройте:
    Rule Name: ACME Challenge
    Rule Type: Allow
    Path Prefix: /.well-known/
    Priority: High
    Description: Let's Encrypt verification
    
  4. Save

Шаг 2: Создать сайт (временно HTTP)

  1. Web Services → Add Site
  2. Заполните:
    Site Name: quick-fly-space.shop
    Domain: quick-fly-space.shop
    Port: 80
    SSL: Disabled (пока!)
    
    Upstream Servers:
      - Protocol: HTTP
      - Address: 127.0.0.1  (или ваш backend)
      - Port: 8080
    
  3. Save

Шаг 3: Проверить HTTP доступность

curl -v http://quick-fly-space.shop/.well-known/acme-challenge/test
# Должен вернуть 404 (это нормально, файла нет)
# НО соединение должно быть успешным!

Шаг 4: Получить SSL сертификат

  1. Settings → SSL Certificates
  2. Add Certificate
  3. Выберите: Let's Encrypt
  4. Заполните:
    Domain: quick-fly-space.shop
    Email: your-email@company.com
    
    ☑ I agree to the Let's Encrypt Terms of Service
    
  5. Request Certificate

⏱️ Подождите 30-60 секунд...

SUCCESS: Сертификат появится в списке!

Шаг 5: Применить SSL к сайту

  1. Web Services → Sites → quick-fly-space.shop → Edit
  2. Измените:
    Port: 443
    ☑ Enable SSL
    SSL Certificate: [Выберите Let's Encrypt сертификат]
    
  3. Save

Шаг 6: Добавить HTTP → HTTPS редирект

  1. Web Services → Add Site
  2. Создайте второй site:
    Domain: quick-fly-space.shop
    Port: 80
    
    В разделе Advanced → Custom Configuration:
    return 301 https://$host$request_uri;
    
  3. Save

Проверка:

# Должен редиректить на HTTPS
curl -I http://quick-fly-space.shop
# HTTP/1.1 301 Moved Permanently
# Location: https://quick-fly-space.shop/

# HTTPS должен работать
curl -I https://quick-fly-space.shop
# HTTP/2 200 

🎉 Готово! SSL работает!


6.4 Вариант B: SSL через Certbot (Advanced)

Для продвинутых пользователей или если UI не работает

Установка Certbot:

sudo apt update
sudo apt install certbot -y

Получение сертификата:

# ВАЖНО: Остановите tengine на время получения!
docker compose stop safeline-tengine

# Запросите сертификат
sudo certbot certonly \
  --standalone \
  -d quick-fly-space.shop \
  --email your@email.com \
  --agree-tos \
  --non-interactive

# Сертификат сохранён в:
# /etc/letsencrypt/live/quick-fly-space.shop/

# Запустите tengine обратно
docker compose start safeline-tengine

Копирование в SafeLine:

# Копируем сертификат
sudo cp /etc/letsencrypt/live/quick-fly-space.shop/fullchain.pem \
  /app/safeline/resources/nginx/certs/cert_1.crt

# Копируем приватный ключ
sudo cp /etc/letsencrypt/live/quick-fly-space.shop/privkey.pem \
  /app/safeline/resources/nginx/certs/cert_1.key

# Права доступа
sudo chown 1000:1000 /app/safeline/resources/nginx/certs/cert_1.*
sudo chmod 644 /app/safeline/resources/nginx/certs/cert_1.crt
sudo chmod 600 /app/safeline/resources/nginx/certs/cert_1.key

# SafeLine подхватит изменения в течение часа
# Или рестартните tengine:
docker restart safeline-tengine

6.5 Автоматическое обновление SSL

📄 Используйте готовый скрипт из /safeline-production-v2/scripts/ssl-renew.sh

Установка скрипта:

# Копируем скрипт
sudo cp scripts/ssl-renew.sh /usr/local/bin/safeline-ssl-renew

# Делаем исполняемым
sudo chmod +x /usr/local/bin/safeline-ssl-renew

# Редактируем переменные
sudo nano /usr/local/bin/safeline-ssl-renew

Важные переменные:

DOMAIN="quick-fly-space.shop"  # Ваш домен
SAFELINE_DIR="/app/safeline"   # Путь к SafeLine
NOTIFY_EMAIL=""                # Email для уведомлений (опционально)

Тестирование скрипта:

# Запускаем вручную
sudo /usr/local/bin/safeline-ssl-renew

# Проверяем логи
tail -f /var/log/safeline-ssl-renew.log

Настройка Cron:

# Открываем crontab
sudo crontab -e

# Добавляем строку (каждый день в 3:00):
0 3 * * * /usr/local/bin/safeline-ssl-renew >> /var/log/safeline-ssl-renew.log 2>&1

Проверка Cron:

# Просмотр установленных задач
sudo crontab -l

# Проверка что cron работает
sudo systemctl status cron

6.6 Мониторинг срока действия SSL

Проверка вручную:

# Проверка срока действия
echo | openssl s_client -connect quick-fly-space.shop:443 -servername quick-fly-space.shop 2>/dev/null | \
  openssl x509 -noout -dates

# Вывод:
# notBefore=Jan 27 00:00:00 2026 GMT
# notAfter=Apr 27 23:59:59 2026 GMT

Скрипт проверки:

#!/bin/bash
# check-ssl-expiry.sh

DOMAIN="quick-fly-space.shop"

# Получаем дату истечения
EXPIRY=$(echo | openssl s_client -connect $DOMAIN:443 -servername $DOMAIN 2>/dev/null | \
  openssl x509 -noout -enddate | cut -d= -f2)

# Конвертируем в секунды
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)

# Считаем дни
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))

echo "Сертификат истекает: $EXPIRY"
echo "Осталось дней: $DAYS_LEFT"

if [ $DAYS_LEFT -lt 30 ]; then
    echo "⚠️  WARNING: Менее 30 дней до истечения!"
elif [ $DAYS_LEFT -lt 7 ]; then
    echo "🚨 CRITICAL: Менее 7 дней до истечения!"
else
    echo "✅ OK: Сертификат валидный"
fi

[Часть 2 продолжается в следующем файле...]

Навигация

📖 Часть 1: Введение, ошибки, требования
📖 Часть 2: Установка и конфигурация ← ВЫ ЗДЕСЬ
📖 Часть 3: Backend, мониторинг, troubleshooting


Следующий файл: BOOKSTACK_ARTICLE_PART3.md