From c1145b2fc521dc1c8f36f6bc1756a89fd4f3e976 Mon Sep 17 00:00:00 2001 From: vvzvlad Date: Sat, 27 Aug 2022 18:06:40 +0300 Subject: [PATCH] typos and style fixs --- article/article_draft.md | 62 ++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/article/article_draft.md b/article/article_draft.md index ddd070f..c8010d0 100644 --- a/article/article_draft.md +++ b/article/article_draft.md @@ -43,7 +43,7 @@ apt update && apt install -y wireguard iptables ipcalc qrencode curl jq traceroute dnsutils ufw ``` -Включаем перенаправление трафика: в этом случае сервер, получив пакет, который предназначается ни одному из его адресов, не отбросит его, а попытается перенаправить в соответствии со своими маршрутами. +Включаем перенаправление трафика: в этом случае сервер, получив пакет, который предназначается ни одному из его IP-адресов, не отбросит его, а попытается перенаправить в соответствии со своими маршрутами. ```bash echo "net.ipv4.ip_forward=1" > > /etc/sysctl.conf echo "net.ipv4.conf.all.forwarding=1" > > /etc/sysctl.conf @@ -97,9 +97,9 @@ PostDown = iptables -t nat -D POSTROUTING -o `ip route | awk '/default/ {print $ Управляются интерфейсы обычно при помощи утилиты **wg-quick**: ```wg-quick down wg-external``` и ```wg-quick up wg-external``` -Утилита **wg-quick** — это, на самом деле, 400 строк на баше, которые автоматизируют частоиспользуемые вещи: например, установку маршрутов. Сам факт туннеля не делает ничего, кроме создания "трубы" за которой находится другой пир. Для того, чтобы ваш запрос в браузере попал в интерфейс, системе надо явно сказать "маршрутизируй, пожалуйста, пакеты с таким-то адресом назначения вот в этот сетевой интерфейс". +Утилита **wg-quick** — это, на самом деле, 400 строк на баше, которые автоматизируют частоиспользуемые вещи: например, установку маршрутов. Сам факт наличия туннеля не делает ничего, кроме создания защищенной "трубы", за которой находится другой пир. Для того, чтобы ваш запрос в браузере попал в интерфейс, системе надо явно сказать "маршрутизируй, пожалуйста, пакеты с таким-то адресом назначения вот в этот сетевой интерфейс". -Именно этим занимается **wg-quick**. Ну еще и настройкой DNS, указанных в конфиге и установкой MTU. Но ничего сложного в этом нет, достаточно сделать "```cat /usr/bin/wg-quick```", чтобы посмотреть на эту логику, и если надо, сделать тоже самое руками. +Именно этим занимается **wg-quick**. Ну еще и настройкой DNS, указанных в конфиге, установкой MTU, и еще парой вещей. Но ничего сложного в этом нет, достаточно сделать "```cat /usr/bin/wg-quick```", чтобы посмотреть на эту логику и, если надо, сделать то же самое руками. **Interface-Address** — это IP текущего пира. Вся адресация в WG статическая. С одной стороны, это упрощает настройку и бутстрап, с другой стороны, усложняет работу, если у вас очень много клиентов. @@ -107,10 +107,10 @@ PostDown = iptables -t nat -D POSTROUTING -o `ip route | awk '/default/ {print $ **Interface-PostUp** и **PostDown** — это скрипты, выполняющиеся после поднятия и после остановки интерфейса. Есть еще **PreUP** и **PreDown**. Кроме публичных и приватных ключей есть еще опция **PresharedKey**, которая обеспечивает дополнительное шифрование симметричным шифром. Ключ генерируется командой ```wg genpsk``` и кладется в **PresharedKey** в секциях **Peer** на обоих пирах. Неиспользование этой опции не снижает нагрузку по шифровке-расшифровке: если ключ не указан, используется нулевое значение ключа. -Для действительного обеспечения пост-квантовой безопасности (невозможности расшифровки данных квантовыми компьютерами) разработчики рекомендуют дополнительный внешний квантово-устойчивый механизм хендшейка (например, Microsoft SIDH, который они пиарят именно в таком контексте), чей найденный общий ключ можно использовать в качестве **PresharedKey**. +Для действительного обеспечения пост-квантовой безопасности — невозможности расшифровки данных квантовыми компьютерами, разработчики рекомендуют дополнительный внешний квантово-устойчивый механизм хендшейка (например, SIDH, который Microsoft пиарит именно в таком контексте), созданный которым общий ключ можно использовать в качестве **PresharedKey**. Заклинания в PostUp достаточно просты. ````ip route | awk '/default/ {print $5; exit}'```` — это команда для подстановки имени сетевого интерфейса, куда по-умолчанию выполняется маршрутизация: как правило, это тот интерфейс, в который воткнут провайдер или роутер. -Таким образом страшная команда превращается просто в ```iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE```, которая представляет собой включение NAT в режиме маскарада: сервер будет отправлять пришедшие ему пакеты пакеты во внешнюю сеть, подменяя в них адрес отправителя на свой, чтобы ответы на эти пакеты тоже приходили ему, а не исходному отправителю. +Таким образом, страшная команда превращается просто в ```iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE```, которая представляет собой включение NAT в режиме маскарада: сервер будет отправлять пришедшие ему пакеты во внешнюю сеть, подменяя в них адрес отправителя на свой, чтобы ответы на эти пакеты тоже приходили ему, а не исходному отправителю. Вторая команда уже немного сложнее, но она подставляет IP-адрес дефолтного маршрута. ```bash @@ -128,18 +128,18 @@ root@:~# ip route | awk '/default/ { print $5 }' inet 192.168.88.70/24 brd 192.168.88.255 scope global dynamic enp1s0 ``` И дальше вытаскиваем оттуда адрес, в данном случае 192.168.88.70. -И команда превращается в ```ip rule add from 95.93.219.123 table main``` — это необходимо для сервера **internal**, потому что иначе при активации маршрута 0.0.0.0/0 он начинает пересылать ответы на пакеты, приходящие ему на внешние адреса через туннель WG. Сервер на том конце, конечно, пересылает их по назначению, но тут уже не готов отправитель пакета: он присылает что-то на внешний адрес сервера **internal**, а ответ ему приходит с **external**. Естественно, при включенном rp_filter пакет отбрасывается. В этом случае сервер перестает быть доступен, например, по SSH снаружи, к нему надо коннектиться только по внутреннему IP wireguard-а. Отключать rp_filter это из пушки по воробьям, а вот дополнительное правило исправляет ситуацию. +И команда превращается в ```ip rule add from 95.93.219.123 table main``` — это необходимо для сервера **internal**, потому что иначе при активации маршрута 0.0.0.0/0 он начинает пересылать ответы на пакеты, приходящие ему на внешние адреса через туннель WG. Сервер на том конце, конечно, пересылает их по назначению, но тут уже не готов отправитель пакета: он присылает что-то на внешний адрес сервера **internal**, а ответ ему приходит с **external**. Естественно, при включенном rp_filter пакет отбрасывается. В этом случае сервер перестает быть доступен, например, по SSH снаружи, к нему надо коннектиться только по внутреннему IP wireguard-а. Отключать rp_filter — это из пушки по воробьям, а вот дополнительное правило исправляет ситуацию. -Продолжим писать конфиг: в него надо добавить секцию **Peer**, чтобы связать их с друг-другом. +Продолжим писать конфиг: в оба конфига надо добавить секцию **Peer**, чтобы связать сервера друг с другом. Я намеренно не привожу сразу готовые конфиги, потому что хочу показать механизм создания конфигов в ручном режиме — в свое время у меня были проблемы с тем, что я генерировал конфиги утилитами типа ```easy-wg-quick``` или веб-интерфейсами, которые спрашивают тебя о названии клиента и красиво показывают QR-код, но отнюдь не способствуют пониманию того, как работает WG на самом деле. -Итак, добавляем в каждый по секции **Peer**, для чего генерируем из приватного ключа публичный (вот в pubkey как раз происходит крипто-магия): +Итак, добавляем в каждый конфиг по секции **Peer**, для чего генерируем из приватного ключа публичный (вот в ```pubkey``` как раз происходит крипто-магия): ```bash echo "kOd3FVBggwpjD3AlZKXUxNTzJT0+f3MJdUdR8n6ZBn8=" | wg pubkey MxnOnIlKfSyZyRutnYyoWHb3Izjalgf1t8F1oPJiyyw= ``` -Это публичный ключ сервера internal, его мы помещаем в секцию **peer** на **external**: +Это публичный ключ сервера **internal**, его мы помещаем в секцию **Peer** на **external**: ```/etc/wireguard/wg-external.conf``` ```ini [Peer] @@ -150,19 +150,24 @@ PersistentKeepalive=25 ``` Там же, в **Endpoint** указываем адрес сервера **internal** и порт, который мы задали в **ListenPort**. -С **AllowedIPs** при использовании ```wg-quick``` возникает небольшая путаница: это изначально именно, то как оно называется — список разрешенных IP-адресов к приему из туннеля: если что-то прилетает с другим src, оно будет отброшено. Но при использовании ```wg-quick``` она разумно считает, что если там есть какие-то устройства, которые могут послать пакет, то значит пакеты к этим устройствам надо маршрутизировать туда же, и создает маршруты на эти адреса, указывающие на туннель пира. В данных примерах **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=10.20.30.0/24``` — означает что трафик с **external** в подсеть 10.20.30.0-10.20.30.255 будет уходить в туннель к **internal**. В принципе, нужды в этом особо нет, **external** у нас исключительно выходная нода. Но вдруг мы как-нибудь захотим зайти оттуда по ssh на какую-нибудь другую машину. +С **AllowedIPs** при использовании ```wg-quick``` возникает небольшая путаница. +Это изначально именно, то как оно называется — список разрешенных IP-адресов к приему из туннеля: если что-то прилетает с другим src, оно будет отброшено. +Но при использовании ```wg-quick``` она разумно считает, что если там есть какие-то устройства, которые могут послать пакет, значит, пакеты к этим устройствам надо маршрутизировать туда же, и создает маршруты на эти адреса, указывающие на туннель пира. + +В данных примерах **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=10.20.30.0/24``` означает что трафик с **external** в подсеть 10.20.30.0-10.20.30.255 будет уходить в туннель к **internal**. В принципе, нужды в этом особо нет, **external** у нас исключительно выходная нода. Но вдруг мы как-нибудь захотим зайти оттуда по ssh на какую-нибудь другую машину. Повторяем генерацию публичного ключа с **external**: ```bash root@:~# echo "6CCRP42JiTObyf64Vo0BcqsX6vptsqOU+MKUslUun28=" | wg pubkey FulnUTovyyfgn5kmgPkcj2OjKRFGeLkaTsHtAOy6HW8= ``` -Мы получаем публичный ключ сервера **external** и помещаем его в секцию peer сервера **internal**: +Мы получаем публичный ключ сервера **external** и помещаем его в секцию **Peer** сервера **internal**: ```/etc/wireguard/wg-internal.conf``` ```ini [Peer] #external node @@ -170,7 +175,8 @@ PublicKey = FulnUTovyyfgn5kmgPkcj2OjKRFGeLkaTsHtAOy6HW8= AllowedIPs = 10.20.30.2/32, 0.0.0.0/0 ``` -**AllowedIPs** тут ```10.20.30.2/32, 0.0.0.0/0``` — указываем, что за туннелем находится конкретный IP 10.20.30.2 и помимо этого, пробрасываем весь трафик, не связанный другими маршрутами, в этот туннель: **external** у нас это основная выходная нода нашего VPN, так что по умолчанию весь трафик будет направляться через нее, т.к. зарубежных маршрутов больше, чем российских, и логичнее фильтровать именно российские, а зарубежный трафик пустить по умолчанию через ноду в другой стране. +**AllowedIPs** тут ```10.20.30.2/32, 0.0.0.0/0```. Этим мы указываем, что за туннелем находится конкретный IP 10.20.30.2, и, помимо этого, пробрасываем весь трафик, не связанный другими маршрутами, в этот туннель: **external** у нас основная выходная нода нашего VPN. +Поэтому по умолчанию весь трафик будет направляться через нее, так как зарубежных маршрутов больше, чем российских, и логичнее фильтровать именно российские, а зарубежный трафик пустить по умолчанию через ноду в другой стране. Итак, два конфига: `/etc/wireguard/wg-internal.conf` @@ -280,7 +286,7 @@ HOST: trikster-internal.local Loss% Snt Last Avg Best Wrst StDev ## Шаг третий: добавляем конфиг клиента -Создаем конфиг клиента, конечного устройства-пользователя VPN. За основу берем ```wg-external.conf```, потому что он такой же точно клиент, который подключается к **internal**, разница только в том, что **external** получает пакеты, а наш клиент будет отправлять. +Создаем конфиг клиента, конечного устройства-пользователя VPN. За основу берем ```wg-external.conf```, потому что это точно такой же клиент, который подключается к **internal**: разница только в том, что **external** будет получать пакеты, а наш клиент — отправлять. Генерируем ему сразу пару публичный-приватный ключ: ```bash @@ -307,10 +313,10 @@ PersistentKeepalive = 25 ``` Тут у нас добавилась опция **PersistentKeepalive**. Дело в том, что роутеры в цепочке между двумя пирами ничего не знают о сессии WG, а знают только о потоке UDP-пакетов. Для маршрутизации UDP-пакетов за NAT они создают у себя табличку, в которой записывают, кто куда и на какой порт отправил пакет. И если с destination-адреса/порта приходит UPD-пакет, то они определяют, куда его отправить по это таблице, делая вывод, что если сервер B недавно отправил пакет серверу А, то ответ от сервера А на этот же адрес и порт скорее всего надо переслать серверу B. -А в отличии от TCP в UDP нет никаких договоренностей о поддержании сессии, т.к. нет и самого понятия сессии. WG же построен таким образом, что при отсутствии трафика, попадающего в туннель, не будет и трафика между пирами, только хедшейки раз в две минуты. Опция **PersistentKeepalive** заставляет его посылать пустые пакеты каждые 25 секунд, предотвращая потерю маршрута на промежуточных роутерах, потому что иначе возможна ситуация, когда мы будем раз за разом отправить пакеты, а до второго пира они доходить не будут, а он об этом и не будет знать. +А в UDP, в отличии от TCP, нет никаких договоренностей о поддержании сессии, т.к. нет и самого понятия сессии. WG же построен таким образом, что при отсутствии трафика, попадающего в туннель, не будет и трафика между пирами, только хедшейки раз в две минуты. Опция **PersistentKeepalive** заставляет его посылать пустые пакеты каждые 25 секунд, предотвращая потерю маршрута на промежуточных роутерах, иначе возможна ситуация, когда мы будем раз за разом отправлять пакеты, а до второго пира они доходить не будут, а он об этом и не будет знать. -Дальше мы для нашего клиента добавляем еще одну секцию peer в конфиг на **internal**: +Дальше мы добавляем еще одну секцию **Peer** в конфиг на **internal** — для клиента: ```ini #notebook-client node [Peer] @@ -318,11 +324,11 @@ PublicKey = 26Vhud00ag/bdB9molvSxfBzZTlzdO+aZgrX3ZDncSg= AllowedIPs = 10.20.30.3/32 ``` -Перезапускаем туннель на **internal** (```wg-quick down/up```), подключаемся.. Оп, хендшейк есть, данные пошли. +Перезапускаем туннель на **internal** (```wg-quick down/up```), подключаемся… Оп, хендшейк есть, данные пошли. Открываем какой-нибудь https://www.reg.ru/web-tools/myip, видим IP **external** ноды, и другую страну. -Таким же образом создаем конфиги для других клиентов. Если это мобильные устройства, то удобнее показать им QR. Он делается следующим образом: создаем в текущей папке конфиг как обычно, конечно, с новыми ключами и другим IP, какой-нибудь ```wg-moblie-client.conf``` и дальше командой ```qrencode -t ansiutf8 < wg-moblie-client.conf``` показываем прям в консоли QR, который сканируем с телефона. -Это удобнее копирования файлов, но вам так же никто не мешает скинуть ```wg-moblie-client.conf``` на телефон или вообще ввести значения 7 полей вручную. +Таким же образом создаем конфиги для других клиентов. Если это мобильные устройства, то удобнее показать им QR. Он делается следующим образом: создаем в текущей папке конфиг как обычно, конечно, с новыми ключами и другим IP, какой-нибудь ```wg-moblie-client.conf```, и дальше командой ```qrencode -t ansiutf8 < wg-moblie-client.conf``` показываем прям в консоли QR, который сканируем с телефона. +Это удобнее копирования файлов, но вам также никто не мешает скинуть ```wg-moblie-client.conf``` на телефон или вообще ввести значения 7 полей вручную. Теперь наша схема выглядит следующим образом: ![](net_scheme_2.png) @@ -334,7 +340,7 @@ AllowedIPs = 10.20.30.3/32 Давайте доделаем. ## Шаг четвертый: добавляем регион-зависимую маршрутизацию. -Как мы помним, мы отправляем все данные с клиента на **internal**, а тот все данные отправляет на **external**, а тот уже своему провайдеру. Так же, мы помним, что у нас на **internal** "слабый" маршрут 0.0.0.0/0, который перебивается любыми другими маршрутами, а сам **internal** находится в российском сегменте. Значит, все, что нам надо — это как-то перехватить запросы на российские IP на уровне **internal** и перенаправить их не в туннель WG до **external**, а напрямую в сетевой порт самого сервера, в тот, через который он получает доступ в православный, российский интернет со скрепами и девицами в кокошниках. +Как мы помним, мы отправляем все данные с клиента на **internal**, тот все данные отправляет на **external**, а тот уже своему провайдеру. Также мы помним, что у нас на **internal** "слабый" маршрут 0.0.0.0/0, который перебивается любыми другими маршрутами, а сам **internal** находится в российском сегменте. Значит, все, что нам надо — это как-то перехватить запросы на российские IP на уровне **internal** и перенаправить их не в туннель WG до **external**, а напрямую в сетевой порт самого сервера, в тот, через который он получает доступ в православный, российский интернет со скрепами и девицами в кокошниках. Давайте проверим предположение. На клиенте получим IP того же сбермаркета (```nslookup sbermarket.ru```), и посмотрим, как туда идет трафик (```traceroute 212.193.158.175```): ```bash @@ -385,7 +391,7 @@ 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 какой-то другой сервер, а не тот, в адрес которого ресолвится имя домена. +Сбермаркет, правда, все еще не открываемся: видимо, проверяет на наличие VPN какой-то другой сервер, а не тот, в адрес которого резолвится имя домена. Можно сходить на https://asnlookup.com/, вбить туда адрес, и получить принадлежность адреса к AS и заодно список подсетей этой autonomous system (AS34879, OOO Sovremennye setevye tekhnologii). С большой вероятностью для более-менее крупных компаний это и будет их сетевая инфраструктура (ну или по крайней мере, инфраструктура, относящаяся к конкретному сайту), прописав для которой маршруты, вы обеспечите доступ на нужный вам сайт/сервис. Для мелких сайтов вы скорее всего получите AS хостера или дата-центра, но, во-первых, это тоже сработает, а во-вторых, мелкие сайты обычно и не закрывают иностранные диапазоны, потому что не испытывают проблем с DDOSом из-за границы. Но можно сделать проще: засунуть в маршруты все адреса российского сегмента (спасибо [статье](https://habr.com/en/post/659655/) на хабре) и не париться о ручном добавлении. @@ -458,7 +464,7 @@ echo "Routes in routing table: $routes_count" 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 @@ -471,13 +477,13 @@ echo "Routes in routing table: $routes_count" 95.161.64.0/20 149.154.160.0/21 ``` -Первая подсеть — это что-то для приложения авито, которой почему-то не было в RIPE. Дальше подсети для TG, чтобы хоть немного ускорить загрузку фото и видео. +Первая подсеть — это для мобильного приложения Авито: ее почему-то не было в списке 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. Окончательная схема выглядит так: ![](net_scheme_3.png)