Минулого разу ми говорили про ідеї, що підштовхнули до створення мережевого тунелю WireGuard, та алгоритми, що лежать в його основі. Цього ж разу спробуємо налаштувати WireGuard і пересвідчимось, що VPN is fun again.
WireGuard ще не включено до кодової бази ядра Linux і наразі розповсюджується як DKMS-модуль. У якості сервера Wireguard працює лише на Linux, а у якості клієнта - під усіма популярними операційними системами, в т.ч. мобільними, в основному як userspace-імплементація на мові Go.
Для демонстрації можливостей WireGuard ми скористаємось Ubuntu 18.04. Для неї пакети розповсюджуються через ppa-репозиторій. На кожному вузлі, що буде з'єднуватись тунелем, необхідно встановити наступні пакети:
# add-apt-repository ppa:wireguard/wireguard
# apt update
# apt install wireguard
Можливо також потрібно буде встановити пакети-заголовки для ядра:
# apt install linux-headers-$(uname -r) linux-headers-generic
Кожен вузол повинен мати свою пару асиметричних ключів, тож згенеруємо їх на кожному хості:
# cd /etc/wireguard/
# umask 077
# wg genkey | tee privatekey | wg pubkey > publickey
У першому сценарії побачимо як з'єднувати тунелем 2 вузла між собою. На вузлі, що виступатиме сервером, пишемо наступне:
# vim /etc/wireguard/wg0.conf
[Interface]
# server's privatekey
PrivateKey = MIRmd9owAlnKwRjk2RbzjcGvlssB5qmGP0UW4M/0QmA=
Address = 172.21.12.1/32
ListenPort = 51820
[Peer]
# client's publickey
PublicKey = HsG+rLgPRd18DN4lqBN4jyvgnOMOk57jd9zpHkEkUHs=
AllowedIPs = 172.21.12.2/32
Де 172.21.12.1/32 адреса сервера, котру він матиме в тунелі, 51820 - UDP-порт, що буде очікувати на підключення (має бути дозволений на фаерволі), PrivateKey/PublicKey - приватний/публічний ключ сервера/клієнта, вміст файлу privatekey/publickey, що були згенеровані вище на сервері/клієнті відповідно.
На стороні ж клієнта конфігураційний файл матиме наступний вигляд:
# vim /etc/wireguard/wg0.conf
[Interface]
# client's privatekey
PrivateKey = QLnMSq7IaXks08mka1vr0bplWmUJmoCXErcPz56ykmU=
Address = 172.21.12.2/32
[Peer]
# server's publickey
PublicKey = 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
Endpoint = <server's public ip address>:51820
AllowedIPs = 172.21.12.1/32
PersistentKeepalive = 25
Де, аналогічно, PrivateKey - ключ клієнта privatekey та PublicKey - публічний ключ сервера. Тобто за основу можна взяти наступне: приватний ключ окремої системи не має копіюватись за межі цієї системи.
Мабуть тут буде доречно згадати про те, яким чином в WireGuard працює маршрутизація пакетів (Cryptokey routing). Сенс її полягає в поєднанні публічних ключів та списку IP, що дозволені в тунелі. Кожен мережевий інтерфейс (мається на увазі wg) має приватний ключ та список вузлів (peers), з'єднання з якими може відбуватись. У свою чергу кожен пір має публічний ключ. Публічні ключі використовуються для аутентифікації з'єднань між собою.
Якщо за приклад взяти конфігураційні файли вище (wg0.conf), то все буде виглядати приблизно наступним чином. У конфігурації сервера вказано, що кожен клієнт матиме можливість відправляти пакети до мережевого інтерфейсу сервера з IP адрес (source IPs), що описані в секції AllowedIPs, тобто з 172.21.12.2 (адреса клієнта). Наприклад, якщо пакет буде отримано сервером від вузла HsG+rLgP..., після цього він буде автентифікований і дешифрований, і у разі, якщо його IP адреса буде 172.21.12.2, то пакет буде дозволений на wg0-інтерфейсі і буде маршрутизований далі.
Знову ж, виходячи з конфігурації серверу, якщо мережевий інтерфейс сервера захоче відправити пакет клієнту (peer-у), Wireguard перегляне IP-адресу призначення пакету і порівняє її із кожним списком дозволених адрес кожного піра. У нашому випадку, якщо мережевому інтерфейсу необхідно буде відправити пакет з адресою призначення 172.21.12.2, то сервер зашифрує його використовуючи публічний ключ клієнта HsG+rLgP... і вже потім відправить його на останній відомий публічний ендпоїнт клієнта.
У конфігурації клієнта указано, що сервер може відправляти пакети клієнту з мережевого інтерфейсу із адресою 172.21.12.1 (source IP). Наприклад, якщо пакет буде отримано від вузла 50TO6tBK... (тобто сервера), зможе бути розшифрований та коректно автентифікований і адреса пакета буде 172.21.12.1, то такий пакет буде дозволено на мережевому інтерфейсі клієнта; інакше ж його буде відкинуто.
Також у wg0.conf клієнта вказано, якщо його мережевий інтерфейс захоче відправити пакет серверу, то він спочатку має його зашифрувати лише для сервера із AllowedIPs адресою. Тобто, якщо мережевому інтерфейсу клієнта необхідно відправити пакет на 172.21.12.1, він його зашифрує ключем 50TO6tBK... і потім відправить на останній відомий публічний ендпоїнт сервера.
Саме це і входить до терміну Cryptokey Routing Table: асоціація публічних ключів та дозволених IP-адрес.
Піднімаємо тунель спочатку на сервері, а потім і на клієнті:
# wg-quick up wg0
У результаті чого на сервері буде піднято wg0-інтерфейс із адресою 172.21.12.1, а на клієнті - з адресою 172.21.12.2. Перевіримо роботу тунелю. Для цього запустимо ping на клієнті:
$ ping -c2 172.21.12.1
PING 172.21.12.1 (172.21.12.1) 56(84) bytes of data.
64 bytes from 172.21.12.1: icmp_seq=1 ttl=64 time=39.0 ms
64 bytes from 172.21.12.1: icmp_seq=2 ttl=64 time=37.4 ms
--- 172.21.12.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1ms
rtt min/avg/max/mdev = 37.362/38.189/39.017/0.850 ms
І перевіримо чи приходять пакети на сервері:
# tcpdump -n -i wg0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wg0, link-type RAW (Raw IP), capture size 262144 bytes
12:03:58.892503 IP 172.21.12.2 > 172.21.12.1: ICMP echo request, id 662, seq 1, length 64
12:03:58.892556 IP 172.21.12.1 > 172.21.12.2: ICMP echo reply, id 662, seq 1, length 64
12:03:59.893866 IP 172.21.12.2 > 172.21.12.1: ICMP echo request, id 662, seq 2, length 64
12:03:59.893910 IP 172.21.12.1 > 172.21.12.2: ICMP echo reply, id 662, seq 2, length 64
WireGuard розповсюджується із клієнтськими утилітами для діагностики тунелю.
# wg -h
Usage: wg <cmd> [<args>]
Available subcommands:
show: Shows the current configuration and device information
showconf: Shows the current configuration of a given WireGuard interface, for use with `setconf'
set: Change the current configuration, add peers, remove peers, or change peers
setconf: Applies a configuration file to a WireGuard interface
addconf: Appends a configuration file to a WireGuard interface
genkey: Generates a new private key and writes it to stdout
genpsk: Generates a new preshared key and writes it to stdout
pubkey: Reads a private key from stdin and writes a public key to stdout
You may pass `--help' to any of these subcommands to view usage.
Переглянемо стан роботи тунелю однією із них:
# wg show
interface: wg0
public key: 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
private key: (hidden)
listening port: 51820
peer: HsG+rLgPRd18DN4lqBN4jyvgnOMOk57jd9zpHkEkUHs=
endpoint: <client's public IP address>:49589
allowed ips: 172.21.12.2/32
latest handshake: 22 seconds ago
transfer: 35.78 KiB received, 25.74 KiB sent
Якщо звернутись до початкового конфігураційного файлу клієнта, він має початковий endpoint, тобто публічну IP-адресу та порт Wireguard-сервера. Тож клієнт знатиме на яку адресу відправляти шифровані дані. В конфігурації сервера такий параметр відсутній - сервер знаходить endpoint клієнта за адресою звідки приходять правильно автентифіковані дані. Якщо ж сервер змінить свою адресу - клієнт аналогічно визначить новий endpoint сервера і відповідно змінить конфігурацію. Клієнт та сервер відправляють шифровані дані на останній IP endpoint, котрий коректно дешифрував дані. Саме ця логіка і забезпечує режим роумінгу (roaming) по обидві сторони і це неймовірний функціонал на нестабільних чи динамічних мережах на кшталт мобільних.
На сервері додамо WireGuard в автозавантаження:
# systemctl enable wg-quick@wg0
На клієнті варто виконати аналогічну дію за потреби.
Цього разу спробуємо з'єднати клієнт з сервером та локальною мережею позаду нього. Тобто сервер буде виступати таким собі шлюзом для доступу в приватну мережу (наприклад, офісну) і не більше. Для цього необхідно виконати мінімальні зміни конфігураційних файлів, що були використані в попередньому пункті. На сервері:
# vim /etc/wireguard/wg0.conf
[Interface]
# server's privatekey
PrivateKey = MIRmd9owAlnKwRjk2RbzjcGvlssB5qmGP0UW4M/0QmA=
Address = 172.21.12.1/32
ListenPort = 51820
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -s 172.21.12.0/24 -A POSTROUTING -o ens2 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -s 172.21.12.0/24 -D POSTROUTING -o ens2 -j MASQUERADE
[Peer]
# client's publickey
PublicKey = HsG+rLgPRd18DN4lqBN4jyvgnOMOk57jd9zpHkEkUHs=
AllowedIPs = 172.21.12.2/32
Де ens2 - інтерфейс, що обслуговує внутрішню локальну мережу 10.0.0.0/8. Кожного разу, коли буде піднято інтерфейс wg0 на сервері, буде виконано скрипт PostUp, котрий запустить правило iptables. Це правило активує NAT трансляцію для пакетів із підмережі 172.21.12.0/24 в уже згаданий інтерфейс. Власне нічого особливого в цій логіці - аналогічно транслюються адреси і, наприклад, у OpenVPN.
На клієнті конфігураційний файл має бути наступним:
# vim /etc/wireguard/wg0.conf
[Interface]
# client's privatekey
PrivateKey = QLnMSq7IaXks08mka1vr0bplWmUJmoCXErcPz56ykmU=
Address = 172.21.12.2/32
[Peer]
# server's publickey
PublicKey = 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
Endpoint = <server's public ip address>:51820
AllowedIPs = 172.21.12.1/32, 10.0.0.0/8
PersistentKeepalive = 25
Тут варто звернути увагу на параметр AllowedIPs. Він вказує на те, що клієнт має право приймати пакети із 172.21.12.1/32, 10.0.0.0/8 підмереж.
Перезапустимо WireGuard на сервері, потім на клієнті та перевіримо зв'язок з клієнта:
$ ping -c2 10.22.57.7
PING 10.22.57.7 (10.22.57.7) 56(84) bytes of data.
64 bytes from 10.22.57.7: icmp_seq=1 ttl=58 time=41.8 ms
64 bytes from 10.22.57.7: icmp_seq=2 ttl=58 time=43.4 ms
--- 10.22.57.7 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 41.799/42.618/43.438/0.845 ms
Де 10.22.57.7 - вузол, що лежить в тій же мережі, що і WireGuard сервер. Також не варто забувати про попередню активацію ip forwarding в ядрі, що дозволить пересилати пакети між мережевими інтерфейсами:
# vim /etc/sysctl.conf
...
net.ipv4.ip_forward=1
...
# sysctl -p
Мабуть найбільш класичний спосіб використання VPN. Тобто дані до WireGuard-серверу будуть додатково шифруватись, а вже після відправлятимуться як трафік із цієї локації. Це підвищить рівень безпеки та приховає справжнє місцезнаходження клієнта. Для цього варто виконати мінімальні зміни в порівнянні з попереднім пунктом. На клієнті правимо параметр AllowedIPs:
# vim /etc/wireguard/wg0.conf
[Interface]
# client's privatekey
PrivateKey = QLnMSq7IaXks08mka1vr0bplWmUJmoCXErcPz56ykmU=
Address = 172.21.12.2/32
[Peer]
# server's publickey
PublicKey = 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
Endpoint = <server's public ip address>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Тобто джерелом пакетів для клієнта можуть бути будь-які IPv4 адреси.
Це все. Перезапустимо WireGuard та перевіримо публічну IP-адресу на клієнті:
$ sudo wg-quick down wg0
$ sudo wg-quick up wg0
$ curl ifconfig.me
<server's public ip address>
Досить цікавий стан справ з маршрутами. У вже згаданому OpenVPN у клієнта у подібній схемі створюється декілька нових маршрутів із яких простежується логіка роутингу трафіку. Щось схоже на це:
# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.8.0.5 128.0.0.0 UG 0 0 0 tun0
0.0.0.0 192.168.1.1 0.0.0.0 UG 100 0 0 eth0
10.8.0.1 10.8.0.5 255.255.255.255 UGH 0 0 0 tun0
10.8.0.5 0.0.0.0 255.255.255.255 UH 0 0 0 tun0
128.0.0.0 10.8.0.5 128.0.0.0 UG 0 0 0 tun0
169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 tun0
public_ip_of_vpn_server 192.168.1.1 255.255.255.255 UGH 0 0 0 eth0
192.168.1.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
Тобто для початкового з'єднання на публічну адресу public_ip_of_vpn_server OpenVPN створює окремий маршрут через попередній гейтвей 192.168.1.1 (транспортний канал), після чого додає 2 окремих маршрути на приватну адресу OpenVPN-серверу (10.8.0.5), що описує доступ на всю IPv4 мережу (0.0.0.0/0).
У разі WireGuard маршрути додаються інакше, використовуючи маркування fwmark. Це стосується тунелей, що були підняті утилітою wg-quick.
Для уникнення витоку адрес сайтів, що були відвідані, скористаймось власним кешуючим DNS-сервером, котрий встановимо на VPN-сервер. Цей можливий недолік безпеки має назву DNS Leak. Суть його в тому, що дані до DNS-серверів при певних обставинах можуть прямувати не через VPN-сервер, а напряму, по локальним маршрутам і таким чином відслідковуватись третьою стороною. Це може статись, якщо буде використовуватись локальний DNS-сервер, котрий автоматично надає провайдер по DHCP-протоколу і т.п.
У якості вже згаданого DNS-сервера використаємо Unbound. Установимо його на WireGuard сервер та завантажимо адреси кореневих DNS-серверів:
# apt install unbound unbound-host
# curl -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache
Приберемо попередній вміст конфігураційного файлу Unbound та запишемо наступне:
# vim /etc/unbound/unbound.conf
server:
num-threads: 4
# Enable logs
verbosity: 1
# list of Root DNS Server
root-hints: "/var/lib/unbound/root.hints"
# Use the root servers key for DNSSEC
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# Respond to DNS requests on wg interface only
interface: 172.21.12.1
max-udp-size: 3072
# Authorized IPs to access the DNS Server
access-control: 0.0.0.0/0 refuse
access-control: 172.21.12.0/24 allow
# Not allowed to be returned for public internet names
private-address: 172.21.12.0/24
# Hide DNS Server info
hide-identity: yes
hide-version: yes
# Limit DNS Fraud and use DNSSEC
harden-glue: yes
harden-dnssec-stripped: yes
harden-referral-path: yes
# Add an unwanted reply threshold to clean the cache and avoid when possible a DNS Poisoning
unwanted-reply-threshold: 10000000
# Have the validator print validation failures to the log.
val-log-level: 1
# Minimum lifetime of cache entries in seconds
cache-min-ttl: 1800
# Maximum lifetime of cached entries
cache-max-ttl: 14400
prefetch: yes
prefetch-key: yes
Тобто ми дозволили доступ до DNS-серверу лише з адрес підмережі VPN-серверу (172.21.12.0/24) та локального хосту. Зупиняємо локальний systemd-resolved сервіс (якщо його було запущено), додаємо Unbound в автозавантаження та запускаємо:
# systemctl disable systemd-resolved.service
# systemctl stop systemd-resolved
# chown -R unbound:unbound /var/lib/unbound
# systemctl enable unbound
# systemctl start unbound
Перевіримо чи він коректно працює:
$ nslookup www.google.com 172.21.12.1
Server: 172.21.12.1
Address: 172.21.12.1#53
Non-authoritative answer:
Name: www.google.com
Address: 216.58.215.36
Name: www.google.com
Address: 2a00:1450:4007:808::2004
Чудово. Тепер відредагуємо конфігураційний файл WireGuard-клієнта та додамо адресу цього DNS-серверу:
# vim /etc/wireguard/wg0.conf
[Interface]
# client's privatekey
PrivateKey = QLnMSq7IaXks08mka1vr0bplWmUJmoCXErcPz56ykmU=
Address = 172.21.12.2/32
DNS = 172.21.12.1
[Peer]
# server's publickey
PublicKey = 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
Endpoint = <server's public ip address>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
# wg-quick down wg0
# wg-quick up wg0
Тобто всі DNS-дані будуть йти через адресу, яка в свою чергу лежить в адресному просторі WireGuard серверу і доступна лише в тому випадку, якщо VPN-тунель вже піднято.
Перевірити чи витік DNS-даних не відбувається можна за наступним посиланням http://dnsleak.com/.
Останній, але не менш важливий пункт - налаштування фаервола. Для ОС Linux в основному цю роль виконує Iptables або фронтенди на його основі. Дозволимо RELATED трафік на INPUT ланцюжку:
# iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
51820 - UPD-порт, котрий "слухає" WireGuard сервер:
# iptables -A INPUT -p udp -m udp --dport 51820 -m conntrack --ctstate NEW -j ACCEPT
Дозволимо DNS трафік на сервері, якщо запити прийшли із VPN діапазону:
# iptables -A INPUT -s 172.21.12.0/24 -p tcp -m tcp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
# iptables -A INPUT -s 172.21.12.0/24 -p udp -m udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
Та SSH:
# iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
Всі інші пакети в ланцюжку INPUT будуть знищені:
# iptables -A INPUT -j DROP
Наступне правило дозволяє NAT-трансляцію:
# iptables -t nat -A POSTROUTING -s 172.21.12.0/24 -o ens2 -j MASQUERADE
У разі, якщо ці правила будуть вказані напряму, їх потрібно видалити із PostUp та PostDown директив wg0.conf сервера.
Робимо правила постійними:
# apt-get install iptables-persistent
# systemctl enable netfilter-persistent
# netfilter-persistent save
Посилання:
https://www.ckn.io/blog/2017/11/14/wireguard-vpn-typical-setup/
https://www.stavros.io/posts/how-to-configure-wireguard/
https://www.digitalocean.com/community/tutorials/how-to-create-a-point-to-point-vpn-with-wireguard-on-ubuntu-16-04
https://wiki.archlinux.org/index.php/WireGuard
https://www.wireguard.com/netns/
https://arstechnica.com/gadgets/2018/08/wireguard-vpn-review-fast-connections-amaze-but-windows-support-needs-to-happen/
1. SETUP
WireGuard ще не включено до кодової бази ядра Linux і наразі розповсюджується як DKMS-модуль. У якості сервера Wireguard працює лише на Linux, а у якості клієнта - під усіма популярними операційними системами, в т.ч. мобільними, в основному як userspace-імплементація на мові Go.
Для демонстрації можливостей WireGuard ми скористаємось Ubuntu 18.04. Для неї пакети розповсюджуються через ppa-репозиторій. На кожному вузлі, що буде з'єднуватись тунелем, необхідно встановити наступні пакети:
# add-apt-repository ppa:wireguard/wireguard
# apt update
# apt install wireguard
Можливо також потрібно буде встановити пакети-заголовки для ядра:
# apt install linux-headers-$(uname -r) linux-headers-generic
Кожен вузол повинен мати свою пару асиметричних ключів, тож згенеруємо їх на кожному хості:
# cd /etc/wireguard/
# umask 077
# wg genkey | tee privatekey | wg pubkey > publickey
2. HOST-HOST CONNECTION
У першому сценарії побачимо як з'єднувати тунелем 2 вузла між собою. На вузлі, що виступатиме сервером, пишемо наступне:
# vim /etc/wireguard/wg0.conf
[Interface]
# server's privatekey
PrivateKey = MIRmd9owAlnKwRjk2RbzjcGvlssB5qmGP0UW4M/0QmA=
Address = 172.21.12.1/32
ListenPort = 51820
[Peer]
# client's publickey
PublicKey = HsG+rLgPRd18DN4lqBN4jyvgnOMOk57jd9zpHkEkUHs=
AllowedIPs = 172.21.12.2/32
Де 172.21.12.1/32 адреса сервера, котру він матиме в тунелі, 51820 - UDP-порт, що буде очікувати на підключення (має бути дозволений на фаерволі), PrivateKey/PublicKey - приватний/публічний ключ сервера/клієнта, вміст файлу privatekey/publickey, що були згенеровані вище на сервері/клієнті відповідно.
На стороні ж клієнта конфігураційний файл матиме наступний вигляд:
# vim /etc/wireguard/wg0.conf
[Interface]
# client's privatekey
PrivateKey = QLnMSq7IaXks08mka1vr0bplWmUJmoCXErcPz56ykmU=
Address = 172.21.12.2/32
[Peer]
# server's publickey
PublicKey = 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
Endpoint = <server's public ip address>:51820
AllowedIPs = 172.21.12.1/32
PersistentKeepalive = 25
Де, аналогічно, PrivateKey - ключ клієнта privatekey та PublicKey - публічний ключ сервера. Тобто за основу можна взяти наступне: приватний ключ окремої системи не має копіюватись за межі цієї системи.
Мабуть тут буде доречно згадати про те, яким чином в WireGuard працює маршрутизація пакетів (Cryptokey routing). Сенс її полягає в поєднанні публічних ключів та списку IP, що дозволені в тунелі. Кожен мережевий інтерфейс (мається на увазі wg) має приватний ключ та список вузлів (peers), з'єднання з якими може відбуватись. У свою чергу кожен пір має публічний ключ. Публічні ключі використовуються для аутентифікації з'єднань між собою.
Якщо за приклад взяти конфігураційні файли вище (wg0.conf), то все буде виглядати приблизно наступним чином. У конфігурації сервера вказано, що кожен клієнт матиме можливість відправляти пакети до мережевого інтерфейсу сервера з IP адрес (source IPs), що описані в секції AllowedIPs, тобто з 172.21.12.2 (адреса клієнта). Наприклад, якщо пакет буде отримано сервером від вузла HsG+rLgP..., після цього він буде автентифікований і дешифрований, і у разі, якщо його IP адреса буде 172.21.12.2, то пакет буде дозволений на wg0-інтерфейсі і буде маршрутизований далі.
Знову ж, виходячи з конфігурації серверу, якщо мережевий інтерфейс сервера захоче відправити пакет клієнту (peer-у), Wireguard перегляне IP-адресу призначення пакету і порівняє її із кожним списком дозволених адрес кожного піра. У нашому випадку, якщо мережевому інтерфейсу необхідно буде відправити пакет з адресою призначення 172.21.12.2, то сервер зашифрує його використовуючи публічний ключ клієнта HsG+rLgP... і вже потім відправить його на останній відомий публічний ендпоїнт клієнта.
У конфігурації клієнта указано, що сервер може відправляти пакети клієнту з мережевого інтерфейсу із адресою 172.21.12.1 (source IP). Наприклад, якщо пакет буде отримано від вузла 50TO6tBK... (тобто сервера), зможе бути розшифрований та коректно автентифікований і адреса пакета буде 172.21.12.1, то такий пакет буде дозволено на мережевому інтерфейсі клієнта; інакше ж його буде відкинуто.
Також у wg0.conf клієнта вказано, якщо його мережевий інтерфейс захоче відправити пакет серверу, то він спочатку має його зашифрувати лише для сервера із AllowedIPs адресою. Тобто, якщо мережевому інтерфейсу клієнта необхідно відправити пакет на 172.21.12.1, він його зашифрує ключем 50TO6tBK... і потім відправить на останній відомий публічний ендпоїнт сервера.
Саме це і входить до терміну Cryptokey Routing Table: асоціація публічних ключів та дозволених IP-адрес.
Піднімаємо тунель спочатку на сервері, а потім і на клієнті:
# wg-quick up wg0
У результаті чого на сервері буде піднято wg0-інтерфейс із адресою 172.21.12.1, а на клієнті - з адресою 172.21.12.2. Перевіримо роботу тунелю. Для цього запустимо ping на клієнті:
$ ping -c2 172.21.12.1
PING 172.21.12.1 (172.21.12.1) 56(84) bytes of data.
64 bytes from 172.21.12.1: icmp_seq=1 ttl=64 time=39.0 ms
64 bytes from 172.21.12.1: icmp_seq=2 ttl=64 time=37.4 ms
--- 172.21.12.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1ms
rtt min/avg/max/mdev = 37.362/38.189/39.017/0.850 ms
І перевіримо чи приходять пакети на сервері:
# tcpdump -n -i wg0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wg0, link-type RAW (Raw IP), capture size 262144 bytes
12:03:58.892503 IP 172.21.12.2 > 172.21.12.1: ICMP echo request, id 662, seq 1, length 64
12:03:58.892556 IP 172.21.12.1 > 172.21.12.2: ICMP echo reply, id 662, seq 1, length 64
12:03:59.893866 IP 172.21.12.2 > 172.21.12.1: ICMP echo request, id 662, seq 2, length 64
12:03:59.893910 IP 172.21.12.1 > 172.21.12.2: ICMP echo reply, id 662, seq 2, length 64
WireGuard розповсюджується із клієнтськими утилітами для діагностики тунелю.
# wg -h
Usage: wg <cmd> [<args>]
Available subcommands:
show: Shows the current configuration and device information
showconf: Shows the current configuration of a given WireGuard interface, for use with `setconf'
set: Change the current configuration, add peers, remove peers, or change peers
setconf: Applies a configuration file to a WireGuard interface
addconf: Appends a configuration file to a WireGuard interface
genkey: Generates a new private key and writes it to stdout
genpsk: Generates a new preshared key and writes it to stdout
pubkey: Reads a private key from stdin and writes a public key to stdout
You may pass `--help' to any of these subcommands to view usage.
Переглянемо стан роботи тунелю однією із них:
# wg show
interface: wg0
public key: 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
private key: (hidden)
listening port: 51820
peer: HsG+rLgPRd18DN4lqBN4jyvgnOMOk57jd9zpHkEkUHs=
endpoint: <client's public IP address>:49589
allowed ips: 172.21.12.2/32
latest handshake: 22 seconds ago
transfer: 35.78 KiB received, 25.74 KiB sent
Якщо звернутись до початкового конфігураційного файлу клієнта, він має початковий endpoint, тобто публічну IP-адресу та порт Wireguard-сервера. Тож клієнт знатиме на яку адресу відправляти шифровані дані. В конфігурації сервера такий параметр відсутній - сервер знаходить endpoint клієнта за адресою звідки приходять правильно автентифіковані дані. Якщо ж сервер змінить свою адресу - клієнт аналогічно визначить новий endpoint сервера і відповідно змінить конфігурацію. Клієнт та сервер відправляють шифровані дані на останній IP endpoint, котрий коректно дешифрував дані. Саме ця логіка і забезпечує режим роумінгу (roaming) по обидві сторони і це неймовірний функціонал на нестабільних чи динамічних мережах на кшталт мобільних.
На сервері додамо WireGuard в автозавантаження:
# systemctl enable wg-quick@wg0
На клієнті варто виконати аналогічну дію за потреби.
3. ACCESS TO LOCAL NETWORK BEHIND WIREGUARD SERVER
Цього разу спробуємо з'єднати клієнт з сервером та локальною мережею позаду нього. Тобто сервер буде виступати таким собі шлюзом для доступу в приватну мережу (наприклад, офісну) і не більше. Для цього необхідно виконати мінімальні зміни конфігураційних файлів, що були використані в попередньому пункті. На сервері:
# vim /etc/wireguard/wg0.conf
[Interface]
# server's privatekey
PrivateKey = MIRmd9owAlnKwRjk2RbzjcGvlssB5qmGP0UW4M/0QmA=
Address = 172.21.12.1/32
ListenPort = 51820
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -s 172.21.12.0/24 -A POSTROUTING -o ens2 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -s 172.21.12.0/24 -D POSTROUTING -o ens2 -j MASQUERADE
[Peer]
# client's publickey
PublicKey = HsG+rLgPRd18DN4lqBN4jyvgnOMOk57jd9zpHkEkUHs=
AllowedIPs = 172.21.12.2/32
Де ens2 - інтерфейс, що обслуговує внутрішню локальну мережу 10.0.0.0/8. Кожного разу, коли буде піднято інтерфейс wg0 на сервері, буде виконано скрипт PostUp, котрий запустить правило iptables. Це правило активує NAT трансляцію для пакетів із підмережі 172.21.12.0/24 в уже згаданий інтерфейс. Власне нічого особливого в цій логіці - аналогічно транслюються адреси і, наприклад, у OpenVPN.
На клієнті конфігураційний файл має бути наступним:
# vim /etc/wireguard/wg0.conf
[Interface]
# client's privatekey
PrivateKey = QLnMSq7IaXks08mka1vr0bplWmUJmoCXErcPz56ykmU=
Address = 172.21.12.2/32
[Peer]
# server's publickey
PublicKey = 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
Endpoint = <server's public ip address>:51820
AllowedIPs = 172.21.12.1/32, 10.0.0.0/8
PersistentKeepalive = 25
Тут варто звернути увагу на параметр AllowedIPs. Він вказує на те, що клієнт має право приймати пакети із 172.21.12.1/32, 10.0.0.0/8 підмереж.
Перезапустимо WireGuard на сервері, потім на клієнті та перевіримо зв'язок з клієнта:
$ ping -c2 10.22.57.7
PING 10.22.57.7 (10.22.57.7) 56(84) bytes of data.
64 bytes from 10.22.57.7: icmp_seq=1 ttl=58 time=41.8 ms
64 bytes from 10.22.57.7: icmp_seq=2 ttl=58 time=43.4 ms
--- 10.22.57.7 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 41.799/42.618/43.438/0.845 ms
Де 10.22.57.7 - вузол, що лежить в тій же мережі, що і WireGuard сервер. Також не варто забувати про попередню активацію ip forwarding в ядрі, що дозволить пересилати пакети між мережевими інтерфейсами:
# vim /etc/sysctl.conf
...
net.ipv4.ip_forward=1
...
# sysctl -p
4. ACCESS THE INTERNET THROUGH WIREGUARD SERVER
Мабуть найбільш класичний спосіб використання VPN. Тобто дані до WireGuard-серверу будуть додатково шифруватись, а вже після відправлятимуться як трафік із цієї локації. Це підвищить рівень безпеки та приховає справжнє місцезнаходження клієнта. Для цього варто виконати мінімальні зміни в порівнянні з попереднім пунктом. На клієнті правимо параметр AllowedIPs:
# vim /etc/wireguard/wg0.conf
[Interface]
# client's privatekey
PrivateKey = QLnMSq7IaXks08mka1vr0bplWmUJmoCXErcPz56ykmU=
Address = 172.21.12.2/32
[Peer]
# server's publickey
PublicKey = 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
Endpoint = <server's public ip address>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Тобто джерелом пакетів для клієнта можуть бути будь-які IPv4 адреси.
Це все. Перезапустимо WireGuard та перевіримо публічну IP-адресу на клієнті:
$ sudo wg-quick down wg0
$ sudo wg-quick up wg0
$ curl ifconfig.me
<server's public ip address>
Досить цікавий стан справ з маршрутами. У вже згаданому OpenVPN у клієнта у подібній схемі створюється декілька нових маршрутів із яких простежується логіка роутингу трафіку. Щось схоже на це:
# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.8.0.5 128.0.0.0 UG 0 0 0 tun0
0.0.0.0 192.168.1.1 0.0.0.0 UG 100 0 0 eth0
10.8.0.1 10.8.0.5 255.255.255.255 UGH 0 0 0 tun0
10.8.0.5 0.0.0.0 255.255.255.255 UH 0 0 0 tun0
128.0.0.0 10.8.0.5 128.0.0.0 UG 0 0 0 tun0
169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 tun0
public_ip_of_vpn_server 192.168.1.1 255.255.255.255 UGH 0 0 0 eth0
192.168.1.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
Тобто для початкового з'єднання на публічну адресу public_ip_of_vpn_server OpenVPN створює окремий маршрут через попередній гейтвей 192.168.1.1 (транспортний канал), після чого додає 2 окремих маршрути на приватну адресу OpenVPN-серверу (10.8.0.5), що описує доступ на всю IPv4 мережу (0.0.0.0/0).
У разі WireGuard маршрути додаються інакше, використовуючи маркування fwmark. Це стосується тунелей, що були підняті утилітою wg-quick.
5. UNBOUND FOR AVOIDING DNS LEAKS
Для уникнення витоку адрес сайтів, що були відвідані, скористаймось власним кешуючим DNS-сервером, котрий встановимо на VPN-сервер. Цей можливий недолік безпеки має назву DNS Leak. Суть його в тому, що дані до DNS-серверів при певних обставинах можуть прямувати не через VPN-сервер, а напряму, по локальним маршрутам і таким чином відслідковуватись третьою стороною. Це може статись, якщо буде використовуватись локальний DNS-сервер, котрий автоматично надає провайдер по DHCP-протоколу і т.п.
У якості вже згаданого DNS-сервера використаємо Unbound. Установимо його на WireGuard сервер та завантажимо адреси кореневих DNS-серверів:
# apt install unbound unbound-host
# curl -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache
Приберемо попередній вміст конфігураційного файлу Unbound та запишемо наступне:
# vim /etc/unbound/unbound.conf
server:
num-threads: 4
# Enable logs
verbosity: 1
# list of Root DNS Server
root-hints: "/var/lib/unbound/root.hints"
# Use the root servers key for DNSSEC
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# Respond to DNS requests on wg interface only
interface: 172.21.12.1
max-udp-size: 3072
# Authorized IPs to access the DNS Server
access-control: 0.0.0.0/0 refuse
access-control: 172.21.12.0/24 allow
# Not allowed to be returned for public internet names
private-address: 172.21.12.0/24
# Hide DNS Server info
hide-identity: yes
hide-version: yes
# Limit DNS Fraud and use DNSSEC
harden-glue: yes
harden-dnssec-stripped: yes
harden-referral-path: yes
# Add an unwanted reply threshold to clean the cache and avoid when possible a DNS Poisoning
unwanted-reply-threshold: 10000000
# Have the validator print validation failures to the log.
val-log-level: 1
# Minimum lifetime of cache entries in seconds
cache-min-ttl: 1800
# Maximum lifetime of cached entries
cache-max-ttl: 14400
prefetch: yes
prefetch-key: yes
Тобто ми дозволили доступ до DNS-серверу лише з адрес підмережі VPN-серверу (172.21.12.0/24) та локального хосту. Зупиняємо локальний systemd-resolved сервіс (якщо його було запущено), додаємо Unbound в автозавантаження та запускаємо:
# systemctl disable systemd-resolved.service
# systemctl stop systemd-resolved
# chown -R unbound:unbound /var/lib/unbound
# systemctl enable unbound
# systemctl start unbound
Перевіримо чи він коректно працює:
$ nslookup www.google.com 172.21.12.1
Server: 172.21.12.1
Address: 172.21.12.1#53
Non-authoritative answer:
Name: www.google.com
Address: 216.58.215.36
Name: www.google.com
Address: 2a00:1450:4007:808::2004
Чудово. Тепер відредагуємо конфігураційний файл WireGuard-клієнта та додамо адресу цього DNS-серверу:
# vim /etc/wireguard/wg0.conf
[Interface]
# client's privatekey
PrivateKey = QLnMSq7IaXks08mka1vr0bplWmUJmoCXErcPz56ykmU=
Address = 172.21.12.2/32
DNS = 172.21.12.1
[Peer]
# server's publickey
PublicKey = 50TO6tBKW3fWu28f0v3FxVc1oycooD5zlpGd3fbbcjQ=
Endpoint = <server's public ip address>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
# wg-quick down wg0
# wg-quick up wg0
Тобто всі DNS-дані будуть йти через адресу, яка в свою чергу лежить в адресному просторі WireGuard серверу і доступна лише в тому випадку, якщо VPN-тунель вже піднято.
Перевірити чи витік DNS-даних не відбувається можна за наступним посиланням http://dnsleak.com/.
6. FIREWALL CONFIGURATION
Останній, але не менш важливий пункт - налаштування фаервола. Для ОС Linux в основному цю роль виконує Iptables або фронтенди на його основі. Дозволимо RELATED трафік на INPUT ланцюжку:
# iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
51820 - UPD-порт, котрий "слухає" WireGuard сервер:
# iptables -A INPUT -p udp -m udp --dport 51820 -m conntrack --ctstate NEW -j ACCEPT
Дозволимо DNS трафік на сервері, якщо запити прийшли із VPN діапазону:
# iptables -A INPUT -s 172.21.12.0/24 -p tcp -m tcp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
# iptables -A INPUT -s 172.21.12.0/24 -p udp -m udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
Та SSH:
# iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
Всі інші пакети в ланцюжку INPUT будуть знищені:
# iptables -A INPUT -j DROP
Наступне правило дозволяє NAT-трансляцію:
# iptables -t nat -A POSTROUTING -s 172.21.12.0/24 -o ens2 -j MASQUERADE
У разі, якщо ці правила будуть вказані напряму, їх потрібно видалити із PostUp та PostDown директив wg0.conf сервера.
Робимо правила постійними:
# apt-get install iptables-persistent
# systemctl enable netfilter-persistent
# netfilter-persistent save
Посилання:
https://www.ckn.io/blog/2017/11/14/wireguard-vpn-typical-setup/
https://www.stavros.io/posts/how-to-configure-wireguard/
https://www.digitalocean.com/community/tutorials/how-to-create-a-point-to-point-vpn-with-wireguard-on-ubuntu-16-04
https://wiki.archlinux.org/index.php/WireGuard
https://www.wireguard.com/netns/
https://arstechnica.com/gadgets/2018/08/wireguard-vpn-review-fast-connections-amaze-but-windows-support-needs-to-happen/
Немає коментарів:
Дописати коментар