VPN с Mobile IP на Raspberry Pi. WG туннель
Как оно выглядит
Зачем?
Ух. 16 лет зареган, первая статья. :)
Для умных дядь в статье ничего интересного, это статья про обычный туннель. Оно для самых маленьких, но отчаянно нуждающихся. :)
Когда я стал упорядочивать «знания» ввиде этой статейки, и понял что в итоге получилось, то заметил, что информации по данной теме, оказывается, предостаточно (даже на Хабре), жаль, что осознал я это только уже когда всё сделал и понял, что мне, собственно, нужно было гуглить. В общем, пусть останется, дабы кто-то, кто гуглит так же неправильно, мог случайно на эту статью наткнуться и что-то полезное для себя почерпнуть. В процессе реализации я на каждом шаге споткнулся по нескольку раз, поэтому решил прокомментировать тут всё максимально подробно.
Если честно, когда мне понадобилось слать весь трафик через VPN и так, чтобы на выходе светился «человеческий» IP, то гугл мне в этом как-то несильно помог, да и даже в какую сторону гуглить я не понял, банальные «Residential IP VPN» выдавали только комерческие предложения (да я бы и не прочь купить, но нужной мне страны не было в списке) или «Mobile IP VPN», что выдавало вообще какой-то мусор, либо сервисы по продаже мобильных проксей. А прокси мне было мало, мне нужно, чтобы каждый бит выходящий от меня выходил с IP, который бы не кричал о себе «я сервер в ДЦ, скорее всего я VPN» (даже без анализа трафика). Да и, опять же, нужной страны в продаже не было.
Познания в сетях, роутах и прочем у меня на уровне домохозяйки (как, впрочем, и *nix систем), поэтому процесс получения результата шёл, мягко говоря, небыстро. Не смотря на то, что VPN в РФ вроде как в непонятном статусе, сама технология не запрещена (WG блокируется только на зарубежные адреса, если сервер стоИт в РФ, то проблем нет как с доступом на него из РФ, так и из зарубежа), так что работать будет, а в случае зарубежной симки ещё и IP получится зарубежный со всеми вытекающими, ну это уже вынужденный бонус. :)
Хотя речь в статье про Raspberry Pi, её наличие непринципиально, точкой выхода может быть как любой другой VPS, так и, например, ваш домашний роутер/компьютер, если вы захотите сделать себе Residential IP VPN, суть в построении туннеля.
Сетап
Paspberry Pi 5
LTE Hat (на алике полно, на авито тоже попадаются, но и с простым USB модемом тоже прокатит, но мне хотелось небольшой, красивой, цельной коробочки, из которой торчат только антенны, а из кабелей подходит только питание, ну и приоритет у такого модема (route metric) по умолчанию ставится выше WiFi, так что дополнительно никаких настроек делать не нужно.
VPS. Я не готов полагаться на то, что какой-то ОПСоС вдруг не начнёт выдавать IP за NAT, поэтому на DynDNS не рассчитываю, нужен железный вариант
VPS
Сервер подойдёт любой, я делал на FirstVDS, просто потому, что уже там зареган и так было быстрее оформить отдельный сервер для тестов, но вообщее правильней будет взять сервер в той стране, чья симкарта будет у вас вставлена.
Итак, будем считать, что сервер новый и первым делом нужно его настроить. У меня на VPS поставлена Ubuntu, так что инструкции будут для неё (для Debian оно вроде +/- похоже тоже)
$ adduser -m USERNAME # Создаём пользователя чтобы не работать под рутом
$ usermod -aG sudo USERNAME # Разрешаем ему выполнять команды от рута
# Коннектимся на сервер уже под НОВЫМ пользователем и обновляем систему
$ sudo apt update && sudo apt upgrade -y
Docker
Дальше все компоненты будем устанавливать через докер. Не работал с ним до этого момента, но штука оказалась дико удобной, особенно, когда ты делаешь десятки экспериментов/переустановок в процессе настройки и основная система от всех твоих кривых действий практически изолирована, всё, что вы наэкспериментируете с сетями останется в контейнере и ваш сервер не станет вдруг внезапно недоступен из-за криворукости.
$ curl -sSL https://get.docker.com/ | sh # Устанавливаем Docker
$ sudo groupadd docker # Создаём группу
$ sudo usermod -a -G docker $USER # Позволяем докеру работать под текущим (созданным выше) пользователем, а не из под рута
Portainer
Для управления контейнерами докера я выбрал portainer. Можно и без него, всё в командной строке, быстрее и короче, но так удобней (да и для дальнейших экспериментов полезно)
$ sudo mkdir -p /portainer # Создаём директорию для portainer
$ sudo docker pull portainer/portainer-ce:latest # Скачиваем его последний образ
# Запускаем portainer в самом докере
# 9443 — SSL порт внутри контейнера
# 9444 — SSL порт, который будет торчать снаружи.
# Можно их делать одинаковыми, но я не очень люблю значения "по умолчанию"
$ sudo docker run -d -p 9444:9443 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /portainer:/data portainer/portainer-ce:latest
После этого, можно открывать в браузере httpS://your.vps.server:9444, должно появиться окно с предложением задать юзер и пароль:
httpS://your.vps.server:9444
Собственно, указываете свои данные и далее на стартовой странице будет нелишним указать ещё небольшую деталь:
Кнопка с настройками
Тут указать IP или адрес сервера. Это необязательно, но зато при клике на порт какого-нибудь контейнера вместо перехода на http://0.0.0.0: PORT вы попадёте по правильному адресу:
Адрес вашего VPS
Далее, переходим к управлению своими контейнерами:
Список контейнеров вашего сервера
Wireguard (VPS)
Кому лень смотреть как делается через UI, ниже способ всё это сделать одной командой
Далее, необходимо установить Wireguard. Добрые люди сделали темплейты (раздел »Templates» в меню слева) на быструю установку WG «в один клик», но они несильно кастомизабельны и у меня «в один клик» не запустились, потребовалась некая доработка, так что я решил, что лучше будет показать как задеплоить контейнер «с нуля», без шаблонов.
В правом верхнем углу тыкаем на синюю кнопку »Add container», вводим имя контейнера (любое), имя образа linuxserver/wireguard:latest
(именно такое) и добавляем порт, который будет торчать наружу. HOST порт может быть любым, а CONTAINER порт должен быть 51820
и обязательно UDP:
Основные настройки
Далее, прокручиваем страницу ниже, к разделу »Advanced container settings».
В разделе »Volumes» нужно указать (забиндить) директорию с конфигами WG (/config
) внутри контейнера на какую-то директорию нашего сервера, в которую мы легко сможем попасть:
Настройки директорий
Далее, нам нужно задать некоторые переменные окружения на вкладке »Env»:
# Эти два свойства мапят пользователя внутри контейнера на локального пользователя
# Того, которого мы создавали в самом начале
PUID=1000
PGID=1000
# Диапазон адресов, который будет использоваться в сети,
# которую создаст WireGuard для клиентов
INTERNAL_SUBNET=10.13.13.0
# Это просто установка таймзоны, в каждом контейнере может быть своя,
# поэтому выгодно везде ставить UTC, как мне кажется
TZ=Etc/UTC
# Ни на что не влияет, "клиентов" можно добавить позже сколько
# угодно. Можно тут даже ноль указать, это просто количество клиентов (пиров)
# для которых при старте создадутся конфигурации. Если использовать этот
# Wireguard только как VPS сервер, то можно указывать значения больше ноля
# Для нашей задачи указывать не нужно, но для проверки можно.
PEERS=2
# Если внешний порт вы поменяли, то для генерации правильных конфигов клиентам
# нужно указать этот порт и тут, если мспользовали по умолчанию, то не нужно
SERVERPORT=51888
Переменные окружения
На вкладке »Restart policy» выбрать »Unless stopped», чтобы в случае чего контейнер перезапустился, но не перезапускался, если мы остановим его сами, вручную.
Далее, на вкладке »Runtime & resources» нужно добавить необходимое условие работы wireguard: net.ipv4.conf.all.src_valid_mark=1
, а так же разрешаем пересылку пакетов между разными сетямиnet.ipv4.ip_forward=1
Ну и последнее, на вкладке »Capatibilities» поставить переключатель NET_ADMIN
в true, чтобы WG мог работать с сетями:
NET_ADMIN
Всё, можно тыкать на синюю кнопку »Deploy container», wireguard должен стартануть, в списке контейнеров появится ваш новый контейнер.
Нелишним будет проверить, что всё окей и тыкнуть на кнопочку «логи», чтобы посмотреть, что всё стартовало без ошибок.
Список контейнеров с wireguard
Там же, в логе будут напечатаны QR коды для подключения к серверу. Они не понадобятся, но можно быстро проверить с телефона, что всё работает, например. На этом шаге VPN у вас уже есть.
К слову, всё, что выше делалось через UI можно сделать одной командой в консоли:
docker run -d \
--name=wireguard \
--cap-add=NET_ADMIN \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Etc/UTC \
-e SERVERPORT=51888 \
-e PEERS=2 \
-e INTERNAL_SUBNET=10.13.13.0 \
-p 51888:51820/udp \
-v /portainer/wireguard/config:/config \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--sysctl="net.ipv4.ip_forward=1" \
--restart unless-stopped \
linuxserver/wireguard:latest
Настройка туннеля на VPS
Собственно, сейчас нужно настроить правила, чтобы когда к этому серверу WG подключился клиент и raspberry, трафик от клиента выходил через малинку.
Так как мы замапили внутреннюю директорию /config на реальную /portainer/wireguard/config, то по пути /portainer/wireguard/config/wg_confs/wg0.conf
будет сгенерирован файл конфигурации сервера:
$ nano /portainer/wireguard/config/wg_confs/wg0.conf
[Interface]
Address = 10.13.13.1
ListenPort = 51820
PrivateKey = VPS_PRIVATE_KEY
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE
[Peer]
PublicKey = CLIENT_PUBLIC_KEY
PresharedKey = CLIENT_PSK
AllowedIPs = 10.13.13.2/32
[Peer]
PublicKey = PI_PUBLIC_KEY
PresharedKey = PI_PSK
AllowedIPs = 10.13.13.3/32
Если бы задача стояла «запусть свой VPN сервер», то на этом можно было бы закончить, но нам нужно его слегка подправить:
# Удаляем сгенерённый файл
$ rm /portainer/wireguard/config/wg_confs/wg0.conf
# Создаём новый
$ nano /portainer/wireguard/config/wg_confs/wg0.conf
[Interface]
# Адрес нашего "сервера"
Address = 10.13.13.1/32
# Порт внутри контейнера для коннекта (не внешний, торчащий наружу)
ListenPort = 51820
# Приватный ключ сервера
PrivateKey = VPS_PRIVATE_KEY
# Если вам для каких-то задач требуется доступ к вашему VPS "изнутри"
# Например, какие-то порты из интернета вы сделали недоступными,
# то можно раскомментировать эту строчку
# PostUp = iptables -t nat -A PREROUTING -d 10.13.13.1/32 -j DNAT --to-destination 172.17.0.1
# Либо использовать более "общий" вариант, в котором конкретные адреса
# подставятся автоматически, обе команды делают одно и то же:
# PostUp = iptables -t nat -A PREROUTING -d $(ip addr show %i | awk '/inet/ {print $2}') -j DNAT --to-destination $(ip addr show eth0 | awk '/inet/ {print $2}' | awk -F"." '{print $1"."$2"."$3".1"}')
# В командах ниже %i будет заменён на имя сетевого интерфейса wireguard
# По умолчанию это 'wg0'
# Так как WG находится в контейнере, то нам не нужны тут никакие
# дополнительные таблицы маршрутизации, чтобы разделять трафик
# на WG/не-WG: ещё один плюс контейнеров докера.
# Так же, мы не выходим в интернет через этот сервер, маршрутизация
# в интернет (как в автосгенерёном конфиге выше) нам тоже не нужна
# Настройка пира нашей Raspberry Pi
[Peer]
# ПУБЛИЧНЫЙ ключ клиента
PublicKey = PUBLIC_PI_KEY
# Так как через данный пир мы будем ходить в интернеты, то нельзя ограничивать
# тут адреса, нули означают "разрешены любые коннекшены".
# Зарезервируем ему адрес "10.13.13.2/32", просто держим в голове
AllowedIPs = 0.0.0.0/0
# Наш ноутбук, телефон, роутер, что угодно, что хочет в интернет
# через VPN, и желает, чтобы снаружи его IP светился как "мобильный"
[Peer]
PublicKey = PUBLIC_CLIENT_1_KEY
AllowedIPs = 10.13.13.3/32
# Второй и последующие клиенты, если нужно подключить больше одного девайса
[Peer]
PublicKey = PUBLIC_CLIENT_2_KEY
AllowedIPs = 10.13.13.4/32
Сохраняем (Ctrl+O), выходим (Ctrl+X).
PresharedKey я убрал (это дополнительная защита к стойскости ключа пиров).
# Перезапускаем контейнер с WG
$ docker restart wireguard
Для генерации ключей можно использовать как онлайн генераторы, так и консольную команду для wireguard, но так как wg, скорее всего, не установлен нигде, кроме контейнеров, то команду можно выполнить только внутри контейнера. На Raspberry или на VPS, или вообще на любом другом компе, где установлен wg — неважно. В нашем случае следующее можно выполнить на любом из пиров:
# Запустить шелл в контейнере с именем "wireguard"
$ docker exec -it wireguard sh
# Сгенерировать пару публичный-приватный ключ (можно нагенерить с десяток,
# сразу на все пиры):
$ priv=`wg genkey` && printf "Private key: $priv\nPublic key: `echo "$priv" | wg pubkey`\n" && unset -v priv
# Пример вывода:
# Private key: EIrYKTpC93LiFBFsoyH/v8eCmMfD4rJLSmc2cgDl9lc=
# Public key: I1mrtQTkguQRbD5bNTPGk8Li3EbBhcDmc2xCNaTIwj0=
# Выход из шелла контейнера обратно в хост:
$ exit
Raspberry Pi
Тут, наверное, самый простой путь: собираем запчасти воедино, вставляем симкарту, устанавливаем Raspberry Pi OS, всё по мануалам с оффсайта, ошибиться там невозможно. В качестве host OS выбираем Pi Lite x64. Я провёл довольно много разных экспериментов и потестил пару разных Hats для малины, на других OS типа ubuntu не всегда они определялись без бубна, а минимальная официальная ось поддерживает их из коробки, да и UI нам на сервере не нужен.
При установке официальной OS можно указать пользователя (он уже не будет рутом, то, что нам и нужно), задать ему пароль, а так же указать параметры домашней сети, чтобы при старте малина автоматически подключилась к ней. Далее на своём роутере вы сможете узнать её IP, чтобы подключиться. Так никакие мониторы и клавиатуры к данной коробочке подключать не потребуется, можете её на пауэрбанке рядом с собой положить и продолжить настройку.
Как и в случае с докером, до сего момента я одноплатных компьютеров не касался, теперь у меня их несколько и появилось желание на них что-то интересное поделать, сервер, который потребляет 5Вт — это прекрасно.
Docker + Portainer
Всё отличие от VPS только в том, что сейчас ваш сервер рядом с вами, можно даже в карман его положить, так что шаги практически идентичные. Можно действовать точно так же, как описано выше, в разделе VPS, а можно с заделом на будущее обратиться к хелпер-скриптам от pi-hosted (неплохой набор скриптов для тех, кто хочет превратить свою Raspberry в монстра).
Wireguard (Pi)
Дабы не постить опять кучу скриншотов, оставлю только консольную команду, которая запускает контейнер с WG (в данном примере я выбрал порт 51999, это не обязательно можно оставлять всегда и везде по умолчанию 51820):
docker run -d \
--name=wireguard \
--cap-add=NET_ADMIN \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Etc/UTC \
-e INTERNAL_SUBNET=10.13.13.0 \
-p 51999:51820/udp \
-v /portainer/Files/AppData/Config/wireguard/config:/config \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--sysctl="net.ipv4.ip_forward=1" \
--restart unless-stopped \
linuxserver/wireguard:latest
На самом деле, можно и предыдущую (из раздела VPS) команду скопировать. У WG нет разницы «сервер» это или «клиент», всё есть «пир».
Из отличий тут только отсутствие PEERS
, дабы показать, что оно необязательно и клиентов можно добавлять самому сколько угодно, а так же другой путь до директории конфигурации, так как я использовал pi-hosted, и это там путь по умолчанию.
Настройка туннеля на Raspberry Pi
# Удаляем сгенерённый файл
$ rm /portainer/Files/AppData/Config/wireguard/config/wg_confs/wg0.conf
# Создаём новый
$ nano /portainer/Files/AppData/Config/wireguard/config/wg_confs/wg0.conf
[Interface]
PrivateKey = PI_PRIVATE_KEY
Address = 10.13.13.2/32
# Если хочется использовать RPi как VPS "сервер" напрямую, то
# можно тут указать порт, а ниже добавить клиента
ListenPort = 51820
# Добавляем некие правила при старте WG
# Все входящие пакеты в сеть WG (wg0 интерфейс) помечаются меткой 0x30
# Метка может быть любая, если бы мы не запускались в контейнере, то нужно
# было бы следить, чтобы данную метку не ставил кто-то ещё и выбирать уникальную
PreUp = iptables -t mangle -A PREROUTING -i %i -j MARK --set-mark 0x30
# Подменяем всем исходящим, помеченным меткой 0x30 пакетам адрес на
# адрес согласно текущим таблицам маршрутизации.
# Иными словами, если у нас кто-то из VPN сети просится в интернет, то
# позволяем этому пакету выйти через любой интерфейс, под который он попадает
# не заставляя его принудительно ходить только через LTE/WiFi/LAN
PreUp = iptables -t nat -A POSTROUTING ! -o %i -m mark --mark 0x30 -j MASQUERADE
# Отменяем два предыдущих правила при остановке WG
PostDown = iptables -t mangle -D PREROUTING -i %i -j MARK --set-mark 0x30; iptables -t nat -D POSTROUTING ! -o %i -m mark --mark 0x30 -j MASQUERADE
# Тут указываем параметры коннекшена к нашему VPS
[Peer]
PublicKey = VPS_PRIVATE_KEY
# Нужно указать public IP/host нашего VPS и тот порт,
# который мы там замапили
Endpoint = vps.host.ip:51888
# Разрешаем трафик из всей wg подсети,
# всех пиров от 10.13.13.1 до 10.13.13.255
AllowedIPs = 10.13.13.0/24
# Каждые 15 секунд посылать хэртбиты, дабы в случае,
# если мы за NAT, то не отвалиться
PersistentKeepalive = 15
# Если хочется использовать и этот WG как "сервер",
# то можно добавить "клиентов" и тут, но для коннекта вам потребуется
# знать реальный IP этой малинки (например, вы с ней в одной локалке/WiFi
# или ОПСоС выделил нормальный, доступный IP
[Peer]
PublicKey = DIRECT_CLIENT_PUBLIC_KEY
# Так как все пиры в итоге объединятся в одну сеть, то нужно следить
# чтобы адреса не пересекались и так как в конфиге VPS мы остановились
# на клиенте с адресом 10.13.13.4, то здесь минимальный будет уже 5
# Они не обязательно должны идти по порядку, если что
AllowedIPs = 10.13.13.5/32
Сохраняем (Ctrl+O), выходим (Ctrl+X).
# Перезапускаем контейнер с WG
$ docker restart wireguard
Клиенты
Всё, туннель настроен, работает, осталось только создать конфиги для клиентов и подсунуть их, собственно, самим клиентам (мобилке, ноутбуку).
На VPS у нас было 2 клиента, вот пример их конфигураций:
Клиент 1 на VPS (ноутбук/роутер)
[Interface]
# ПРИВАТНЫЙ ключ нашего клиента
PrivateKey = PRIVATE_CLIENT_1_KEY
# Этот адрес должен соответстовать адресу, который был указан
# в конфиге на VPS
Address = 10.13.13.3/32
# DNS сервер для всех запросов, без его указания доступ
# в инернет у вас будет работать только при указании прямых
# IP адресов. Я использую гугл (8.8.8.8) или Cloudflare (1.1.1.1)
DNS = 1.1.1.1
# VPS
[Peer]
# ПУБЛИЧНЫЙ ключ VPS сервера
PublicKey = PUBLIC_VPS_KEY
# Так как мы собираемся выходить в интернеты через эту точку
# то назад нам могут прийти пакеты с абсолютно любых IP
# поэтоуму нам не нужно ограничивать тут "разрешённые" адреса
# ставим "разрешать вообще всё"
AllowedIPs = 0.0.0.0/0
# Публичный IP сервера и порт, который мы ему задавали
Endpoint = vps.host.ip:51888
Клиент 2 на VPS (смартфон)
Точно такой же конфиг, только приватный ключ нужен от второго клиента и адрес будет 10.13.13.4/32
Клиент 3 на Raspberry Pi (прямое подключение, без VPS)
Опять же, всё практически то же самое:
[Interface]
PrivateKey = PRIVATE_CLIENT_3_KEY
# DNS может быть у каждого клиента свой
DNS = 8.8.8.8
# Внимание на адрес
Address = 10.13.13.4/32
[Peer]
# Здесь уже данные от Raspberry Pi, не от VPS
PublicKey = PUBLIC_PI_KEY
AllowedIPs = 0.0.0.0/0
# Адрес Raspberry Pi
# (локальный, если вы в одной локалке, либо публичный ОПСоСа)
# Порт не от VPS, а тот, который экспортировали в контейнере на RPi
# В нашем примере это был 51999
Endpoint = local.or.good.public.raspberry.ip:51999
Собственно, всё. Туннель настроен, IP теперь человеческий, а в случае использования зарубежной симки (даже в роуминге) ещё и IP зарубежный получается со всеми бонусами. Можно искать какого-нибудь друга, который поделится у себя дома пятью ваттами электричества, интернеты у него просить не нужно.
Проверка тоже говорит, что мы вроде как норм:
2ip.io
При рестарте серверов контейнеры поднимаются и коннектятся автоматически, при падении Raspberry или VPS инет у клиентов по умолчанию отваливается, поэтому случайно свой реальный IP засветить невозможно.
Поднятый туннель никак не влияет на трафик самого VPS или Raspberry, так как находится в контейнере и сам сервер в интернеты ходит напрямую, вы можете проверить это командой curl 2ip.io
чтобы в консоли VPS или малины узнать свой внешний IP
Проверить состояние интерфейса, подключённых клиентов и количество трафика пропущенных через них можно командой wg
внутри контейнера:
# Запустить шелл в контейнере с именем "wireguard"
$ docker exec -it wireguard sh
# Выводим состояние Wireguard
$ wg
Заключение
Обычно у LTE скорость загрузки сильно ниже скорости скачивания, но у меня попалась какая-то неправильная симка на тестах, у которой скачивание и загрузка по скорости равны, однако, логично предположить, что при работе через туннель «скачивать» она будет то, что вы ей отправите, а «загружать» то, что вы захотите с интернетов скачать, поэтому ограничение скорости у вас будет по минимальному параметру (обычно по скорости аплоада LTE)
«Сегодня я многое понял» ©
На саму эту Raspberry у меня ещё довольно много планов, это был первый шаг в довольно долгом и трудном пути познания этих ваших «сетевых технологий». В мыслях повысить отказоустойчивость (балансирование интернет соединений, чтобы в случае, если в месте установки малины кроме розетки в 220 вольт будет доступ к WiFi и/или проводу при отвале LTE доступ к коробочке оставался), возможно добавить какие-то другие туннели, и скорее всего там будет контейнер с эмулятором смартфона и FakeGPS. Пока что все эти темы мне неизвестны, но желание и потребность в их изучении имеется.
Полезные ссылки
Неофициальная, но весьма познавательная документация по Wireguard.
Хорошая статья про разные виды туннелей на Wireguard (и ещё одна туда же).