Перейти к содержанию

Ввод сервера VPN в эксплуатацию

Обзор

Полный цикл: закупка → настройка Ansible → создание inbounds → регистрация в MySQL → активация.

Детальный чеклист: docs/guides/SERVER-SETUP-CHECKLIST.md (monorepo shivavpn). Автоматизация: scripts/commission_servers.py.

1. Добавить сервер в Ansible инвентарь

Файл: ansible/shiva-ansible/inventory/servers.ini → группа [vpn]

vpn-provider-country-01  ansible_host=<IP>  ansible_port=22  ansible_user=root  external_ip_list=[<IP>]

2. Настройка сервера через Ansible

cd ansible/shiva-ansible
ansible-playbook -i inventory/servers.ini playbooks/vpn.yml \
  --limit vpn-provider-country-01 --vault-password-file .vault_pass

Что выполняется:

  1. SSH hardening — ключи, StrictModes, MaxStartups 200:30:300, MaxSessions 50
  2. UFW — порты 22, 80, 443, 1443, xray_port (33081), 9100
  3. sysctl — BBR, TCP буферы, somaxconn 65535, file-max 1M
  4. nginx — reverse proxy для 3x-ui, dhparam, certbot
  5. node_exporter — мониторинг с basic auth
  6. 3x-ui — Docker контейнер с xray

После запуска зайти в 3x-ui: https://<IP>:1443 — сменить пароль admin.

3. Создание inbounds

Автоматический (рекомендуется)

python3 scripts/commission_servers.py --server <IP>

Создаёт 2 inbound: - Inbound 1: порт 2087, XHTTP+Reality (google.com SNI) - Inbound 2: порт 853, TCP+Reality (google.com SNI)

Ручной

Через 3x-ui панель (https://<IP>:1443). Параметры inbounds — см. docs/guides/SERVER-SETUP-CHECKLIST.md.

Обязательно:

  • Reality security (не TLS, не none)
  • sockopt: tcpKeepAliveIdle=300, tcpKeepAliveInterval=15, tcpMptcp=true, tcpCongestion=bbr, tcpMaxSeg=1440
  • Правильные SNI destinations

4. Добавить сервер в MySQL

INSERT INTO server (id, name, ip, latitude, longitude, city_id, is_active, is_recommended, is_allocated)
VALUES (UUID_TO_BIN(UUID()), 'AT #8', '<SERVER_IP>',
        48.2082, 16.3738,
        (SELECT id FROM city WHERE name_key = 'vienna'),
        FALSE, TRUE, TRUE);

is_active=FALSE

Сервер создаётся неактивным. Активация — после проверки inbounds и мониторинга.

Проверить города: SELECT country.code, country.name_key, city.name_key FROM country JOIN city ON country.id = city.country_id;

5. Добавить в мониторинг

Файл: ansible/shiva-ansible/roles/grafana/files/prometheus.yml

Добавить target <IP>:9100 в соответствующий job.

cd ansible/shiva-ansible
ansible-playbook -i inventory/servers.ini playbooks/grafana.yml

Пароли

Пароль node_exporter basic auth хранится в ansible-vault (vault_node_exporter_basic_auth_password). Prometheus config ссылается на vault-переменные.

6. Активация

UPDATE server SET is_active = 1 WHERE ip = '<SERVER_IP>';

Каскад (5-70 мин): Backend event → NodeSyncWorker → VCS mysql_sync → full_sync → fill_configs.

Детали: docs/guides/SERVER-CONFIG-FLOW.md.

7. Проверка

# Health check
python3 scripts/check_vpn_server.py <IP>

# Проверить что конфиги доставлены
./scripts/ssh-internal.sh 10.99.87.249 "docker exec vpn-config-service python -c \"from app.db import get_db; ...\""

Docker log rotation

Обязательно на каждом VPN сервере (без этого логи растут до 10-20 ГБ):

// /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "3"
  }
}

SSH-ключ vpn-config-service

На каждом VPN-сервере обязательно два ключа в /root/.ssh/authorized_keys:

  1. id_ed25519_shivavpn — ручной доступ + Ansible
  2. id_ed25519_vpn_config_service — для VPN Config Service worker (fill_configs, full_sync)
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGtLzyxtfv8RJaU/PsW9YPPAmMT1jYh2/iM89d0uogux vpn-config-service@shivavpn

Без второго ключа worker получит "Permission denied" при попытке добавить клиентов или прочитать SQLite.

Inbounds: требования

Inbound ID Порт Протокол Назначение
1 2087 VLESS XHTTP Основной (XHTTP + Reality)
2 853 VLESS TCP Fallback + WL proxy target
3 10443 VLESS TCP Stealth (опционально)

Обязательные настройки:

  • fingerprint: random (inbound 1, 2), qq или edge (inbound 3 stealth)
  • sockopt: tcpKeepAliveIdle=300, tcpKeepAliveInterval=15, tcpCongestion=bbr, tcpMptcp=true, tcpMaxSeg=1440
  • flow: пусто для XHTTP (inbound 1), xtls-rprx-vision для TCP (inbound 2, 3)

Без sockopt копятся zombie TCP-соединения (до 24K+), что ломает метрики.

MySQL: полная структура записей

XUI-сервер — 2 записи (по одной на каждый inbound)

-- Inbound 1: XHTTP (port 2087)
INSERT INTO server (id, name, ip, port, inbound_id, panel_type, is_active, city_id, ...)
VALUES (UUID_TO_BIN(UUID()), 'AT #8', '<IP>', 2087, 1, 'XUI', 0, ...);

-- Inbound 2: TCP (port 853)
INSERT INTO server (id, name, ip, port, inbound_id, panel_type, is_active, city_id, ...)
VALUES (UUID_TO_BIN(UUID()), 'AT #8-2', '<IP>', 853, 2, 'XUI', 0, ...);

WL-сервер (whitelist proxy)

Отдельная запись для проксированного маршрута. Подробнее: Добавление Whitelist-сервера.

WLS-сервер (stealth, inbound 3)

Требует двух записей: XUI (для fill_configs) + WHITELIST (для proxy-маршрута).

Stealth-серверы

Stealth inbounds маскируются под конкурентские VPN сервисы для обхода DPI. Отличаются от стандартных fingerprint'ом и SNI.

Создание stealth inbound:

python3 scripts/create_inbound.py --server IP --port 10443 --dest eh.vk.com:443 --fingerprint qq

Текущие stealth-серверы (6 шт):

Code Upstream IP Proxy (RU) SNI Fingerprint
WLSFR 79.127.138.202 212.233.123.88 eh.vk.com qq
WLSDE 89.222.124.21 90.156.219.233 eh.vk.com qq
WLSNL 45.67.231.125 83.166.235.99 eh.vk.com qq
WLSAT 5.181.21.90 90.156.214.48 eh.vk.com qq
WLSES 45.12.150.3 90.156.218.106 ozon.ru qq
WLSPL 88.218.2.46 90.156.213.143 stats.vk-portal.net edge

Отличия stealth inbound (ID=3) от стандартных:

Параметр Stealth (inbound 3) Стандартный (½)
port 10443 2087 / 853
fingerprint qq или edge random
SNI/dest eh.vk.com, ozon.ru, stats.vk-portal.net dl.google.com, vk.com
flow xtls-rprx-vision пусто (XHTTP) / xtls-rprx-vision (TCP)
network tcp xhttp / tcp
shortIds 1-2 коротких (e.g. ["34"]) min 3
serverNames 1 (e.g. ["eh.vk.com"]) min 2

MySQL записи для WLS (stealth) — нужны 2 записи на каждый upstream:

  • panel_type='XUI', inbound_id=3 — для fill_configs (добавляет клиентов)
  • panel_type='WHITELIST', inbound_id=3 — WLS proxy маршрут (change_ip = RU proxy)

Kernel Tuning (sysctl + ulimits)

Обязательно на каждом сервере. Без этого — мало файловых дескрипторов, маленькие TCP буферы, нет BBR, мёртвые SSH-соединения висят часами.

Source of truth: ansible/shiva-ansible/roles/sysctl/tasks/main.yml

Применяется через Ansible (playbooks/sysctl.yml) или commission_servers.py step 2.

/etc/sysctl.d/99-vpn-tuning.conf

# File descriptors (1M для 6000+ клиентов)
fs.file-max = 1000000
fs.nr_open = 1000000

# Network core
net.core.default_qdisc = fq
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.optmem_max = 65535

# TCP performance
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_rmem = 4096 131072 16777216
net.ipv4.tcp_wmem = 4096 131072 16777216
net.ipv4.tcp_mem = 65536 131072 262144
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_tw_buckets = 32768
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_fastopen = 0
net.ipv4.tcp_notsent_lowat = 16384
net.ipv4.ip_local_port_range = 1024 65535

# TCP keepalive
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5

# IP forwarding + security
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv6.conf.all.accept_redirects = 0

# Memory
vm.swappiness = 10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 15
vm.overcommit_memory = 1
vm.vfs_cache_pressure = 50

Ключевые параметры — почему наши значения отличаются от дефолтных:

Параметр Дефолт Наш Зачем
net.core.somaxconn 128 65535 accept() backlog для тысяч одновременных подключений
net.ipv4.tcp_max_syn_backlog 128 8192 SYN queue — защита от потерь при всплесках
net.ipv4.tcp_fin_timeout 60 15 Быстрее освобождать TIME_WAIT сокеты
net.ipv4.tcp_tw_reuse 0 1 Переиспользовать TIME_WAIT — критично при 50+ SSH
net.core.netdev_max_backlog 1000 65535 Буфер входящих пакетов на NIC
net.ipv4.tcp_keepalive_time 7200 300 Обнаружить мёртвое соединение через 5 мин (не 2 ч)
net.ipv4.tcp_slow_start_after_idle 1 0 Не сбрасывать cwnd на idle соединениях
net.ipv4.tcp_mtu_probing 0 1 MTU discovery для разных сетей
net.ipv4.tcp_notsent_lowat -1 16384 Меньше latency на write() — важно для VPN
vm.swappiness 60 10 Минимизировать swap на 1-2GB RAM VPS
net.core.default_qdisc pfifo_fast fq Fair Queue — нужно для BBR
net.ipv4.tcp_congestion_control cubic bbr BBR — выше пропускная способность
net.core.rmem_max / wmem_max 212992 16777216 16MB буферы для гигабитных соединений
fs.file-max ~100K 1000000 1M дескрипторов для 6000+ клиентов на сервер
vm.overcommit_memory 0 1 Разрешить overcommit (xray выделяет много VMA)
net.ipv4.tcp_fastopen 1 0 Отключено — конфликтует с Reality handshake

/etc/security/limits.d/99-vpn.conf

cat > /etc/security/limits.d/99-vpn.conf << 'EOF'
* soft nofile 1000000
* hard nofile 1000000
* soft nproc 1000000
* hard nproc 1000000
root soft nofile 1000000
root hard nofile 1000000
root soft nproc 1000000
root hard nproc 1000000
EOF

systemd limits — /etc/systemd/system.conf.d/limits.conf

mkdir -p /etc/systemd/system.conf.d
cat > /etc/systemd/system.conf.d/limits.conf << 'EOF'
[Manager]
DefaultLimitNOFILE=1000000
DefaultLimitNPROC=1000000
EOF
systemctl daemon-reexec

Worker-машина (10.99.87.249) — дополнительно

На worker'е (VPN Config Service) добавить к существующим параметрам:

net.ipv4.ip_local_port_range = 1024 65535    # больше ephemeral портов для исходящих SSH

Применение

  • Новые серверы: commission_servers.py step 2 — автоматически
  • Существующие: ansible-playbook -i inventory/servers.ini vpn_performance_tuning_2025.yml

Чеклист

  • Сервер в servers.ini
  • Ansible playbook vpn.yml выполнен
  • 3x-ui пароль admin сменён
  • SSH ключ vpn-config-service добавлен в authorized_keys
  • SSH daemon tuning (MaxStartups 200:30:300, MaxSessions 50, LoginGraceTime 15, ClientAliveInterval 30)
  • Kernel sysctl tuning (/etc/sysctl.d/99-vpn-tuning.conf) — BBR, somaxconn 65535, keepalive 300s
  • ulimits (/etc/security/limits.d/99-vpn.conf) — nofile/nproc 1000000
  • Inbound 1 (XHTTP:2087) создан с правильными sockopt и fingerprint
  • Inbound 2 (TCP:853) создан с правильными sockopt и fingerprint
  • UFW: порты 2087, 853, 9100 открыты
  • Сервер в MySQL: 2 записи XUI (inbound 1 + 2), is_active=0
  • Мониторинг (node_exporter в Prometheus)
  • Docker log rotation настроен
  • is_active = 1 после проверки
  • Ansible inventory servers.ini обновлён

См. также: Ansible · DevOps скрипты → commission_servers · VPN Config Service · Инвентарь серверов · SSL-сертификаты