docs: add HARDENING.md — step-by-step DPI bypass and server hardening guide
Covers 7 issues: WireGuard tunnel, zapret/nfqws for DPI, SNI protection, dnscrypt-proxy, HSTS, SSE timeout, wstunnel fallback. Includes commands, verification checklist, and rollback for each step. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
728
HARDENING.md
Normal file
728
HARDENING.md
Normal file
@@ -0,0 +1,728 @@
|
|||||||
|
# HARDENING.md — Защита Gitea от DPI/ТСПУ и повышение стабильности
|
||||||
|
|
||||||
|
> **Серверы:** sensey24.ru (45.132.50.182) + core-gitlab-01 (1.1.1.122)
|
||||||
|
> **Дата аудита:** 2026-02-21
|
||||||
|
> **Gitea:** 1.24.5, порт 3000 на core-gitlab-01
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Текущее состояние
|
||||||
|
|
||||||
|
| Компонент | Статус | Примечание |
|
||||||
|
|-----------|--------|------------|
|
||||||
|
| TCP tuning (BBR, буферы) | OK | sysctl настроен на обоих серверах |
|
||||||
|
| MSS clamping (1200) | OK | iptables TCPMSS на sensey24.ru |
|
||||||
|
| TCP keepalive | OK | net.ipv4.tcp_keepalive_time=60 |
|
||||||
|
| Apache ProxyPass | OK | Gitea проксируется через ppp0 (20.0.0.2:3000) |
|
||||||
|
| IPsec (strongSwan) | ПРОБЛЕМА | SA = 0, туннель без шифрования |
|
||||||
|
| DPI/ТСПУ защита | НЕТ | zapret, obfs4, stunnel не установлены |
|
||||||
|
| SNI скрытие | НЕТ | TLS ClientHello в открытом виде |
|
||||||
|
| DNS шифрование | НЕТ | 8.8.8.8 plaintext |
|
||||||
|
| HSTS | НЕТ | Заголовок не настроен |
|
||||||
|
| WireGuard | ЧАСТИЧНО | Установлен на sensey24.ru, не настроен |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проблема 1: IPsec SA = 0 → Замена на WireGuard
|
||||||
|
|
||||||
|
### Почему WireGuard вместо IPsec
|
||||||
|
|
||||||
|
- strongSwan показывает 0 Security Associations — туннель фактически не шифрует
|
||||||
|
- WireGuard проще в настройке, быстрее, меньше attack surface
|
||||||
|
- WireGuard уже установлен на sensey24.ru
|
||||||
|
- UDP 51820 меньше подвержен DPI-блокировкам, чем ESP/IKE
|
||||||
|
|
||||||
|
### 1.1 Генерация ключей
|
||||||
|
|
||||||
|
**На sensey24.ru:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -p 31216 robot@sensey24.ru
|
||||||
|
|
||||||
|
# Ключи уже могут быть — проверяем
|
||||||
|
ls /etc/wireguard/
|
||||||
|
|
||||||
|
# Генерация ключей сервера
|
||||||
|
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key
|
||||||
|
chmod 600 /etc/wireguard/server_private.key
|
||||||
|
```
|
||||||
|
|
||||||
|
**На core-gitlab-01:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -p 7913 robot@sensey24.ru # это проброс на core-gitlab-01
|
||||||
|
|
||||||
|
# Установка WireGuard
|
||||||
|
sudo apt update && sudo apt install -y wireguard
|
||||||
|
|
||||||
|
# Генерация ключей клиента
|
||||||
|
wg genkey | tee /etc/wireguard/client_private.key | wg pubkey > /etc/wireguard/client_public.key
|
||||||
|
chmod 600 /etc/wireguard/client_private.key
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Настройка на sensey24.ru (WG-сервер)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Подставить реальные ключи
|
||||||
|
SERVER_PRIVATE=$(cat /etc/wireguard/server_private.key)
|
||||||
|
CLIENT_PUBLIC=$(cat /etc/wireguard/client_public.key) # скопировать с core-gitlab-01
|
||||||
|
|
||||||
|
cat > /etc/wireguard/wg0.conf << EOF
|
||||||
|
[Interface]
|
||||||
|
Address = 10.10.10.1/24
|
||||||
|
ListenPort = 51820
|
||||||
|
PrivateKey = ${SERVER_PRIVATE}
|
||||||
|
|
||||||
|
# Разрешить форвардинг
|
||||||
|
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
# core-gitlab-01
|
||||||
|
PublicKey = ${CLIENT_PUBLIC}
|
||||||
|
AllowedIPs = 10.10.10.2/32
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod 600 /etc/wireguard/wg0.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Включение
|
||||||
|
sudo systemctl enable wg-quick@wg0
|
||||||
|
sudo wg-quick up wg0
|
||||||
|
|
||||||
|
# Открыть порт
|
||||||
|
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT
|
||||||
|
sudo netfilter-persistent save
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Настройка на core-gitlab-01 (WG-клиент)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CLIENT_PRIVATE=$(cat /etc/wireguard/client_private.key)
|
||||||
|
SERVER_PUBLIC=$(cat /etc/wireguard/server_public.key) # скопировать с sensey24.ru
|
||||||
|
|
||||||
|
cat > /etc/wireguard/wg0.conf << EOF
|
||||||
|
[Interface]
|
||||||
|
Address = 10.10.10.2/24
|
||||||
|
PrivateKey = ${CLIENT_PRIVATE}
|
||||||
|
|
||||||
|
# Keepalive через NAT
|
||||||
|
[Peer]
|
||||||
|
PublicKey = ${SERVER_PUBLIC}
|
||||||
|
Endpoint = 45.132.50.182:51820
|
||||||
|
AllowedIPs = 10.10.10.1/32
|
||||||
|
PersistentKeepalive = 25
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod 600 /etc/wireguard/wg0.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable wg-quick@wg0
|
||||||
|
sudo wg-quick up wg0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.4 Миграция Apache ProxyPass с ppp0 на wg0
|
||||||
|
|
||||||
|
**На sensey24.ru** — обновить конфиг Apache:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Найти текущий конфиг с ProxyPass
|
||||||
|
grep -r "20.0.0.2" /etc/apache2/
|
||||||
|
|
||||||
|
# Заменить IP в ProxyPass
|
||||||
|
# Было: ProxyPass / http://20.0.0.2:3000/
|
||||||
|
# Стало: ProxyPass / http://10.10.10.2:3000/
|
||||||
|
sudo sed -i 's|20\.0\.0\.2|10.10.10.2|g' /etc/apache2/sites-enabled/*.conf
|
||||||
|
|
||||||
|
# Проверить синтаксис и перезагрузить
|
||||||
|
sudo apache2ctl configtest
|
||||||
|
sudo systemctl reload apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.5 Тест
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# На sensey24.ru:
|
||||||
|
ping -c 3 10.10.10.2
|
||||||
|
curl -s -o /dev/null -w "%{http_code}" http://10.10.10.2:3000/
|
||||||
|
|
||||||
|
# Проверить WG-туннель:
|
||||||
|
sudo wg show
|
||||||
|
|
||||||
|
# Ожидаемый результат:
|
||||||
|
# interface: wg0
|
||||||
|
# latest handshake: <недавнее время>
|
||||||
|
# transfer: X received, Y sent
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.6 Откат
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# На sensey24.ru — вернуть Apache на ppp0:
|
||||||
|
sudo sed -i 's|10\.10\.10\.2|20.0.0.2|g' /etc/apache2/sites-enabled/*.conf
|
||||||
|
sudo systemctl reload apache2
|
||||||
|
|
||||||
|
# Остановить WG:
|
||||||
|
sudo wg-quick down wg0
|
||||||
|
sudo systemctl disable wg-quick@wg0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проблема 2: DPI/ТСПУ блокирует HTTPS → zapret (nfqws)
|
||||||
|
|
||||||
|
### Почему zapret
|
||||||
|
|
||||||
|
- zapret/nfqws модифицирует исходящие TLS ClientHello пакеты
|
||||||
|
- Ломает DPI-сигнатуры без изменения для клиента
|
||||||
|
- Работает прозрачно на уровне iptables/nfqueue
|
||||||
|
- Не требует клиентского ПО
|
||||||
|
|
||||||
|
### 2.1 Установка на sensey24.ru
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -p 31216 robot@sensey24.ru
|
||||||
|
|
||||||
|
cd /opt
|
||||||
|
sudo git clone --depth 1 https://github.com/bol-van/zapret.git
|
||||||
|
cd zapret
|
||||||
|
|
||||||
|
# Сборка
|
||||||
|
sudo make -C nfq
|
||||||
|
|
||||||
|
# Проверить бинарник
|
||||||
|
ls -la nfq/nfqws
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Конфигурация nfqws для HTTPS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Создать конфиг
|
||||||
|
sudo mkdir -p /etc/zapret
|
||||||
|
|
||||||
|
cat > /etc/zapret/nfqws.conf << 'EOF'
|
||||||
|
# Стратегия для обхода DPI на порту 443 (HTTPS/TLS)
|
||||||
|
# --dpi-desync=fake,split2 — отправляет фейковый ClientHello + разбивает настоящий
|
||||||
|
# --dpi-desync-ttl=6 — фейковый пакет "умирает" до DPI, но после маршрутизатора
|
||||||
|
NFQWS_ARGS="--dpi-desync=fake,split2 --dpi-desync-ttl=6 --dpi-desync-fooling=md5sig"
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 Правило iptables для перенаправления в NFQUEUE
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Перехватываем исходящий HTTPS-трафик
|
||||||
|
sudo iptables -t mangle -A POSTROUTING -p tcp --dport 443 -m connbytes --connbytes 1:6 --connbytes-mode packets --connbytes-dir=original -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
|
||||||
|
# Сохранить
|
||||||
|
sudo netfilter-persistent save
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 Systemd unit
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > /etc/systemd/system/nfqws.service << 'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=nfqws DPI bypass
|
||||||
|
After=network.target
|
||||||
|
Wants=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
EnvironmentFile=/etc/zapret/nfqws.conf
|
||||||
|
ExecStart=/opt/zapret/nfq/nfqws --qnum=200 $NFQWS_ARGS
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
LimitNOFILE=65535
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable nfqws
|
||||||
|
sudo systemctl start nfqws
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.5 Тест
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверить что nfqws запущен
|
||||||
|
sudo systemctl status nfqws
|
||||||
|
|
||||||
|
# Проверить что пакеты идут через NFQUEUE
|
||||||
|
sudo iptables -t mangle -L POSTROUTING -v -n | grep NFQUEUE
|
||||||
|
|
||||||
|
# Тест подключения с внешнего хоста
|
||||||
|
curl -v --connect-timeout 10 https://git.sensey24.ru/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.6 Откат
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl stop nfqws
|
||||||
|
sudo systemctl disable nfqws
|
||||||
|
sudo iptables -t mangle -D POSTROUTING -p tcp --dport 443 -m connbytes --connbytes 1:6 --connbytes-mode packets --connbytes-dir=original -j NFQUEUE --queue-num 200 --queue-bypass
|
||||||
|
sudo netfilter-persistent save
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проблема 3: SNI в открытом виде
|
||||||
|
|
||||||
|
### Вариант A: zapret --dpi-desync (уже решает)
|
||||||
|
|
||||||
|
Конфигурация из Проблемы 2 уже разбивает TLS ClientHello. Параметр `split2` отделяет SNI от начала handshake, что ломает простые DPI-фильтры по SNI.
|
||||||
|
|
||||||
|
Для усиления можно добавить `--dpi-desync-split-pos=1` в `/etc/zapret/nfqws.conf`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Обновить конфиг
|
||||||
|
sudo sed -i 's|NFQWS_ARGS="|NFQWS_ARGS="--dpi-desync-split-pos=1 |' /etc/zapret/nfqws.conf
|
||||||
|
sudo systemctl restart nfqws
|
||||||
|
```
|
||||||
|
|
||||||
|
### Вариант B: Cloudflare Proxy (опционально)
|
||||||
|
|
||||||
|
Если запрос идёт через Cloudflare, SNI показывает IP Cloudflare, а не реальный сервер.
|
||||||
|
|
||||||
|
1. Добавить домен `git.sensey24.ru` в Cloudflare
|
||||||
|
2. Включить проксирование (оранжевое облачко)
|
||||||
|
3. В настройках SSL → Full (Strict)
|
||||||
|
4. В Apache добавить заголовки для реального IP:
|
||||||
|
|
||||||
|
```apache
|
||||||
|
# /etc/apache2/sites-enabled/git.sensey24.ru.conf
|
||||||
|
RemoteIPHeader CF-Connecting-IP
|
||||||
|
RemoteIPTrustedProxy 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Минус:** Cloudflare добавляет задержку и видит весь трафик. Для приватного Git-сервера zapret предпочтительнее.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проблема 4: DNS в открытом виде → dnscrypt-proxy
|
||||||
|
|
||||||
|
### 4.1 Установка на sensey24.ru
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt install -y dnscrypt-proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Конфигурация
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp /etc/dnscrypt-proxy/dnscrypt-proxy.toml /etc/dnscrypt-proxy/dnscrypt-proxy.toml.bak
|
||||||
|
|
||||||
|
cat > /etc/dnscrypt-proxy/dnscrypt-proxy.toml << 'EOF'
|
||||||
|
listen_addresses = ['127.0.0.53:53']
|
||||||
|
max_clients = 250
|
||||||
|
|
||||||
|
# Серверы с поддержкой DoH и DNSSEC
|
||||||
|
server_names = ['cloudflare', 'google', 'scaleway-fr']
|
||||||
|
|
||||||
|
[query_log]
|
||||||
|
file = '/var/log/dnscrypt-proxy/query.log'
|
||||||
|
|
||||||
|
[sources]
|
||||||
|
[sources.'public-resolvers']
|
||||||
|
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md',
|
||||||
|
'https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md']
|
||||||
|
cache_file = '/var/cache/dnscrypt-proxy/public-resolvers.md'
|
||||||
|
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 Отключение systemd-resolved и переключение DNS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Остановить systemd-resolved (если используется)
|
||||||
|
sudo systemctl stop systemd-resolved
|
||||||
|
sudo systemctl disable systemd-resolved
|
||||||
|
|
||||||
|
# Настроить resolv.conf
|
||||||
|
sudo rm -f /etc/resolv.conf
|
||||||
|
echo "nameserver 127.0.0.53" | sudo tee /etc/resolv.conf
|
||||||
|
sudo chattr +i /etc/resolv.conf # защита от перезаписи
|
||||||
|
|
||||||
|
# Запуск
|
||||||
|
sudo systemctl enable dnscrypt-proxy
|
||||||
|
sudo systemctl restart dnscrypt-proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 Проверка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверить что dnscrypt-proxy слушает
|
||||||
|
ss -ulnp | grep 53
|
||||||
|
|
||||||
|
# Проверить резолвинг
|
||||||
|
dig git.sensey24.ru @127.0.0.53
|
||||||
|
|
||||||
|
# Убедиться что DNS НЕ идёт в plaintext (должен быть пустой вывод)
|
||||||
|
sudo tcpdump -i eth0 -n port 53 -c 10 &
|
||||||
|
dig google.com
|
||||||
|
# Если tcpdump ничего не показывает — DNS зашифрован
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.5 Откат
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chattr -i /etc/resolv.conf
|
||||||
|
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
|
||||||
|
sudo systemctl stop dnscrypt-proxy
|
||||||
|
sudo systemctl disable dnscrypt-proxy
|
||||||
|
sudo systemctl enable systemd-resolved
|
||||||
|
sudo systemctl start systemd-resolved
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проблема 5: Нет HSTS → Apache header
|
||||||
|
|
||||||
|
### 5.1 Включение
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -p 31216 robot@sensey24.ru
|
||||||
|
|
||||||
|
# Включить модуль headers
|
||||||
|
sudo a2enmod headers
|
||||||
|
|
||||||
|
# Добавить HSTS в конфиг SSL-сайта
|
||||||
|
sudo sed -i '/<VirtualHost \*:443>/a\ Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"' /etc/apache2/sites-enabled/*ssl*.conf
|
||||||
|
|
||||||
|
# Если файл называется иначе — найти нужный:
|
||||||
|
grep -rl "443" /etc/apache2/sites-enabled/
|
||||||
|
|
||||||
|
# Перезагрузить
|
||||||
|
sudo apache2ctl configtest && sudo systemctl reload apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 Проверка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sI https://git.sensey24.ru/ | grep -i strict
|
||||||
|
# Ожидаемый результат:
|
||||||
|
# Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 Откат
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo sed -i '/Strict-Transport-Security/d' /etc/apache2/sites-enabled/*ssl*.conf
|
||||||
|
sudo systemctl reload apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проблема 6: Apache SSE timeout → ProxyTimeout для EventSource
|
||||||
|
|
||||||
|
### Почему
|
||||||
|
|
||||||
|
Gitea использует Server-Sent Events (SSE) для real-time обновлений. Стандартный `ProxyTimeout 60` обрывает SSE-соединения.
|
||||||
|
|
||||||
|
### 6.1 Добавить location-specific timeout
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -p 31216 robot@sensey24.ru
|
||||||
|
|
||||||
|
# Найти конфиг виртуального хоста
|
||||||
|
grep -rl "ProxyPass" /etc/apache2/sites-enabled/
|
||||||
|
```
|
||||||
|
|
||||||
|
Добавить в конфиг виртуального хоста (внутри `<VirtualHost *:443>`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat >> /tmp/sse-patch.conf << 'EOF'
|
||||||
|
|
||||||
|
# SSE/EventSource — длинные соединения
|
||||||
|
<Location "/user/events">
|
||||||
|
ProxyPass http://10.10.10.2:3000/user/events
|
||||||
|
ProxyPassReverse http://10.10.10.2:3000/user/events
|
||||||
|
ProxyTimeout 3600
|
||||||
|
</Location>
|
||||||
|
|
||||||
|
# Gitea API streaming
|
||||||
|
<Location "/api/v1">
|
||||||
|
ProxyTimeout 300
|
||||||
|
</Location>
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
Вставить перед закрывающим `</VirtualHost>`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Определить номер строки </VirtualHost> в SSL-конфиге
|
||||||
|
CONF=$(grep -rl "ProxyPass.*3000" /etc/apache2/sites-enabled/)
|
||||||
|
LINE=$(grep -n "</VirtualHost>" "$CONF" | tail -1 | cut -d: -f1)
|
||||||
|
sudo sed -i "${LINE}r /tmp/sse-patch.conf" "$CONF"
|
||||||
|
|
||||||
|
sudo apache2ctl configtest && sudo systemctl reload apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Проверка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSE должен держаться дольше 60 секунд
|
||||||
|
curl -N -H "Accept: text/event-stream" https://git.sensey24.ru/user/events &
|
||||||
|
sleep 70
|
||||||
|
# Если соединение живо через 70 секунд — работает
|
||||||
|
kill %1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 Откат
|
||||||
|
|
||||||
|
Удалить добавленные блоки `<Location>` из конфига и `sudo systemctl reload apache2`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проблема 7: WireGuard через wstunnel (если WG блокируют)
|
||||||
|
|
||||||
|
### Когда нужно
|
||||||
|
|
||||||
|
Если провайдер/ТСПУ блокирует UDP 51820 (WireGuard), трафик оборачивается в WebSocket поверх HTTPS (порт 443), что выглядит как обычный HTTPS.
|
||||||
|
|
||||||
|
### 7.1 Установка wstunnel на обоих серверах
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# На sensey24.ru и core-gitlab-01:
|
||||||
|
WSTUNNEL_VERSION="10.1.0"
|
||||||
|
wget "https://github.com/erebe/wstunnel/releases/download/v${WSTUNNEL_VERSION}/wstunnel_${WSTUNNEL_VERSION}_linux_amd64.tar.gz"
|
||||||
|
tar xzf "wstunnel_${WSTUNNEL_VERSION}_linux_amd64.tar.gz"
|
||||||
|
sudo mv wstunnel /usr/local/bin/
|
||||||
|
sudo chmod +x /usr/local/bin/wstunnel
|
||||||
|
|
||||||
|
# Проверка
|
||||||
|
wstunnel --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Серверная сторона (sensey24.ru)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > /etc/systemd/system/wstunnel-server.service << 'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=wstunnel server (WireGuard over WSS)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/wstunnel server \
|
||||||
|
--restrict-to 127.0.0.1:51820 \
|
||||||
|
wss://0.0.0.0:8443
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable wstunnel-server
|
||||||
|
sudo systemctl start wstunnel-server
|
||||||
|
|
||||||
|
# Открыть порт
|
||||||
|
sudo iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
|
||||||
|
sudo netfilter-persistent save
|
||||||
|
```
|
||||||
|
|
||||||
|
При этом WireGuard должен слушать на `127.0.0.1:51820`. Обновить `/etc/wireguard/wg0.conf`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Interface]
|
||||||
|
Address = 10.10.10.1/24
|
||||||
|
ListenPort = 51820
|
||||||
|
# Привязать к localhost — трафик приходит через wstunnel
|
||||||
|
PostUp = ip route add 10.10.10.0/24 dev wg0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 Клиентская сторона (core-gitlab-01)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > /etc/systemd/system/wstunnel-client.service << 'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=wstunnel client (WireGuard over WSS)
|
||||||
|
After=network.target
|
||||||
|
Before=wg-quick@wg0.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/wstunnel client \
|
||||||
|
--local-to-remote udp://127.0.0.1:51820:127.0.0.1:51820 \
|
||||||
|
wss://45.132.50.182:8443
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable wstunnel-client
|
||||||
|
sudo systemctl start wstunnel-client
|
||||||
|
```
|
||||||
|
|
||||||
|
Обновить `/etc/wireguard/wg0.conf` на core-gitlab-01:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Interface]
|
||||||
|
Address = 10.10.10.2/24
|
||||||
|
PrivateKey = <CLIENT_PRIVATE_KEY>
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = <SERVER_PUBLIC_KEY>
|
||||||
|
# Endpoint теперь через wstunnel (localhost)
|
||||||
|
Endpoint = 127.0.0.1:51820
|
||||||
|
AllowedIPs = 10.10.10.1/32
|
||||||
|
PersistentKeepalive = 25
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart wg-quick@wg0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 Тест
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# На core-gitlab-01:
|
||||||
|
ping -c 3 10.10.10.1
|
||||||
|
|
||||||
|
# Проверить что wstunnel работает:
|
||||||
|
sudo systemctl status wstunnel-client
|
||||||
|
ss -tnp | grep 8443
|
||||||
|
# Должно показать TCP-соединение к 45.132.50.182:8443
|
||||||
|
|
||||||
|
# На sensey24.ru:
|
||||||
|
sudo wg show
|
||||||
|
# Должен показать handshake и transfer
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.5 Откат
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# На core-gitlab-01:
|
||||||
|
sudo systemctl stop wstunnel-client
|
||||||
|
sudo systemctl disable wstunnel-client
|
||||||
|
# Вернуть Endpoint в wg0.conf на 45.132.50.182:51820
|
||||||
|
sudo systemctl restart wg-quick@wg0
|
||||||
|
|
||||||
|
# На sensey24.ru:
|
||||||
|
sudo systemctl stop wstunnel-server
|
||||||
|
sudo systemctl disable wstunnel-server
|
||||||
|
sudo iptables -D INPUT -p tcp --dport 8443 -j ACCEPT
|
||||||
|
sudo netfilter-persistent save
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Верификация — полный чеклист
|
||||||
|
|
||||||
|
После применения всех изменений выполнить проверки:
|
||||||
|
|
||||||
|
### Инфраструктура
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. WireGuard туннель активен
|
||||||
|
sudo wg show
|
||||||
|
# Ожидание: handshake < 2 минут назад, transfer > 0
|
||||||
|
|
||||||
|
# 2. Ping через WG
|
||||||
|
ping -c 5 10.10.10.2 # с sensey24.ru
|
||||||
|
ping -c 5 10.10.10.1 # с core-gitlab-01
|
||||||
|
|
||||||
|
# 3. Gitea доступна через WG
|
||||||
|
curl -s -o /dev/null -w "%{http_code}" http://10.10.10.2:3000/
|
||||||
|
# Ожидание: 200
|
||||||
|
```
|
||||||
|
|
||||||
|
### DPI/ТСПУ обход
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 4. nfqws запущен
|
||||||
|
sudo systemctl is-active nfqws
|
||||||
|
# Ожидание: active
|
||||||
|
|
||||||
|
# 5. NFQUEUE ловит пакеты
|
||||||
|
sudo iptables -t mangle -L POSTROUTING -v -n | grep "NFQUEUE.*200"
|
||||||
|
# Ожидание: счётчик pkts > 0
|
||||||
|
|
||||||
|
# 6. Внешний доступ к Gitea
|
||||||
|
curl -v --connect-timeout 15 https://git.sensey24.ru/
|
||||||
|
# Ожидание: HTTP 200, без timeout
|
||||||
|
```
|
||||||
|
|
||||||
|
### DNS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 7. dnscrypt-proxy работает
|
||||||
|
sudo systemctl is-active dnscrypt-proxy
|
||||||
|
# Ожидание: active
|
||||||
|
|
||||||
|
# 8. DNS через зашифрованный канал
|
||||||
|
dig +short google.com @127.0.0.53
|
||||||
|
# Ожидание: IP-адрес
|
||||||
|
|
||||||
|
# 9. Нет plaintext DNS
|
||||||
|
sudo timeout 10 tcpdump -i eth0 -n port 53 -c 1 2>/dev/null
|
||||||
|
dig google.com > /dev/null 2>&1
|
||||||
|
# Ожидание: tcpdump не перехватил пакетов
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTTPS/TLS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 10. HSTS заголовок присутствует
|
||||||
|
curl -sI https://git.sensey24.ru/ | grep -i "strict-transport"
|
||||||
|
# Ожидание: Strict-Transport-Security: max-age=31536000
|
||||||
|
|
||||||
|
# 11. SSL Labs (внешний тест)
|
||||||
|
# Открыть: https://www.ssllabs.com/ssltest/analyze.html?d=git.sensey24.ru
|
||||||
|
# Ожидание: A или A+
|
||||||
|
```
|
||||||
|
|
||||||
|
### Производительность
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 12. SSE держится > 60 сек
|
||||||
|
timeout 75 curl -s -N -H "Accept: text/event-stream" https://git.sensey24.ru/user/events > /dev/null 2>&1
|
||||||
|
echo "Exit code: $?"
|
||||||
|
# Ожидание: exit code 124 (killed by timeout, не обрыв соединения)
|
||||||
|
|
||||||
|
# 13. Git clone работает
|
||||||
|
cd /tmp && git clone https://git.sensey24.ru/aibot777/test-repo.git 2>&1 | tail -1
|
||||||
|
rm -rf test-repo
|
||||||
|
# Ожидание: успешное клонирование
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сводная таблица (после применения)
|
||||||
|
|
||||||
|
| # | Проверка | Команда | Ожидание |
|
||||||
|
|---|----------|---------|----------|
|
||||||
|
| 1 | WG туннель | `sudo wg show` | handshake < 2 мин |
|
||||||
|
| 2 | WG ping | `ping 10.10.10.2` | 0% loss |
|
||||||
|
| 3 | Gitea через WG | `curl http://10.10.10.2:3000/` | 200 |
|
||||||
|
| 4 | nfqws | `systemctl is-active nfqws` | active |
|
||||||
|
| 5 | NFQUEUE | `iptables -t mangle -L` | pkts > 0 |
|
||||||
|
| 6 | Внешний доступ | `curl https://git.sensey24.ru/` | 200 |
|
||||||
|
| 7 | dnscrypt | `systemctl is-active dnscrypt-proxy` | active |
|
||||||
|
| 8 | DNS resolve | `dig @127.0.0.53 google.com` | ответ есть |
|
||||||
|
| 9 | No plaintext DNS | `tcpdump port 53` | пусто |
|
||||||
|
| 10 | HSTS | `curl -sI \| grep strict` | заголовок есть |
|
||||||
|
| 11 | SSL Labs | браузер | A/A+ |
|
||||||
|
| 12 | SSE > 60s | `timeout 75 curl SSE` | exit 124 |
|
||||||
|
| 13 | Git clone | `git clone` | success |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Порядок применения (рекомендуемый)
|
||||||
|
|
||||||
|
1. **WireGuard** (Проблема 1) — база для шифрованного канала
|
||||||
|
2. **Миграция Apache** (1.4) — переключить ProxyPass на wg0
|
||||||
|
3. **zapret/nfqws** (Проблема 2) — обход DPI
|
||||||
|
4. **HSTS** (Проблема 5) — быстро, одна команда
|
||||||
|
5. **SSE timeout** (Проблема 6) — стабильность
|
||||||
|
6. **dnscrypt-proxy** (Проблема 4) — DNS шифрование
|
||||||
|
7. **wstunnel** (Проблема 7) — только если WG заблокирован
|
||||||
|
|
||||||
|
> Каждый шаг независим и может быть откачен отдельно. Рекомендуется применять по одному и тестировать после каждого.
|
||||||
Reference in New Issue
Block a user