Když internet = web
Co se dozvíte v článku
Internet není jen web!
rozčilují se lidé z branže, ale někdy jim to není nic platné. Občas prostě přijedete do míst, kde se provozovatel sítě rozhodl, že bude nejlepší uživatele pustit jen na dva TCP porty a ještě na nich hlídat provoz na vyšších vrstvách. Stává se to zejména v hotelových sítích, ale nejen tam.
Pokud se na takovém místě octnete a chcete dělat něco víc než jen odesílat obrázky koťátek na sociální sítě, máte prostě smůlu. Povolené jsou jen cílové porty 80 a 443 a na druhém zmíněném se ještě navíc hlídá, zda používáte HTTPS. Když se pokusíte tudy posílat třeba provoz na OpenVPN nebo nedej bože zkusíte odeslat UDP pro WireGuard, jste nemilosrdně zaříznuti.
Jedinou cestou ven je tedy web, ale musí přece existovat způsob, jak zneužít webové služby k odesílání libovolných dat, která zabalíme do HTTPS. Výsledek by byl zašifrovaný, vypadal by jako webový provoz a prošel by restriktivními omezeními místní sítě. Taková věc existuje a jmenuje se WebSocket.
Krátce o WebSocket
WebSocket je komunikační protokol, který byl standardizován v roce 2011 v RFC 6455. Protokol je na rozdíl od HTTP plně duplexní (obousměrný) a byl navržen pro efektivnější komunikaci mezi prohlížečem a webovým serverem. Běží na standardních portech 80 a 443 a využívá klasického HTTP či HTTPS spojení obou stran.
Umožňuje udržovat trvalé spojení obou stran a výměnu dat mezi nimi. K úvodnímu navázání spojení se použije klasické TCP na vybraný port, poté se pomocí TLS a HTTP naváže spojení se serverem a následně jsou využity hlavičky Upgrade: WebSocket
a Connection: Upgrade
k přechodu na WebSocket. Pokud server komunikaci podporuje, odpoví stejně a mezi oběma stranami vzniká tunel pro přenos dat.
Vše je samozřejmě komplikovanější, přidávají se hlavičky o verzích, je možné celou komunikaci dále šifrovat na aplikační úrovni a je možné využít oboustrannou autentizaci. Nám teď bude stačit jen tento základ, který nám umožní otevřít si tunel do světa odkudkoliv, kde máme povolený provoz na HTTPS.
Tunel pomocí wstunnel
WebSocket je běžně využíván webovým prohlížečem a webovým serverem, existují ale i implementace v dalších utilitách. My můžeme velmi dobře využít projekt wstunnel, který vytváří tunelovací utilitu pro Linux (AArch64, x86), Windows a macOS.
Mějme dva stroje, jeden je za restriktivním firewallem, devatery NATy a sedmery proxy servery a může jen na porty 80 a 443 pomocí HTTP(S). Druhý je veřejně na internetu, máme ho pod kontrolou a chceme si skrze něj natáhnout třeba VPN pomocí WireGuardu, který vždy běží v režimu UDP.
Začneme tím, že si na oba stroje stáhneme utilitu wstunnel
. Ta je napsaná v Haskellu a autor rovnou na GitHubu vystavuje statické binárky pro zmíněné platformy. Instalace tedy spočívá v prostém stažení souboru na obou stranách:
# wget https://github.com/erebe/wstunnel/releases/download/v4.1/wstunnel-x64-linux # chmod +x wstunnel-x64-linux
Server
Nejprve spustíme utilitu v režimu serveru, tedy na stroji, kam se budeme připojovat. Na řádce určíme, na jakém TCP portu má wstunnel
poslouchat a kam má po vybalení provoz zase posílat – v našem případě to bude localhost, kde poběží skutečná služba.
# ./wstunnel-x64-linux --server wss://0.0.0.0:443 -r 127.0.0.1:51820 Starting server with opts WsServerInfo {useTls = True, host = "0.0.0.0", port = 443} WAIT for TLS connection on 0.0.0.0:443
Pomocí utility ss (socket statistics) můžeme jednoduše ověřit, že utilita poslouchá na vybraném portu:
# ss -tulpn Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process tcp LISTEN 0 2048 0.0.0.0:443 0.0.0.0:* users:(("wstunnel-x64-li",pid=539,fd=23))
Klient
V našem modelovém příkladu budeme tunelovat UDP, musíme o tom klientovi říct. Kromě toho mu opět nahlásíme, kde má poslouchat a pak také, kam má provoz směřovat – kde je druhý konec tunelu.
# ./wstunnel-x64-linux -u --udpTimeoutSec -1 -L 127.0.0.1:51820:127.0.0.1:51820 wss://example.com/ WAIT for datagrames on 127.0.0.1:51820
Tahle řádka snese podrobnější vysvětlení: -u
znamená UDP, --udpTimeoutSec
nastavuje timeout pro zavření tunelu ( -1
znamená nikdy), -L
nastavuje místní a vzdálený port pro směřování komunikace z obou stran tunelu a konečně pak uvádíme doménové jméno či IP adresu našeho serveru.
Opět můžeme snadno ověřit, že utilita poslouchá, ovšem tentokrát na UDP:
# ss -tulpn Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process udp UNCONN 0 0 127.0.0.1:51820 0.0.0.0:* users:(("wstunnel-x64-li",pid=5307,fd=11))
V tuhle chvíli máme natažené HTTPS spojení mezi dvěma body, přičemž utility se starají o příjem komunikace na jedné straně (vždy localhost:51820), balení do WebSockets, HTTPS a TCP a odesílání na druhou stranu. Z hlediska mezilehlých zařízení to vypadá jako standardní komunikace s webovým serverem.
Automatické spouštění
Na experimenty vám bude stačit takovéto spouštění z řádky, ale pokud chcete celou věc posunout do produkce, budete muset zajistit automatický start. Protože na většině distribucí dnes máme systemd, stačí vytvořit nový unit soubor a tím vznikne nová služba.
Do souboru /etc/systemd/system/wstunnel.service
stačí připsat následující konfiguraci:
[Unit] Description=WebSocket tunel pro WireGuard After=network.target [Service] Type=simple User=nobody ExecStart=/usr/local/bin/wstunnel -u --udpTimeoutSec -1 -L 127.0.0.1:51820:127.0.0.1:51820 wss://example.com/ Restart=no [Install] WantedBy=multi-user.target
Řádku ExecStart
samozřejmě upravte dle svých požadavků. Teď bude už jen stačit:
# systemctl --now enable wstunnel
Odbočka: Nginx jako reverzní proxy
Port 443 je obvykle využíván pro webové servery a jak známo, na jednom portu může vždy poslouchat pouze jeden proces. Pokud máme server vyhrazený pouze pro WebSocket, nemusí nás to trápit. Pokud ale chceme tunelovat přes svůj skutečný webový server, nastává kolize.
Můžeme to ovšem vyřešit velmi elegantně a jednoduše, pokud například používáme webový server Nginx. Ten totiž umí běžet zároveň v režimu reverzní proxy, takže jen požadavky na určitou konkrétní vybranou URL pošle k vyřízení na jiný server. Do konfigurace některého z našich virtuálních hostitelů přidáme další sekci location
a v ní nastavíme reverzní proxy:
location /TajnaAdresaProxy/ { proxy_pass http://127.0.0.1:33344; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_connect_timeout 10m; proxy_send_timeout 10m; proxy_read_timeout 90m; send_timeout 10m; }
Takto nastavený Nginx bude požadavky směrované na tajnou adresu otáčet na localhost na port 33344 (vyberte si dle libosti). Na tomto portu musíme spustit wstunnel
a vypnout na něm šifrování – to za nás už zajistí Nginx. Šifrování vypneme tak, že místo wss
použijeme pouze ws
.
# wstunnel --server ws://127.0.0.1:33344 --restrictTo=127.0.0.1:51820
Klient pak bude nastaven stejně jako dřív, jen je třeba mu přidat parametr --upgradePathPrefix
následovaný naší tajnou cestou na webovém serveru. Klient se pak připojí k Nginx, požádá o správnou cestu a webový server ho propoji se správným WebSocketem. Pokud přijde běžný klient s prohlížečem, je mu zobrazen normální web.
WireGuard uvnitř WebSocketu
Teď už zbývá jen takto vytvořený tunel nějak využít. V našem modelovém příkladu protáhneme WebSocketem UDP komunikaci pro VPN WireGuard. Úplně stejně tam ale můžete rovnou poslat SSH, OpenVPN nebo cokoliv dalšího. My si ukážeme nejjednodušší příklad, kdy prostě veškerý provoz z klienta pošleme rozhraním VPN na server.
Nejprve si na obou stranách vygenerujeme pár klíčů, které budeme poté vkládat do konfigurace:
# wg genkey | tee wg-private.key | wg pubkey > wg-public.key
Poté na klientovi připravíme konfiguraci do /etc/wireguard/wg0.conf
:
[Interface] PrivateKey = WJa0RRvCygczq9Gn46p5KfVb2wkePF1u9/3eBs2iPF4= Address = 10.200.0.2/24 DNS = 1.1.1.1 DNS = 8.8.8.8 [Peer] PublicKey = 1bTuV2B5eEZw1rARtkFJWF3i2RQUN2TxxWtrISdQFDI= AllowedIPs = 0.0.0.0/0, ::0 Endpoint = 127.0.0.1:51820
První klíč je soukromý klíč klienta, ten druhý je naopak veřejný klíč serveru. Všimněte si, že se na posledním řádku připojujeme sami k sobě. Ven to přeci nejde a musíme použít už sestavený WebSocket.
Na serveru to uděláme podobně, opět v /etc/wireguard/wg0.conf
:
[Interface] PrivateKey = qC1XsKhoFtAxQthw/0B7e45HJJPbE8QhAc/IrojKTVo= Address = 10.200.0.1/24 ListenPort = 51820 [Peer] PublicKey = JWFY879EoMwdcGjpGEVdrmnCwAxi8pjt3QM0obY57C0= AllowedIPs = 10.200.0.2/32
Na obou stranách pak spustíme WireGuard pomocí:
# wg-quick up wg0
Obě strany na sebe začnou vidět a my jsme schopni se z klienta připojit na server na adrese 10.200.0.1
a přenášet s ním libovolná data. Samozřejmě můžeme spustit NAT a posílat pak přes server veškerou internetovou komunikaci, ale to už je nad rámec tohoto článku.
WebSocket jako cesta ven
WireGuard byl jen modelový příklad, který ukázal, jak je možné tunel vytvořený pomocí WebSocketu použít. Jakmile si takto otevřeme cestu ven z restriktivní sítě, máme pole působnosti otevřené a můžeme tímto univerzálním tunelem protahovat jak jednotlivé aplikační protokoly, tak i celá spojení pomocí libovolné VPN.
Z našeho síťového hlediska máme propojené dva TCP nebo UDP porty, podobně, jako bychom je tunelovali třeba přes SSH. Vnější svět ale vidí jen normální šifrované HTTPS spojení, přes které tečou obousměrně data.