mirror of
https://github.com/vvzvlad/trickster-vpn.git
synced 2024-12-25 18:31:00 +03:00
fix typos
This commit is contained in:
parent
c1145b2fc5
commit
eae6759ac8
@ -19,17 +19,21 @@
|
||||
Ах да, еще UDP. UDP для туннелей это хорошо, потому что у TCP уже есть механизмы, которые позволяют ему работать на неидеальных соединениях, а UDP представляет из себя именно такое соединение. А когда вы засовываете TCP в TCP, то отказываетесь от большей части этих механизмов (инкапсулированный TCP-пакет будет гарантированно доставлен другой стороне, хотя протокол допускает недоставку), но все еще несете весь оверхед вида "хендшейк соединения для отправки хендшейка".
|
||||
Не говоря уж о том, что инкапсулировать UDP в TCP — ничуть не лучшая идея, потому что сразу рушит все предположения всяких скайпов о том, что лучше пропустить пару пакетов, чем уменьшить задержку: каждый UDP пакет в этом случае будет принудительно перепослан и доставлен корректно, не считаясь с тем, сколько это займет времени.
|
||||
|
||||
Особенно для одинокого пользователя-хакера приятна работа с шифрованием: нет ни необходимости в сертификатах и удостоверяющих центрах, ни в логинах-паролях, все, что нужно — это с тем пиром, с которым хотите установить соединение, передать друг другу публичные ключи вашего интерфейса WG. Для больших компаний, это, конечно, будет скорее минусом, как и то, что WG — это только базовая часть полноценной большой инфраструктуры VPN.
|
||||
Особенно для одинокого пользователя-хакера приятна работа с шифрованием: нет ни необходимости в сертификатах и удостоверяющих центрах, ни в логинах-паролях, все, что нужно — это с тем пиром, с которым хотите установить соединение, передать друг другу публичные ключи вашего интерфейса WG. Для больших компаний, это, конечно, будет скорее минусом, как и то, что WG — это только базовая часть полноценной большой инфраструктуры VPN.
|
||||
Но, например, именно WireGuard использовали в Cloudflare для своего WARP (https://blog.cloudflare.com/announcing-warp-plus/, https://blog.cloudflare.com/warp-technical-challenges/), правда, написав его собственную реализацию — boringtun.
|
||||
|
||||
Еще одним минусом WG является то, что трафик не обфусцирован — DPI может обнаружить трафик WireGuard, так что его можно довольно легко заблокировать (не говоря уж о блокировке UDP совсем, что почти не мешает вебу, но гарантированно ломает работу WireGuard). Для скрытия трафика рекомендуется использовать специализированное ПО — Cloak, Obfsproxy, Shadowsocks, Stunnel, SoftEther, SSTP, или, в конце-концов, простой SSH. Часть из этих инструментов может работать совместно с WG, а часть способа его заменять в качестве инструмента стеганографии: WG изначально создавался под скорость и криптографическую защищенность.
|
||||
|
||||
|
||||
Если очень упрощать, ключи работают следующим образом: у нас есть закрытый (приватный) ключ, из которого можно сгенерировать открытый, или публичный. Наоборот — нельзя, из открытого ключа мы получить закрытый никак не можем. После чего, мы можем зашифровать с помощью закрытого ключа какую-то строку, а при помощи открытого — расшифровать ее и тем самым убедиться, что у собеседника точно есть закрытый ключ, а значит, он тот, за кого себя выдает. Таким образом, мы можем без проблем передавать открытый ключ — он всего лишь позволяет проверить подлинность автора, но не притвориться им. Это как в SSH — публичный ключ лежит на сервере, где его потеря небольшая беда: все что сможет сделать с ним злоумышленник это положить его на свой сервер, чтобы вы к нему могли подключиться с помощью закрытого ключа.
|
||||
Если очень упрощать, ключи работают следующим образом: у нас есть закрытый (приватный) ключ, из которого можно сгенерировать открытый, или публичный. Наоборот — нельзя, из открытого ключа мы получить закрытый никак не можем. После чего, мы можем зашифровать с помощью закрытого ключа какую-то строку, а при помощи открытого — расшифровать ее и тем самым убедиться, что у собеседника точно есть закрытый ключ, а значит, он тот, за кого себя выдает. Таким образом, мы можем без проблем передавать открытый ключ — он всего лишь позволяет проверить подлинность автора, но не притвориться им.
|
||||
Это как в SSH — публичный ключ лежит на сервере, где его потеря небольшая беда: все что сможет сделать с ним злоумышленник это положить его на свой сервер, чтобы вы к нему могли подключиться с помощью закрытого ключа.
|
||||
|
||||
Так вот, в WG первый этап подключения заключается в том, что каждая сторона с помощью зашифрованного приватным ключом сообщения доказывает собеседнику, что она именно она: это проверяется публичным ключом.
|
||||
Второй этап — это создание с помощью этих ключей и матана симметричных ключей для шифрования самого трафика. Благодаря тому, что расшифровать зашифрованное публичным ключом нельзя без приватного, мы сможем создать ключ для симметричного шифрования и отправить его по защищенному каналу. Этот шаг необходим потому, что симметричное шифрование — это гораздо менее ресурсоемкая операция, и минус у нее только один: необходимость синхронизации ключа у обоих сторон, при том, что перехват ключа третьей стороной ведет к возможности расшифровки трафика. Но эта проблема решается с помощью ассиметричной схемы. Это называется "Протокол Диффи — Хеллмана" — способ защищенного получения общего секретного ключа. В WG используется ECDH — вариация Диффи-Хеллмана на эллиптических кривых. Первые два этапа в терминах WG называется рукопожатием или хендшейком.
|
||||
После всего этого эти симметричные ключи используются уже для шифрования трафика. Раз в 2 минуты происходит новое рукопожатие, и сессионные ключи меняются.
|
||||
После всего этого эти симметричные ключи используются уже для шифрования трафика. Раз в 2 минуты происходит новое рукопожатие, и сессионные ключи меняются.
|
||||
|
||||
Разумеется, все немного сложнее — например, отправляются не сами ключи, а сгенерированные на основе их эферемные ключи, которые удаляются сразу после операции, и так далее. Заинтересовавшихся отправляю к [краткому описанию криптографии](https://www.wireguard.com/protocol/).
|
||||
|
||||
А мы же перейдем к более практическим действиям.
|
||||
|
||||
## Шаг первый: создаем и настраиваем два сервера.
|
||||
@ -133,7 +137,7 @@ root@:~# ip route | awk '/default/ { print $5 }'
|
||||
Продолжим писать конфиг: в оба конфига надо добавить секцию **Peer**, чтобы связать сервера друг с другом.
|
||||
Я намеренно не привожу сразу готовые конфиги, потому что хочу показать механизм создания конфигов в ручном режиме — в свое время у меня были проблемы с тем, что я генерировал конфиги утилитами типа ```easy-wg-quick``` или веб-интерфейсами, которые спрашивают тебя о названии клиента и красиво показывают QR-код, но отнюдь не способствуют пониманию того, как работает WG на самом деле.
|
||||
|
||||
Итак, добавляем в каждый конфиг по секции **Peer**, для чего генерируем из приватного ключа публичный (вот в ```pubkey``` как раз происходит крипто-магия):
|
||||
Итак, добавляем в каждый конфиг секцию **Peer**, для чего генерируем из приватного ключа публичный (вот в ```pubkey``` как раз происходит крипто-магия):
|
||||
```bash
|
||||
echo "kOd3FVBggwpjD3AlZKXUxNTzJT0+f3MJdUdR8n6ZBn8=" | wg pubkey
|
||||
MxnOnIlKfSyZyRutnYyoWHb3Izjalgf1t8F1oPJiyyw=
|
||||
@ -158,8 +162,9 @@ PersistentKeepalive=25
|
||||
В данных примерах **AllowedIPs** можно читать как "адреса, трафик на которые будет маршрутизироваться в туннель этого пира, и с которых пир сможет отправить что-то в туннель".
|
||||
|
||||
То есть, пункт ```AllowedIPs = 10.20.30.3/32``` означает, буквально, "только запросы на 10.20.30.3 (адрес пира WG) отправлять в туннель", т.е. дать доступ только до машины этого клиента.
|
||||
Пункт ```AllowedIPs = 192.168.88.0/24``` означает, что при запросе адреса из этой подсети этот запрос уйдет в туннель клиента, и если у него включен форвардинг и ему доступна эта подсеть, то к ней можно будет получить доступ.
|
||||
А ```AllowedIPs = 0.0.0.0/0``` означает, что в туннель надо маршрутизировать весь трафик вообще. Правда, это не относится к трафику, например, локальной сети: приоритет у маршрута, который создается из маски подсети и адреса шлюза, выше, чем у 0.0.0.0/0. Также, маршрут 0.0.0.0/0 перебьют маршруты других пиров, если они будут в конфиге.
|
||||
Пункт ```AllowedIPs = 192.168.88.0/24``` означает, что при запросе адреса из этой подсети этот запрос уйдет в туннель клиента, и если у него включен форвардинг и ему доступна эта подсеть, то к ней можно будет получить доступ.
|
||||
```AllowedIPs = 0.0.0.0/0``` означает, что в туннель надо маршрутизировать весь трафик вообще. Правда, это не относится к трафику, например, локальной сети: приоритет у маршрута, который создается из маски подсети и адреса шлюза, выше, чем у 0.0.0.0/0. Также, маршрут 0.0.0.0/0 перебьют маршруты других пиров, если они будут в конфиге.
|
||||
|
||||
В данном случае ```AllowedIPs=10.20.30.0/24``` означает что трафик с **external** в подсеть 10.20.30.0-10.20.30.255 будет уходить в туннель к **internal**. В принципе, нужды в этом особо нет, **external** у нас исключительно выходная нода. Но вдруг мы как-нибудь захотим зайти оттуда по ssh на какую-нибудь другую машину.
|
||||
|
||||
Повторяем генерацию публичного ключа с **external**:
|
||||
@ -361,7 +366,7 @@ default via 195.2.79.1 dev ens3 onlink
|
||||
...
|
||||
```
|
||||
|
||||
Вот 195.2.79.1 и ens3 и есть нужные нам данные. Используем уже знакомые нам подстановочные команды и создадим новый маршрут:
|
||||
Вот ```195.2.79.1``` и ```ens3``` и есть нужные нам данные. Используем уже знакомые нам подстановочные команды и создадим новый маршрут:
|
||||
```bash
|
||||
target_ip="212.193.158.175/32"
|
||||
gateway=`ip route | awk '/default/ {print $3; exit}'`
|
||||
@ -391,24 +396,27 @@ HOST: vvzvladMBP14.local Loss% Snt Last Avg Best Wrst StDev
|
||||
5.|-- cdn.ngenix.net 0.0% 10 3.8 5.0 3.8 8.4 1.3
|
||||
```
|
||||
|
||||
Сбермаркет, правда, все еще не открываемся: видимо, проверяет на наличие VPN какой-то другой сервер, а не тот, в адрес которого резолвится имя домена.
|
||||
Ура, сбермаркет открывается! Правда, так может сработать не со всеми сервисами: например, JS на сайте может дернуть другой сервер, а не тот, в адрес которого резолвится имя домена, и до которого нет маршрута.
|
||||
Можно сходить на https://asnlookup.com/, вбить туда адрес, и получить принадлежность адреса к AS и заодно список подсетей этой autonomous system (AS34879, OOO Sovremennye setevye tekhnologii). С большой вероятностью для более-менее крупных компаний это и будет их сетевая инфраструктура (ну или по крайней мере, инфраструктура, относящаяся к конкретному сайту), прописав для которой маршруты, вы обеспечите доступ на нужный вам сайт/сервис. Для мелких сайтов вы скорее всего получите AS хостера или дата-центра, но, во-первых, это тоже сработает, а во-вторых, мелкие сайты обычно и не закрывают иностранные диапазоны, потому что не испытывают проблем с DDOSом из-за границы.
|
||||
|
||||
Но можно сделать проще: засунуть в маршруты все адреса российского сегмента (спасибо [статье](https://habr.com/en/post/659655/) на хабре) и не париться о ручном добавлении.
|
||||
Но можно сделать проще и автоматически: засунуть в маршруты вообще все адреса российского сегмента (спасибо [статье](https://habr.com/en/post/659655/) на хабре) и не париться о ручном добавлении.
|
||||
RIPE отдает их все в виде JSON вот по этому адресу: https://stat.ripe.net/data/country-resource-list/data.json?resource=ru
|
||||
Утилита jq преобразует из json в список подсетей: ```curl https://stat.ripe.net/data/country-resource-list/data.json?resource=ru | jq -r ".data.resources.ipv4[]"```
|
||||
Правда, почему-то некоторые адреса там в формате ```195.85.234.0-195.85.236.255```, а не подсети, поэтому для них нам необходима еще утилита ipcalc:
|
||||
Утилита ```jq``` преобразует из JSON в список подсетей: ```curl https://stat.ripe.net/data/country-resource-list/data.json?resource=ru | jq -r ".data.resources.ipv4[]"```
|
||||
Правда, почему-то некоторые адреса там в формате ```195.85.234.0-195.85.236.255```, а не в виде подсети. Их там с десяток, можно было бы и забить, но если уж начали делать, давайте сделаем до конца и красиво.
|
||||
|
||||
Нам для них нужна утилита ipcalc:
|
||||
```bash
|
||||
root@trikster-internal:~# ipcalc 195.85.234.0-195.85.236.255 |grep -v "deaggregate"
|
||||
195.85.234.0/23
|
||||
195.85.236.0/24
|
||||
```
|
||||
|
||||
Выделить эти адреса из базового списка можно банально через ```grep '-'``` или ```grep -v '/'```. Но их там немного, и на них, в принципе, можно забить.
|
||||
Выделить эти адреса из базового списка можно банально через ```grep '-'``` или ```grep -v '/'```.
|
||||
|
||||
Скрипт на баше выглядит как-то так (я не удержался и добавил туда еще и прогрессбар):
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
function ProgressBar {
|
||||
let _progress=(${1}*100/${2}*100)/100
|
||||
@ -458,13 +466,14 @@ echo "Routes in routing table: $routes_count"
|
||||
```
|
||||
|
||||
|
||||
Добавим строчки в крон (```export EDITOR=nano; crontab -e```), чтобы он запускался каждую неделю (для того, чтобы обновить список адресов, если они поменялись) и после перезагрузки:
|
||||
Добавим строчки в крон (```EDITOR=nano crontab -e```), чтобы он запускался после перезагрузки, и каждую неделю для того, чтобы обновить список адресов, если они поменялись:
|
||||
```
|
||||
@reboot sleep 30 && bash /root/update_ru_routes.sh > /root/update_routes_log.txt 2>&1
|
||||
0 3 * * mon bash /root/update_ru_routes.sh > /root/update_routes_log.txt 2>&1
|
||||
```
|
||||
|
||||
Если вам принудительно надо маршрутизировать какую-то сеть через **internal**, то можно рядом со скриптом создать файлик ```subnets_user_list.txt```, в который поместить список подсетей, тогда они каждый раз будут добавляться к общему списку при обновлении, в bash-скрипте это есть. Мой, например, выглядит так:
|
||||
Если вам принудительно надо маршрутизировать какую-то сеть через **internal**, то можно рядом со скриптом создать файлик ```subnets_user_list.txt```, в который поместить список подсетей, тогда они каждый раз будут добавляться к общему списку при обновлении, в bash-скрипте это есть.
|
||||
Мой, например, выглядит так:
|
||||
```
|
||||
#avito
|
||||
146.158.48.0/21
|
||||
@ -477,24 +486,25 @@ echo "Routes in routing table: $routes_count"
|
||||
95.161.64.0/20
|
||||
149.154.160.0/21
|
||||
```
|
||||
Первая подсеть — это для мобильного приложения Авито: ее почему-то не было в списке RIPE. Дальше подсети для Телеграма, чтобы хоть немного ускорить загрузку фото и видео.
|
||||
Первая подсеть — для мобильного приложения Авито: ее почему-то не было в списке RIPE. Дальше подсети для Телеграма, чтобы хоть немного ускорить загрузку фото и видео.
|
||||
|
||||
Проверяем:
|
||||
![](ip.png)
|
||||
Оп, и два разных сервиса показывают нам разные адреса, потому что один хостится где-то внутри россии, а другой — снаружи. Работает!
|
||||
Оп, и два разных сервиса показывают нам разные адреса, потому что один сервис хостится где-то внутри россии, а другой — снаружи. Работает!
|
||||
|
||||
Кстати, если у вас **internal** находится в домашней сети, бонусом вы получаете доступ к домашней сети из любого места, где находится устройство со включенным VPN: маршрут 0.0.0.0/0 на устройстве отправляет в VPN весь трафик, а **internal**, замечая трафик в ту подсеть, в которой он находится, отправляет ее в локальный порт, а не в туннель до **external**. Очень удобно: у меня в домашней сети работает сервер с докерами web2rss, ownCloud, navidrome, freshrss, rss-bridge, homeassistant, и мне для получения к ним доступа совершенно не надо заморачиваться с пробросом портов, авторизацией каждого сервера и https.
|
||||
Кстати, если у вас **internal** находится в домашней сети, бонусом вы получаете доступ к домашней сети из любого места, где находится устройство со включенным VPN: маршрут 0.0.0.0/0 на устройстве отправляет в VPN весь трафик, а **internal**, замечая трафик в ту подсеть, в которой он находится, отправляет ее в локальный порт, а не в туннель до **external**. Очень удобно: у меня в домашней сети работает сервер с докерами web2rss, ownCloud, navidrome, freshrss, rss-bridge, homeassistant, и мне для получения к ним доступа совершенно не надо заморачиваться с пробросом портов, авторизацией каждого сервера и https, не говоря уж о том, что некоторые сервисы, типа IoT-устройств, не имеют ни авторизации, ни шифрования в принципе.
|
||||
|
||||
Окончательная схема выглядит так:
|
||||
![](net_scheme_3.png)
|
||||
|
||||
|
||||
## Шаг пятый: настраиваем фаервол
|
||||
Хорошей привычкой и тоном будет закрытие всего ненужного на серверах.
|
||||
Полезной привычкой и хорошим тоном будет закрытие всего ненужного на серверах.
|
||||
|
||||
Для начала на обоих серверах редактируем файл ```/etc/default/ufw```, изменяя значение "**DEFAULT_FORWARD_POLICY**" на **ACCEPT**.
|
||||
|
||||
Теперь выполняем следующие команды на **internal**:
|
||||
```bash
|
||||
```
|
||||
ufw reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
@ -507,7 +517,7 @@ ufw enable
|
||||
Что происходит, думаю, понятно — запретить все, разрешить исходящие, входящие ssh и подключения к WG, а что приходит из туннеля — разрешить.
|
||||
|
||||
На **external** тоже самое, но открывать порт для WG не надо — он подключается сам.
|
||||
```bash
|
||||
```
|
||||
ufw reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
@ -517,10 +527,13 @@ systemctl enable ufw --now
|
||||
ufw enable
|
||||
```
|
||||
|
||||
Еще хорошо бы поставить и настроить fail2ban, или хотя бы перенести ssh на другой порт. В любом случае, отключение парольной авторизации на ssh вообще и переход только на ключи — это базовая операция.
|
||||
Еще хорошо бы поставить и настроить fail2ban, или хотя бы перенести SSH на другой порт. В любом случае, отключение входа по паролю через SSH вообще и переход только на ключевую авторизацию — это базовая операция, независимо ни от чего.
|
||||
|
||||
## Шаг шестой, бонусный и необязательный: кеширующий защищенный DNS over HTTPS
|
||||
Теперь нам нужна еще одна вещь: DNS. Можно, конечно, жить с DNS 1.1.1.1, но надо учитывать, что трафик на него пойдет через **external**, что автоматически означает задержку порядка 100мс при каждом запросе. Можно, конечно, добавить 1.1.1.1/32 в ```subnets_user_list.txt```, и тогда трафик пойдет через локальную ноду и локальный сервер 1.1.1.1, что уменьшит задержку до 10-20мс, но ваши DNS-запросы будет доступны вашему провайдеру, что в случае локальной ноды может быть для кого-то неприемлемо. Несколькими командами можно легко сделать кеширующий DNS, который еще и будет работать с DNS over HTTPS, а значит, провайдеру будет доступен только сам факт использовании DoH, но не сами запросы. Но это, конечно, не обязательно: у меня **internal** находится в домашней сети, и я просто использую DNS микротика, который находится в той же сети и умеет в DoH. Но если у вас **internal** сервер это VPS, то можно сделать там и DNS сервер. Использовать будем ```cloudflared``` и ```dnsmasq```.
|
||||
Теперь нам нужна еще одна вещь: DNS.
|
||||
Можно, конечно, жить с DNS 1.1.1.1, но надо учитывать, что трафик на него пойдет через **external**, что автоматически означает задержку порядка 100мс при каждом запросе. Можно, конечно, добавить 1.1.1.1/32 в ```subnets_user_list.txt```, и тогда трафик пойдет через локальную ноду и локальный сервер 1.1.1.1, что уменьшит задержку до 10-20мс, но ваши DNS-запросы будет доступны вашему провайдеру, что в случае локальной ноды может быть для кого-то неприемлемо.
|
||||
|
||||
Несколькими командами можно легко сделать кеширующий DNS, который еще и будет работать с DNS over HTTPS, а значит, провайдеру будет доступен только сам факт использовании DoH, но не сами запросы. Но это, конечно, не обязательно: у меня **internal** находится в домашней сети, и я просто использую DNS микротика, который находится в той же сети и умеет в DoH. Но если у вас **internal** сервер это VPS, то можно сделать там и DNS сервер. Использовать будем ```cloudflared``` и ```dnsmasq```.
|
||||
|
||||
Добавляем репозитарий:
|
||||
```bash
|
||||
@ -611,6 +624,7 @@ P.S. особые параноики могут запустить cloudflared
|
||||
|
||||
## Заключение
|
||||
Пары ключей в статье — действующие, так что вы можете, ничего не исправляя в конфигах (только IP **internal**-сервера), залить их на свои сервера и клиент и поиграться. Но для боевого применения ключи надо перегенерить, конечно.
|
||||
|
||||
Благо, сделать это просто, даже если вам лень разбираться с ручной генерацией ключей — я сделал небольшой bash-скрипт для этого. Достаточно сделать так на **internal**-сервере:
|
||||
|
||||
```bash
|
||||
|
Loading…
Reference in New Issue
Block a user