За­бав­ная ситу­ация: сай­тов и сер­висов, дос­тупных толь­ко через VPN, все боль­ше, но при этом мно­гие рос­сий­ские ком­пании зак­рыва­ют дос­туп из‑за гра­ницы. В резуль­тате при­ходит­ся целыми дня­ми теребить пол­зунки «вкл‑выкл», что уто­митель­но. Я рас­ска­жу, как с помощью магии мар­шру­тов и WireGuard, решить эту проб­лему и сде­лать «умный» VPN, который не надо отклю­чать.

Ес­ли ты поль­зуешь­ся VPN, то и сам навер­няка стал­кива­ешь­ся с бло­киров­ками зарубеж­ного тра­фика. К при­меру, могут не откры­вать­ся pochta.ru, leroymerlin.ru, rt.ru, avito.ru.

По­луча­ется мем.

Каж­дый с этим борет­ся как может. Нап­ример, на устрой­ствах Apple род­ными средс­тва­ми мож­но нас­тро­ить авто­мати­зацию, которая будет запус­кать VPN, ког­да откры­ваешь опре­делен­ные при­ложе­ния (нап­ример, Twitter), а ког­да выходишь из них — вык­лючать обратно. Но это кос­тыль, а хочет­ся все сде­лать кра­сиво, да еще и про­качать навык работы с сетью.

По­это­му мы сей­час поп­робу­ем «вклю­чать чуть‑чуть VPN».

info

При таком исполь­зовании это ско­рее прок­си, а не VPN, но любые методы обхо­да бло­киро­вок теперь называ­ют VPN, так что, сде­лав эту ого­вор­ку, даль­ше рас­сла­бим­ся и будем делать как все.

За­одно чуть улуч­шим качес­тво свя­зи с локаль­ными ресур­сами: необ­ходимость тас­кать тра­фик сна­чала до VPN вне стра­ны, а потом обратно до сер­вера внут­ри нее дра­матич­но ска­зыва­ется если не на ско­рос­ти, то на задер­жке точ­но. Даже на про­вод­ном интерне­те пинг в 4 мс до Яндекса лег­ко прев­раща­ется в 190 мс, а на мобиль­ном интерне­те — из 80 мс в 240 мс. Допол­нитель­ный хоп внут­ри стра­ны чуть ухуд­шит дело, но далеко не так дра­матич­но.

Де­лать все мы будем на осно­ве WireGuard — это отно­ситель­но новая (раз­рабаты­вает­ся с 2016 года, в отли­чие от OpenVPN и IPsec: пер­вый — это двух­тысяч­ные, а вто­рой еще рань­ше) тех­нология VPN. Соз­дал ее, по сути, один человек — zx2c4, которо­го в миру зовут Джей­соном Донен­фель­дом. Плю­сы WG — ско­рость (осо­бен­но для Linux, где он может работать как модуль ядра, начиная с Kernel 5.6, и Windows, где модуль для ядра выпус­тили око­ло недели назад), низ­кие задер­жки, сов­ремен­ная крип­тогра­фия и прос­тая нас­трой­ка и исполь­зование конеч­ным юзе­ром.

Ах да, еще UDP. UDP для тун­нелей — это хорошо, потому что у TCP уже есть механиз­мы, которые поз­воля­ют ему работать на неидеаль­ных соеди­нени­ях, а UDP пред­став­ляет собой имен­но такое соеди­нение. А ког­да ты засовы­ваешь TCP в TCP, то отка­зыва­ешь­ся от боль­шей час­ти этих механиз­мов (инкапсу­лиро­ван­ный TCP-пакет будет гаран­тирован­но дос­тавлен дру­гой сто­роне, хотя про­токол допус­кает недос­тавку), но все еще несешь весь овер­хед вида «хен­дшейк соеди­нения для отправ­ки хен­дшей­ка».

Не говоря уж о том, что инкапсу­лиро­вать UDP в TCP — ничуть не луч­шая идея, потому что сра­зу рушит все пред­положе­ния вся­ких скай­пов о том, что луч­ше про­пус­тить пару пакетов, чем умень­шить задер­жку: каж­дый UDP-пакет в этом слу­чае будет при­нуди­тель­но пос­лан заново и дос­тавлен кор­рек­тно, не счи­таясь с зат­ратами вре­мени.

Осо­бен­но для оди­ноко­го поль­зовате­ля‑хакера при­ятна работа с шиф­ровани­ем: нет необ­ходимос­ти ни в сер­тифика­тах и удос­товеря­ющих цен­трах, ни в логинах‑паролях, все, что нуж­но, — обме­нять­ся пуб­личны­ми клю­чами с пиром, с которым ты хочешь уста­новить соеди­нение. Для боль­ших ком­паний это, конеч­но, будет ско­рее минусом, как и то, что WG — это толь­ко базовая часть пол­ноцен­ной боль­шой инфраструк­туры VPN. Но имен­но WireGuard исполь­зовали, к при­меру, в Cloudflare для сво­его WARP, прав­да, написав его собс­твен­ную реали­зацию — boringtun.

Еще один минус WG — то, что тра­фик не обфусци­рован и глу­бокая инспек­ция пакетов (DPI) выявит и поз­волит заб­локиро­вать такое соеди­нение (не говоря уж о бло­киров­ке UDP сов­сем, что поч­ти не меша­ет работать с вебом, но гаран­тирован­но лома­ет WireGuard). Для скры­тия тра­фика рекомен­дует­ся исполь­зовать спе­циали­зиро­ван­ное ПО — Cloak, Obfsproxy, Shadowsocks, Stunnel, SoftEther, SSTP или, в кон­це кон­цов, прос­той SSH. Часть из этих инс­тру­мен­тов может работать сов­мес­тно с WG, а часть спо­соб­на его заменять в качес­тве инс­тру­мен­та сте­ганог­рафии: WG изна­чаль­но соз­давал­ся под ско­рость и крип­тогра­фичес­кую защищен­ность.

Ес­ли очень упро­щать, клю­чи работа­ют сле­дующим обра­зом: у нас есть зак­рытый (при­ват­ный) ключ, из которо­го мож­но сге­нери­ровать откры­тый, или пуб­личный. Наобо­рот, из откры­того клю­ча получить зак­рытый мы никак не можем. Затем мы шиф­руем с помощью зак­рытого клю­ча какую‑то стро­ку, а при помощи откры­того рас­шифру­ем ее и тем самым убе­дим­ся, что у собесед­ника точ­но есть зак­рытый ключ, а зна­чит, он тот, за кого себя выда­ет. Таким обра­зом, мы можем без проб­лем переда­вать откры­тый ключ — он все­го лишь поз­воля­ет про­верить под­линность авто­ра, но не прит­ворить­ся им.

Это как в SSH — пуб­личный ключ лежит на сер­вере, где его потеря — неболь­шая беда: все, что смо­жет сде­лать с ним зло­умыш­ленник, — это положить его на свой сер­вер, что­бы ты мог под­клю­чить­ся к нему с помощью зак­рытого клю­ча.

Так вот, в WG на пер­вом эта­пе под­клю­чения каж­дая сто­рона с помощью зашиф­рован­ного при­ват­ным клю­чом сооб­щения доказы­вает собесед­нику, что она имен­но она: это про­веря­ется пуб­личным клю­чом.

Вто­рой этап — соз­дание с помощью этих клю­чей и матана сим­метрич­ных клю­чей для шиф­рования самого тра­фика. Бла­года­ря тому что рас­шифро­вать зашиф­рован­ное пуб­личным клю­чом нель­зя без при­ват­ного, мы смо­жем соз­дать ключ для сим­метрич­ного шиф­рования и отпра­вить его по защищен­ному каналу. Этот шаг необ­ходим потому, что сим­метрич­ное шиф­рование гораз­до менее ресур­соем­кая опе­рация, а минус у нее толь­ко один: необ­ходимо син­хро­низи­ровать ключ меж­ду сто­рона­ми, при том что перех­ват клю­ча треть­ей сто­роной ведет к воз­можнос­ти рас­шифров­ки тра­фика.

Но эта проб­лема реша­ется с помощью асим­метрич­ной схе­мы. Это называ­ется про­токол Диф­фи — Хел­лма­на — спо­соб защищен­ного получе­ния обще­го сек­ретно­го клю­ча. В WG исполь­зует­ся ECDH — вари­ация Диф­фи — Хел­лма­на на эллипти­чес­ких кри­вых. Пер­вые два эта­па в тер­минах WG называ­ются рукопо­жати­ем или хен­дшей­ком.
За­тем сим­метрич­ные клю­чи исполь­зуют­ся для шиф­рования тра­фика. Раз в две минуты про­исхо­дит новое рукопо­жатие и сес­сион­ные клю­чи меня­ются.

Ра­зуме­ется, в реаль­нос­ти все нем­ного слож­нее: нап­ример, отправ­ляют­ся не сами клю­чи, а сге­нери­рован­ные на их осно­ве эфе­мер­ные клю­чи, которые уда­ляют­ся сра­зу пос­ле опе­рации. Заин­тересо­вав­шихся под­робнос­тями отправ­ляю к крат­кому опи­санию на сай­те WireGuard.

Мы же перей­дем к более прак­тичес­ким дей­стви­ям.

 

Шаг 1. Создаем и настраиваем два сервера

Один сер­вер будет внут­ри стра­ны — через него тра­фик пой­дет на локаль­ные ресур­сы, а вто­рой — за гра­ницей. Даль­ше я их буду называть local и external.

Иде­аль­но, если local будет в тво­ей домаш­ней сети, потому что при этом тра­фик на внеш­ние ресур­сы не отли­чает­ся от тво­его домаш­него тра­фика. Но для это­го нужен какой‑то хост дома, белый IP и воз­можность проб­росить порт. У меня это вир­туал­ка на домаш­нем сер­вере, но, навер­ное, подой­дет и Raspberry Pi или ана­логич­ный одноплат­ник.

info

Ва­риант с одноплат­ником я не тес­тировал и не уве­рен, что он сра­бота­ет. Raspberry Pi при­дет­ся мар­шру­тизи­ровать весь тра­фик с устрой­ств и дер­жать в памяти око­ло 11 тысяч мар­шру­тов; ресур­сов на это может не хва­тить.

Ес­ли дома хос­та нет, мож­но взять любой сер­вер у хос­тера VDS. Увы, некото­рые сай­ты бло­киру­ют под­сети хос­теров, опа­саясь ботов, так что это менее пред­почти­тель­ный вари­ант.

А вот внеш­нюю машину мож­но арен­довать и у хос­тера. К при­меру, у RU VDS и VDSina есть зарубеж­ные пло­щад­ки. А мож­но выб­рать инос­тран­ного хос­тера, если най­дешь спо­соб опла­чивать его услу­ги. Нап­ример, я исполь­зую исланд­ский 1984.hosting, соз­данный спе­циаль­но для парано­иков.

Счи­таем, что на обо­их сер­верах у нас Debian 11.

Ста­вим нуж­ные нам пакеты:

apt update && apt install -y wireguard iptables ipcalc qrencode curl jq traceroute dnsutils ufw

Вклю­чаем перенап­равле­ние тра­фика: в этом слу­чае сер­вер, получив пакет, который не пред­назна­чает­ся ни одно­му из его IP-адре­сов, не отбро­сит его, а попыта­ется перенап­равить в соот­ветс­твии со сво­ими мар­шру­тами.

echo "net.ipv4.ip_forward=1" > > /etc/sysctl.conf
echo "net.ipv4.conf.all.forwarding=1" > > /etc/sysctl.conf
sysctl -p /etc/sysctl.conf

Оп­циональ­но (но очень удоб­но) сра­зу поменять hostname обо­их сер­веров, что­бы не запутать­ся, где какая кон­соль:

hostnamectl set-hostname trickster-internal
hostnamectl set-hostname trickster-external
 

Шаг 2. Настраиваем WireGuard для связи двух серверов

Для начала генери­руем клю­чи. Запус­каем два раза wg genkey и получа­ем два при­ват­ных клю­ча:

root@trikster-internal:~# wg genkey
kOd3FVBggwpjD3AlZKXUxNTzJT0+f3MJdUdR8n6ZBn8=
root@trikster-internal:~# wg genkey
6CCRP42JiTObyf64Vo0BcqsX6vptsqOU+MKUslUun28=

Ути­лита wg genkey не дела­ет ничего вол­шебно­го, это прос­то ана­лог чего‑то в таком духе:

echo $RANDOM | md5sum | head -c 32 | base64

Толь­ко навер­няка более слу­чай­ное: мы прос­то генери­руем 32 бай­та слу­чай­ных зна­чений и пред­став­ляем их в виде Base64.

Соз­даем два кон­фига. Один на internal:

/etc/wireguard/wg-internal.conf
[Interface]
Address = 10.20.30.1/32
ListenPort = 17968
PrivateKey = kOd3FVBggwpjD3AlZKXUxNTzJT0+f3MJdUdR8n6ZBn8=
PostUp = iptables -t nat -A POSTROUTING -o `ip route | awk '/default/ {print $5; exit}'` -j MASQUERADE
PostUp = ip rule add from `ip addr show $(ip route | awk '/default/ { print $5 }') | grep "inet" | grep -v "inet6" | head -n 1 | awk '/inet/ {print $2}' | awk -F/ '{print $1}'` table main
PostDown = iptables -t nat -D POSTROUTING -o `ip route | awk '/default/ {print $5; exit}'` -j MASQUERADE
PostDown = ip rule del from `ip addr show $(ip route | awk '/default/ { print $5 }') | grep "inet" | grep -v "inet6" | head -n 1 | awk '/inet/ {print $2}' | awk -F/ '{print $1}'` table main

Вто­рой на external:

/etc/wireguard/wg-external.conf
[Interface]
Address=10.20.30.2/32
PrivateKey=6CCRP42JiTObyf64Vo0BcqsX6vptsqOU+MKUslUun28=
PostUp = iptables -t nat -A POSTROUTING -o `ip route | awk '/default/ {print $5; exit}'` -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o `ip route | awk '/default/ {print $5; exit}'` -j MASQUERADE

Сек­ция [Interface] — это нас­трой­ки кон­крет­ного сетево­го интерфей­са WireGuard, того, что будет виден в ip a. Наз­вание интерфей­са берет­ся из наз­вания текуще­го фай­ла кон­фигура­ции. У одно­го интерфей­са всег­да одна клю­чевая пара: у пиров это­го интерфей­са оди­нако­вый пуб­личный ключ.

Но ник­то не меша­ет, если хочет­ся, сде­лать для каж­дого пира отдель­ный кон­фиг и отдель­ный интерфейс (прав­да, на сот­нях кли­ентов это будет неудоб­но).

Уп­равля­ются интерфей­сы обыч­но при помощи ути­литы wg-quick:
wg-quick down wg-external и wg-quick up wg-external

info

Ути­лита wg-quick — это на самом деле 400 строк на баше, которые авто­мати­зиру­ют час­то исполь­зуемые вещи, нап­ример уста­нов­ку мар­шру­тов. Наличие тун­неля само по себе не дает ничего, кро­ме защищен­ной «тру­бы», за которой находит­ся дру­гой пир. Что­бы твой зап­рос в бра­узе­ре попал в интерфейс, сис­теме надо явно ска­зать: «Мар­шру­тизи­руй, пожалуй­ста, пакеты с таким‑то адре­сом наз­начения вот в этот сетевой интерфейс».

Имен­но этим занима­ется wg-quick. Ну еще и нас­трой­кой адре­сов DNS, ука­зан­ных в кон­фиге, уста­нов­кой MTU и еще парой вещей. Но ничего слож­ного в этом нет, дос­таточ­но сде­лать cat /usr/bin/wg-quick, что­бы пос­мотреть на эту логику, и, если надо, пов­торить то же самое руками.

  • Interface-Address — это IP текуще­го пира. Вся адре­сация в WG ста­тичес­кая. С одной сто­роны, это упро­щает нас­трой­ку и бутс­трап, с дру­гой сто­роны, усложня­ет работу, если у тебя очень мно­го кли­ентов.
  • ListenPort — это UDP-порт для под­клю­чения извне. Если не ука­зать, будет прос­лушивать 51820.
  • Interface-PostUp и Interface-PostDown — скрип­ты, которые выпол­няют­ся пос­ле под­нятия и пос­ле оста­нов­ки интерфей­са. Есть еще PreUP и PreDown.

Кро­ме пуб­личных и при­ват­ных клю­чей, есть еще опция PresharedKey, которая обес­печива­ет допол­нитель­ное шиф­рование сим­метрич­ным шиф­ром. Ключ мож­но сге­нери­ровать, нап­ример, коман­дой wg genpsk и добавить в опцию PresharedKey в сек­циях Peer на обо­их пирах. Если не исполь­зовать эту опцию, наг­рузка по шиф­рованию и рас­шифров­ке не вырас­тет: ког­да ключ не ука­зан, исполь­зует­ся нулевое зна­чение клю­ча.

А что­бы по‑нас­тояще­му обес­печить пост­кван­товую безопас­ность (невоз­можность рас­шифров­ки дан­ных кван­товыми компь­юте­рами), раз­работ­чики рекомен­дуют допол­нитель­ный внеш­ний кван­тово‑устой­чивый механизм хен­дшей­ка, нап­ример SIDH, который Microsoft пиарит имен­но в таком кон­тек­сте. Соз­данный им общий ключ мож­но исполь­зовать в качес­тве PresharedKey.

Зак­линания в PostUp дос­таточ­но прос­ты. Вот коман­да для под­ста­нов­ки име­ни сетево­го интерфей­са, куда по умол­чанию выпол­няет­ся мар­шру­тиза­ция:

ip route | awk '/default/ {print $5; exit}'

Как пра­вило, это интерфейс, обра­щен­ный к про­вай­деру или роуте­ру.

Та­ким обра­зом, страш­ная коман­да прев­раща­ется в такую:

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Здесь про­исхо­дит вклю­чение NAT в режиме мас­карада: сер­вер будет отправ­лять при­шед­шие ему пакеты во внеш­нюю сеть, под­меняя адрес отпра­вите­ля сво­им, что­бы отве­ты на эти пакеты тоже при­ходи­ли ему, а не исходно­му отпра­вите­лю.

Вто­рая коман­да уже нем­ного слож­нее, но она под­став­ляет IP-адрес дефол­тно­го мар­шру­та.

`ip addr show $(ip route | awk '/default/ { print $5 }') | grep "inet" | grep -v "inet6" | head -n 1 | awk '/inet/ {print $2}' | awk -F/ '{print $1}'`

Сна­чала мы получа­ем, как и выше, сетевой интерфейс мар­шру­та по умол­чанию:

root@:~# ip route | awk '/default/ { print $5 }'
enp1s0

По­том дан­ные о сос­тоянии это­го интерфей­са:

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 — это стре­лять из пуш­ки по воробь­ям, а вот допол­нитель­ное пра­вило исправ­ляет ситу­ацию.

info

Я намерен­но не при­вожу готовые кон­фиги, потому что хочу показать механизм соз­дания кон­фигов в руч­ном режиме. В свое вре­мя я генери­ровал кон­фиги ути­лита­ми типа easy-wg-quick или веб‑сер­висами, которые спра­шива­ют тебя о наз­вании кли­ента и кра­сиво показы­вают QR-код. Это отнюдь не спо­собс­тву­ет понима­нию того, как работа­ет WG на самом деле, и может выз­вать проб­лемы.

Те­перь в оба кон­фига надо добавить сек­цию Peer, что­бы свя­зать сер­веры друг с дру­гом.

Ге­нери­руем из при­ват­ного клю­ча пуб­личный (вот в wg pubkey как раз и про­исхо­дит крип­томагия):

root@:~# echo "kOd3FVBggwpjD3AlZKXUxNTzJT0+f3MJdUdR8n6ZBn8=" | wg pubkey
MxnOnIlKfSyZyRutnYyoWHb3Izjalgf1t8F1oPJiyyw=

Это пуб­личный ключ сер­вера internal, его мы помеща­ем в сек­цию Peer на external:

/etc/wireguard/wg-external.conf
[Peer]
PublicKey=MxnOnIlKfSyZyRutnYyoWHb3Izjalgf1t8F1oPJiyyw=
AllowedIPs=10.20.30.0/24
Endpoint=195.2.79.13:17968
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 на какую‑нибудь дру­гую машину.

Пов­торя­ем генера­цию пуб­лично­го клю­ча с external:

root@:~# echo "6CCRP42JiTObyf64Vo0BcqsX6vptsqOU+MKUslUun28=" | wg pubkey
FulnUTovyyfgn5kmgPkcj2OjKRFGeLkaTsHtAOy6HW8=

Мы получа­ем пуб­личный ключ сер­вера external и помеща­ем его в сек­цию Peer сер­вера internal.

/etc/wireguard/wg-internal.conf
[Peer] #external node
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.

По­это­му по умол­чанию весь тра­фик будет нап­равлять­ся через нее, так как зарубеж­ных мар­шру­тов боль­ше, чем рос­сий­ских, и логич­нее филь­тро­вать имен­но рос­сий­ские, а зарубеж­ный тра­фик пус­тить по умол­чанию через ноду в дру­гой стра­не.

Итак, два кон­фига.

/etc/wireguard/wg-internal.conf
[Interface]
Address = 10.20.30.1/32
ListenPort = 17968
PrivateKey = kOd3FVBggwpjD3AlZKXUxNTzJT0+f3MJdUdR8n6ZBn8=
PostUp = iptables -t nat -A POSTROUTING -o `ip route | awk '/default/ {print $5; exit}'` -j MASQUERADE
PostUp = ip rule add from `ip addr show $(ip route | awk '/default/ { print $5 }') | grep "inet" | grep -v "inet6" | head -n 1 | awk '/inet/ {print $2}' | awk -F/ '{print $1}'` table main
PostDown = iptables -t nat -D POSTROUTING -o `ip route | awk '/default/ {print $5; exit}'` -j MASQUERADE
PostDown = ip rule del from `ip addr show $(ip route | awk '/default/ { print $5 }') | grep "inet" | grep -v "inet6" | head -n 1 | awk '/inet/ {print $2}' | awk -F/ '{print $1}'` table main
#external node
[Peer]
PublicKey = FulnUTovyyfgn5kmgPkcj2OjKRFGeLkaTsHtAOy6HW8=
AllowedIPs = 10.20.30.2/32, 0.0.0.0/0
/etc/wireguard/wg-external.conf
[Interface]
Address = 10.20.30.2/32
PrivateKey = 6CCRP42JiTObyf64Vo0BcqsX6vptsqOU+MKUslUun28=
PostUp = iptables -t nat -A POSTROUTING -o `ip route | awk '/default/ {print $5; exit}'` -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o `ip route | awk '/default/ {print $5; exit}'` -j MASQUERADE
#internal node
[Peer]
PublicKey = MxnOnIlKfSyZyRutnYyoWHb3Izjalgf1t8F1oPJiyyw=
AllowedIPs = 10.20.30.0/24
Endpoint = 195.2.79.13:17968
PersistentKeepalive = 25

Те­перь мож­но под­нять тун­нели на обо­их сер­верах:

root@trikster-external:~# wg-quick down wg-external ; wg-quick up wg-external
root@trikster-internal:~# wg-quick down wg-internal ; wg-quick up wg-internal

Про­веря­ем, что тун­нели активны, коман­дой wg:

root@trikster-internal:~# wg
...
peer: FulnUTovyyfgn5kmgPkcj2OjKRFGeLkaTsHtAOy6HW8=
endpoint: 51.159.187.77:36276
allowed ips: 10.20.30.2/32, 0.0.0.0/0
latest handshake: 13 seconds ago
transfer: 180 B received, 92 B sent
root@trikster-external:~# wg
...
peer: MxnOnIlKfSyZyRutnYyoWHb3Izjalgf1t8F1oPJiyyw=
endpoint: 195.2.79.13:17968
allowed ips: 10.20.30.0/24
latest handshake: 10 seconds ago
transfer: 92 B received, 180 B sent
persistent keepalive: every 25 seconds

Ес­ли видим «latest handshake: ... seconds ago» и бай­ты и в received и в sent, зна­чит, все хорошо. Если бай­ты толь­ко в send, без хен­дшей­ка и получен­ных дан­ных, зна­чит, где‑то в кон­фиге ошиб­ка или сер­веры недос­тупны друг для дру­га.

Ес­ли что‑то пош­ло не так и отва­лил­ся SSH, то дос­таточ­но перезаг­рузить сер­вер — активные тун­нели сбро­сят­ся.

Ес­ли же все хорошо и дос­туп к сер­верам сох­ранил­ся, ста­вим тун­нели в авто­запуск:

root@trikster-internal:~# systemctl enable wg-quick@wg-internal.service
root@trikster-external:~# systemctl enable wg-quick@wg-external.service

Поп­робу­ем пос­мотреть мар­шрут (рекомен­дую замеча­тель­ную ути­литу mytraceroute, mtr) без тун­неля:

root@trikster-internal:~# wg-quick down wg-internal && sleep 10 && mtr -r google.com
HOST: trikster-internal.local Loss% Snt Last Avg Best Wrst StDev
1.|-- host-89-22-232-243.hosted 0.0% 10 0.3 5.4 0.3 49.8 15.6
2.|-- 172.31.0.1 0.0% 10 0.3 19.8 0.3 122.2 42.6
3.|-- 109.239.138.90 0.0% 10 1.5 1.9 1.4 3.0 0.6
4.|-- 91.108.51.4 0.0% 10 11.4 11.4 11.3 11.7 0.1
5.|-- 178.18.227.12.ix.dataix.e 0.0% 10 11.0 17.9 11.0 77.0 20.8

И с тун­нелем:

root@trikster-internal:~# wg-quick up wg-internal && sleep 10 && mtr -r google.com
HOST: trikster-internal.local Loss% Snt Last Avg Best Wrst StDev
1.|-- 10.20.30.2 0.0% 10 51.3 51.3 51.2 51.4 0.1
2.|-- 10.200.100.0 0.0% 10 51.4 51.4 51.2 51.6 0.1
3.|-- 10.197.37.65 0.0% 10 52.5 52.2 52.0 52.5 0.2
4.|-- 10.197.0.41 0.0% 10 52.2 52.2 52.1 52.5 0.1
5.|-- 10.197.0.44 0.0% 10 52.0 52.2 51.9 52.4 0.1

Все хорошо, тра­фик идет через внеш­ний сер­вер — сна­чала на 10.20.30.2, который у нас наз­начен выход­ной нодой, а потом через его мар­шру­тиза­торы.

У нас получи­лась при­мер­но сле­дующая схе­ма.

 

Шаг 3. Добавляем конфиг клиента

Соз­даем кон­фиг кли­ента, конеч­ного устрой­ства — поль­зовате­ля VPN. За осно­ву берем wg-external.conf, потому что это точ­но такой же кли­ент, который под­клю­чает­ся к internal: раз­ница толь­ко в том, что external будет получать пакеты, а наш кли­ент — отправ­лять.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Подписаться
Уведомить о
2 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии