Hlavní navigace

WebSocket jako cesta k úniku z příliš restriktivní sítě

30. 3. 2022
Doba čtení: 7 minut

Sdílet

 Autor: Depositphotos
Přijeli jste do hotelu, máte přístup k místní Wi-Fi, ale firewall vás pustí jen na web? Zkusíte tedy spustit VPN, ale ouha, ani té se nepodaří projít. Zbývá jediná cesta: vytvořit si websocketový tunel.
Co se dozvíte v článku
  1. Když internet = web
  2. Krátce o WebSocket
  3. Tunel pomocí wstunnel
  4. Automatické spouštění
  5. Odbočka: Nginx jako reverzní proxy
  6. WireGuard uvnitř WebSocketu
  7. WebSocket jako cesta ven
  8. Odkazy

Když internet = web

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.

CS24_early

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.

Odkazy

Byl pro vás článek přínosný?

Autor článku

Petr Krčmář pracuje jako šéfredaktor serveru Root.cz. Studoval počítače a média, takže je rozpolcen mezi dva obory. Snaží se dělat obojí, jak nejlépe umí.