Перейти к содержимому

Руководство по развертыванию защищенного статического веб-сервера на Debian 13

Введение

Данный документ описывает полный процесс настройки production-ready веб-сервера на базе Debian 13 (Trixie) с ядром 6.18.15. Целевая конфигурация предназначена для VPS с ограниченными ресурсами (1 vCPU, 2 GB RAM) и включает:

  • Оптимизацию ядра Linux для сетевых нагрузок
  • Веб-сервер Caddy с автоматическим HTTPS через Let’s Encrypt
  • Встроенный rate limiter для защиты от DDoS-атак
  • Reverse proxy для WebSocket
  • Публикацию статического контента с правильной маршрутизацией

Документ отражает все технические нюансы, выявленные в ходе настройки, включая актуальные имена модулей ядра для kernel 6.x, корректный синтаксис внешних модулей Caddy и современные подходы к управлению capabilities через systemd.


1. Подготовка системы

1.1. Обновление базовой системы

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget gnupg2 ca-certificates lsb-release \
    debian-keyring debian-archive-keyring apt-transport-https \
    software-properties-common build-essential git

1.2. Создание swap-файла

Для VPS с 2 GB RAM наличие swap-файла является обязательным для предотвращения OOM Killer при пиковых нагрузках.

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# Проверка
free -h | grep Swap

1.3. Проверка версии ядра

uname -r

Требуется ядро версии 6.18.x или новее.


2. Конфигурирование модулей ядра

В ядре 6.x произошло массовое переименование crypto-модулей с подчеркиваний на дефисы. Ниже приведен список, верифицированный для Debian 13 с kernel 6.18.15 на AMD CPU.

2.1. Создание конфигурации автозагрузки

sudo tee /etc/modules-load.d/vps.conf <<'EOF'
# =============================================================================
# Kernel modules for VPS: Caddy
# Debian 13, kernel 6.18.15, AMD CPU
# Имена модулей актуализированы для kernel 6.x (дефисы вместо подчеркиваний)
# Верифицировано через: find /lib/modules/$(uname -r) -name "*.ko*"
# =============================================================================

# === Туннелирование (для TUN inbound) ===
tun

# === Сетевые алгоритмы (критично для производительности TCP) ===
# BBR congestion control - максимизирует throughput через интернет с потерями
tcp_bbr
# Fair Queue - оптимальный qdisc для BBR, управляет очередями пакетов
sch_fq

# === Криптография с аппаратным ускорением ===
# AES-NI (флаг CPU: aes) - HW-ускорение TLS AES-GCM
aesni-intel
# GHASH через PCLMULQDQ (флаг CPU: pclmulqdq) - критично для AES-GCM
ghash-clmulni-intel
# ChaCha20-Poly1305 AEAD (флаги CPU: avx2, ssse3) - TLS fallback cipher
chacha20poly1305
# NHPoly1305 для Adiantum/fscrypt (флаги CPU: avx2, sse2)
nhpoly1305
nhpoly1305-avx2
nhpoly1305-sse2

# === Netfilter (firewall + conntrack + NAT) ===
nf_tables
nft_chain_nat
nf_conntrack
nf_nat
nf_log_syslog

# === НЕ загружать явно (встроено в ядро) ===
# SHA-1/SHA-256/SHA-512: CONFIG_CRYPTO_SHA256=y
# Curve25519: подтягивается автоматически через libcurve25519
# Poly1305: подтягивается автоматически через libpoly1305
# Базовый AES: CONFIG_CRYPTO_AES=y (но aesni-intel даёт HW-ускорение)
EOF

2.2. Применение и проверка

sudo systemctl restart systemd-modules-load.service
sudo systemctl status systemd-modules-load.service --no-pager
journalctl -u systemd-modules-load.service -n 20 --no-pager | grep -E "Inserted|Failed"

Ожидаемый результат - все модули загружены без ошибок “Failed to find module”.

lsmod | grep -E "tcp_bbr|sch_fq|aesni|ghash|chacha|nf_conntrack"

2.3. Верификация доступности crypto-алгоритмов

cat /proc/crypto | grep -B1 "gcm(aes)" | head -4
# Ожидаемо: name: gcm(aes) / module: aesni_intel

cat /proc/crypto | grep -B1 "sha256" | head -4
# Ожидаемо: name: sha256 / module: kernel (встроено)

3. Конфигурирование параметров ядра (sysctl)

Параметры оптимизированы для VPS с 2 GB RAM и сценария reverse proxy.

3.1. Создание конфигурации sysctl

sudo tee /etc/sysctl.d/99-vps.conf <<'EOF'
# =============================================================================
# VPS Server Tuning - Debian 13 / Kernel 6.18.15 / 2GB RAM
# =============================================================================

# === IP Forwarding (для маршрутизации трафика) ===
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

# === Reverse Path Filtering (защита от IP spoofing) ===
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# === Отключение source routing и redirects (безопасность) ===
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

# === SYN Cookies (защита от SYN flood) ===
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2

# === TCP Congestion Control ===
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

# === TCP Buffer Sizes (оптимизировано для 2GB RAM) ===
net.ipv4.tcp_rmem = 4096 262144 16777216
net.ipv4.tcp_wmem = 4096 262144 16777216

# === TCP Performance Optimizations ===
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600

# === Conntrack (таблица соединений для firewall + NAT) ===
# 262144 записей x ~300 bytes = ~75MB RAM (приемлемо для 2GB)
net.netfilter.nf_conntrack_max = 262144
net.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_tcp_timeout_established = 43200
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 120

# === Network Queues (оптимизировано для 1 vCPU) ===
net.core.netdev_max_backlog = 16384
net.core.somaxconn = 8192

# === Kernel Security Hardening ===
kernel.sysrq = 0
kernel.yama.ptrace_scope = 1
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.unprivileged_bpf_disabled = 1

# === File Descriptors ===
fs.file-max = 2097152
fs.nr_open = 2097152

# === Virtual Memory (критично для 2GB RAM) ===
vm.swappiness = 30
vm.vfs_cache_pressure = 75
vm.dirty_background_ratio = 3
vm.dirty_ratio = 10
vm.min_free_kbytes = 45056
EOF

3.2. Применение параметров

sudo sysctl --system

# Проверка критичных параметров
sysctl net.ipv4.tcp_congestion_control net.core.default_qdisc
sysctl net.netfilter.nf_conntrack_max
sysctl kernel.sysrq kernel.yama.ptrace_scope kernel.kptr_restrict

3.3. Верификация BBR в действии

sysctl net.ipv4.tcp_available_congestion_control
# Ожидаемый вывод: reno bic cubic bbr

ss -ti | grep -i bbr | head -3

4. Установка базового Caddy

Caddy выбран как оптимальный веб-сервер благодаря автоматическому HTTPS, встроенной поддержке HTTP/3 (QUIC) и минимальной конфигурации.

4.1. Добавление официального репозитория

curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
    sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
    sudo tee /etc/apt/sources.list.d/caddy-stable.list

sudo apt update

4.2. Установка стандартного пакета

sudo apt install -y caddy
caddy version
sudo systemctl status caddy --no-pager

Стандартный пакет Caddy из репозитория Debian не содержит модуль rate limiting. Для production-конфигурации с защитой от DDoS требуется сборка через xcaddy (раздел 5).


5. Сборка Caddy с модулем rate limiter

Модуль github.com/mholt/caddy-ratelimit является внешним модулем и не входит в стандартную поставку Caddy. Требуется самостоятельная компиляция.

5.1. Установка Go

cd /tmp
wget https://go.dev/dl/go1.24.4.linux-amd64.tar.gz

sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.24.4.linux-amd64.tar.gz

echo 'export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin' >> ~/.bashrc
source ~/.bashrc

go version

5.2. Установка xcaddy

go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
xcaddy version

5.3. Компиляция Caddy с модулями

mkdir -p ~/caddy-build && cd ~/caddy-build

xcaddy build \
    --with github.com/mholt/caddy-ratelimit \
    --with github.com/caddyserver/transform-encoder \
    --output /tmp/caddy

/tmp/caddy version
/tmp/caddy list-modules | grep -iE "rate|transform"

Ожидаемый вывод должен содержать http.handlers.rate_limit.

5.4. Замена системного Caddy

sudo systemctl stop caddy
sudo cp /usr/bin/caddy /usr/bin/caddy.orig
sudo cp /tmp/caddy /usr/bin/caddy
sudo chown root:root /usr/bin/caddy
sudo chmod +x /usr/bin/caddy
sudo systemctl start caddy

5.5. Настройка capabilities через systemd (современный подход)

Использование setcap на бинарнике требует capability CAP_SETFCAP, которую systemd не предоставляет сервисам по соображениям безопасности. Правильный подход - использование AmbientCapabilities в unit-файле.

sudo mkdir -p /etc/systemd/system/caddy.service.d
sudo tee /etc/systemd/system/caddy.service.d/capabilities.conf <<'EOF'
[Service]
# Дать возможность привязываться к портам 80/443 без setcap
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE

# Очистить проблемный setcap из оригинального unit
ExecStartPre=
# Вернуть валидацию конфига
ExecStartPre=/usr/bin/caddy validate --config /etc/caddy/Caddyfile
EOF

sudo systemctl daemon-reload

Удаление capabilities с бинарника (если были установлены ранее):

sudo setcap -r /usr/bin/caddy
getcap /usr/bin/caddy
# Вывод должен быть пустым

6. Настройка HTTPS с Let’s Encrypt

Caddy автоматически получает и обновляет TLS-сертификаты Let’s Encrypt. Ниже приведены best practices для безопасной конфигурации.

6.1. Best practices для TLS-ключей

ПараметрРекомендацияОбоснование
Тип ключаECDSA P-256 (по умолчанию)Быстрее RSA, меньше размер сертификата, сопоставимая безопасность
АльтернативаECDSA P-384Выше уровень безопасности, чуть медленнее
ПротоколTLS 1.3 + TLS 1.2TLS 1.3 обязателен, TLS 1.2 для совместимости
Cipher suitesModern (AES-GCM, ChaCha20)Отключение устаревших алгоритмов
HSTSВключен с preloadЗащита от downgrade-атак
OCSP StaplingАвтоматически (Caddy)Ускорение TLS handshake

6.2. Создание директорий

sudo mkdir -p /var/www/html
sudo chown -R caddy:caddy /var/www/html
sudo chmod 755 /var/www/html

sudo mkdir -p /var/log/caddy
sudo chown caddy:caddy /var/log/caddy

6.3. Подготовка DNS

Перед настройкой HTTPS необходимо:

  1. Зарегистрировать домен (например, example.com)
  2. Создать A-запись, указывающую на IP VPS
  3. Создать AAAA-запись (опционально, для IPv6)
  4. Дождаться распространения DNS (dig example.com +short)

7. Публикация статического контента

7.1. Создание главной страницы

sudo tee /var/www/html/index.html <<'EOF'
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Главная - Защищенный сайт</title>
</head>
<body>
    <h1>Главная страница</h1>
    <nav>
        <ul>
            <li><a href="/about">О нас</a></li>
            <li><a href="/products">Продукты</a></li>
            <li><a href="/contact">Контакты</a></li>
        </ul>
    </nav>
</body>
</html>
EOF

sudo chown caddy:caddy /var/www/html/index.html

7.2. Создание вложенных страниц

sudo tee /var/www/html/about.html <<'EOF'
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>О нас</title>
</head>
<body>
    <h1>О нас</h1>
    <p>Информация о компании.</p>
    <a href="/">На главную</a>
</body>
</html>
EOF

sudo chown caddy:caddy /var/www/html/about.html

7.3. Создание страницы ошибки 404

sudo tee /var/www/html/404.html <<'EOF'
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>404 - Страница не найдена</title>
    <style>
        body { font-family: system-ui, sans-serif; max-width: 600px; margin: 10vh auto; text-align: center; }
        .error-code { font-size: 6rem; color: #dc3545; margin: 0; }
    </style>
</head>
<body>
    <p class="error-code">404</p>
    <h1>Страница не найдена</h1>
    <p>Запрошенный ресурс не существует.</p>
    <a href="/">Вернуться на главную</a>
</body>
</html>
EOF

sudo chown caddy:caddy /var/www/html/404.html

8. Полная конфигурация Caddyfile

Ниже приведена финальная конфигурация, учитывающая все технические нюансы модуля caddy-ratelimit.

Ключевые особенности синтаксиса:

  1. rate_limit - внешний модуль, требует xcaddy (см. раздел 5)
  2. Зоны определяются в месте применения - каждый handle содержит полное определение своей зоны с параметрами events и window (не rate и burst)
  3. try_files - отдельная директива верхнего уровня, размещается перед file_server, а не внутри него
  4. handle_errors - должен быть размещен после всех handle для корректной обработки ошибок с сохранением HTTP-статуса

Создание Caddyfile

sudo tee /etc/caddy/Caddyfile <<'EOF'
# =============================================================================
# Caddyfile for VPS: Static site + reverse proxy
# Debian 13, kernel 6.18.15, 2GB RAM
# Требует сборки через xcaddy с модулем github.com/mholt/caddy-ratelimit
# =============================================================================

# =============================================================================
# ГЛОБАЛЬНЫЕ ОПЦИИ
# =============================================================================
{
    email admin@example.com
    admin off
    http_port 80
    https_port 443
    
    log {
        output file /var/log/caddy/global.log {
            roll_size 50mb
            roll_keep 5
            roll_keep_for 720h
        }
        level INFO
    }
}

# =============================================================================
# Основной сайт с HTTPS и rate limiting
# =============================================================================
example.com {
    root * /var/www/html
    
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 10
            roll_keep_for 720h
        }
        format json
    }
    
    # === TLS конфигурация (Let's Encrypt) ===
    tls {
        key_type p256
        protocols tls1.2 tls1.3
    }
    
    # === Security headers ===
    header {
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        X-XSS-Protection "1; mode=block"
        Referrer-Policy "strict-origin-when-cross-origin"
        Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
        Permissions-Policy "geolocation=(), microphone=(), camera=()"
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        -Server
        -X-Powered-By
    }
    
    # === Matchers ===
    @websocket {
        path /ws
        header Connection *Upgrade*
        header Upgrade websocket
    }
    
    @api {
        path /api/*
    }
    
    @static_files {
        path *.css *.js *.jpg *.jpeg *.png *.gif *.ico *.woff *.woff2 *.ttf *.svg
    }
    
    @html_files {
        path *.html
    }
    
    @blocked {
        path /.git/* /.env /*.php /*.asp /*.jsp /wp-* /admin*
    }
    
    # === Блокировка нежелательных путей ===
    respond @blocked "Access denied" 403
    
    # === WebSocket reverse proxy ===
    # Зона определяется непосредственно в месте применения
    handle @websocket {
        rate_limit {
            zone websocket_zone {
                key {remote_host}
                events 30
                window 1s
            }
        }
        
        reverse_proxy localhost:10000 {
            header_up Host {host}
            header_up X-Real-IP {remote_host}
            header_up X-Forwarded-For {remote_host}
            header_up X-Forwarded-Proto {scheme}
        }
    }
    
    # === API endpoints ===
    handle @api {
        rate_limit {
            zone api_zone {
                key {remote_host}
                events 10
                window 1s
            }
        }
        
        respond "API endpoint protected" 200
    }
    
    # === Статический контент ===
    handle {
        rate_limit {
            zone static_zone {
                key {remote_host}
                events 100
                window 1s
            }
        }
        
        encode gzip zstd
        
        header @static_files Cache-Control "public, max-age=31536000, immutable"
        header @html_files Cache-Control "public, max-age=3600"
        
        # try_files - ОТДЕЛЬНАЯ директива перед file_server
        # Ищет: точный путь -> директорию -> .html файл -> 404
        try_files {path} {path}/ {path}.html =404
        
        file_server {
            index index.html
            hide .git .env
        }
    }
    
    # === Обработка ошибок (ВСЕГДА В КОНЦЕ site block) ===
    # Сохраняет оригинальный HTTP-статус (404, 500 и т.д.)
    handle_errors {
        @404 expression {err.status_code} == 404
        rewrite @404 /404.html
        file_server
    }
}

# =============================================================================
# Redirects
# =============================================================================
www.example.com {
    redir https://example.com{uri} permanent
}

http://example.com, http://www.example.com {
    redir https://example.com{uri} permanent
}
EOF

Применение и валидация

sudo caddy validate --config /etc/caddy/Caddyfile
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo systemctl status caddy --no-pager

Замена домена

sudo sed -i 's/example.com/your-domain.com/g' /etc/caddy/Caddyfile
sudo systemctl reload caddy

9. Firewall (nftables)

9.1. Создание конфигурации

sudo tee /etc/nftables.conf <<'EOF'
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;

        iifname "lo" accept
        ct state established,related accept
        ct state invalid drop

        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        tcp dport 22 accept
        tcp dport 80 accept
        tcp dport 443 accept
        udp dport 443 accept
        udp dport 8443 accept

        ct state new limit rate over 100/second drop
    }

    chain forward {
        type filter hook forward priority filter; policy drop;
        ct state established,related accept
    }

    chain output {
        type filter hook output priority filter; policy accept;
    }
}

table ip nat {
    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        oifname "eth0" masquerade
    }
}
EOF

9.2. Применение

sudo nft -c -f /etc/nftables.conf
sudo systemctl enable --now nftables
sudo systemctl restart nftables
sudo nft list ruleset

10. Верификация и мониторинг

10.1. Скрипт комплексной проверки

sudo tee /usr/local/bin/verify-vps-setup.sh <<'EOF'
#!/bin/bash
echo "=== 1. Kernel Modules ==="
lsmod | grep -E "tcp_bbr|sch_fq|aesni|ghash|nf_conntrack" | awk '{print "[OK] " $1}'

echo ""
echo "=== 2. BBR + FQ ==="
echo "TCP CC: $(sysctl -n net.ipv4.tcp_congestion_control)"
echo "Qdisc: $(sysctl -n net.core.default_qdisc)"

echo ""
echo "=== 3. Conntrack ==="
echo "Used: $(cat /proc/sys/net/netfilter/nf_conntrack_count)"
echo "Max: $(cat /proc/sys/net/netfilter/nf_conntrack_max)"

echo ""
echo "=== 4. Caddy Status ==="
systemctl is-active caddy
caddy version | head -1

echo ""
echo "=== 5. Caddy Modules ==="
caddy list-modules 2>/dev/null | grep -iE "rate|transform" | awk '{print "[OK] " $1}'

echo ""
echo "=== 6. Listening Ports ==="
ss -tlnp | grep caddy

echo ""
echo "=== 7. Memory Usage ==="
free -h | grep -E "Mem|Swap"

echo ""
echo "=== 8. Security Hardening ==="
echo "SysRq: $(sysctl -n kernel.sysrq) (expect 0)"
echo "Ptrace: $(sysctl -n kernel.yama.ptrace_scope) (expect 1)"
echo "Kptr: $(sysctl -n kernel.kptr_restrict) (expect 2)"
EOF

sudo chmod +x /usr/local/bin/verify-vps-setup.sh
/usr/local/bin/verify-vps-setup.sh

10.2. Тестирование rate limiter

for i in {1..15}; do
    curl -s -o /dev/null -w "Request $i: %{http_code}\n" \
        https://example.com/api/test
done
# Ожидаемый результат: первые 10 - 200 OK, далее - 429 Too Many Requests

10.3. Тестирование навигации

curl -I https://example.com/
curl -I https://example.com/about
curl -I https://example.com/about.html
curl -I https://example.com/nonexistent

Ожидаемые статусы: 200, 200, 200, 404 соответственно.

10.4. Проверка TLS

Используйте онлайн-сервисы:

10.5. Мониторинг в реальном времени

sudo journalctl -u caddy -f
sudo tail -f /var/log/caddy/access.log
watch -n 2 'cat /proc/sys/net/netfilter/nf_conntrack_count; echo "/"; cat /proc/sys/net/netfilter/nf_conntrack_max'
watch -n 5 'free -h'

11. Обслуживание

11.1. Автоматическое обновление сертификатов

Caddy автоматически продлевает сертификаты Let’s Encrypt за 30 дней до истечения. Дополнительных cron-задач не требуется. Проверить статус:

sudo ls -la /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/

11.2. Ротация логов

Caddy имеет встроенную ротацию логов через параметры roll_size и roll_keep. Для дополнительной страховки:

sudo tee /etc/logrotate.d/caddy-custom <<'EOF'
/var/log/caddy/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 caddy caddy
}
EOF

11.3. Бэкап конфигурации

sudo tar -czf ~/vps-config-backup-$(date +%Y%m%d).tar.gz \
    /etc/caddy/ \
    /etc/modules-load.d/vps.conf \
    /etc/sysctl.d/99-vps.conf \
    /etc/nftables.conf \
    /etc/systemd/system/caddy.service.d/ \
    /var/www/html/

11.4. Обновление Caddy

При использовании custom build через xcaddy обновления через apt не применяются. Для обновления:

cd ~/caddy-build
git pull  # если используется git-репозиторий xcaddy
xcaddy build \
    --with github.com/mholt/caddy-ratelimit \
    --with github.com/caddyserver/transform-encoder \
    --output /tmp/caddy

sudo systemctl stop caddy
sudo cp /tmp/caddy /usr/bin/caddy
sudo systemctl start caddy
caddy version

12. Чек-лист развёртывания

Фаза 1: Подготовка системы

  • Система обновлена (apt upgrade)
  • Swap-файл 2GB создан и активирован
  • Ядро 6.18.x или новее (uname -r)
  • Базовые утилиты установлены (curl, git, build-essential)

Фаза 2: Модули ядра

  • /etc/modules-load.d/vps.conf создан с именами через дефисы
  • Модули tcp_bbr, sch_fq, aesni-intel, ghash-clmulni-intel загружены
  • nf_conntrack и nf_tables активны
  • systemd-modules-load.service без ошибок

Фаза 3: Параметры ядра

  • /etc/sysctl.d/99-vps.conf создан
  • sysctl --system применён без ошибок
  • BBR активен (sysctl net.ipv4.tcp_congestion_control = bbr)
  • FQ активен (sysctl net.core.default_qdisc = fq)
  • Conntrack max = 262144
  • Security hardening применен (sysrq=0, ptrace=1, kptr=2)

Фаза 4: Caddy

  • Официальный репозиторий Caddy добавлен
  • Go установлен
  • xcaddy установлен
  • Caddy собран с модулями caddy-ratelimit и transform-encoder
  • Systemd override с AmbientCapabilities=CAP_NET_BIND_SERVICE создан
  • Capabilities с бинарника удалены (setcap -r)
  • Сервис запущен и слушает порты 80/443

Фаза 5: Конфигурация

  • DNS A-запись указывает на IP VPS
  • /etc/caddy/Caddyfile создан с реальной доменной зоной
  • Зоны rate_limit определены с параметрами events и window
  • try_files размещен как отдельная директива перед file_server
  • handle_errors размещен в конце site block
  • Конфигурация провалидирована (caddy validate)
  • Статический контент размещен в /var/www/html/
  • Страницы index.html, about.html, 404.html созданы
  • Права на директории установлены (caddy:caddy)

Фаза 6: Firewall и сервисы

  • /etc/nftables.conf создан
  • Порты 22, 80, 443 (TCP), 443 (UDP/QUIC) открыты
  • nftables активен и автозапускается

Фаза 7: Верификация

  • https://your-domain.com открывается с валидным сертификатом
  • SSL Labs показывает оценку A или A+
  • Mozilla Observatory показывает A
  • Rate limiter блокирует избыточные запросы (HTTP 429)
  • Security headers присутствуют в ответах
  • HSTS активен
  • Навигация между страницами работает (/about отдаёт about.html)
  • Несуществующие страницы возвращают HTTP 404 с кастомной страницей
  • WebSocket endpoint проксируется корректно
  • Логи пишутся в /var/log/caddy/

Фаза 8: Мониторинг

  • Скрипт verify-vps-setup.sh создан и работает
  • Настроена дополнительная ротация логов через logrotate
  • Swap используется умеренно (vm.swappiness = 30)
  • Использование RAM в пределах 1.5-1.8 GB