Networking for beginners. Part 20: VPN and proxies

All parts of the series “Networking for beginners”

In part nineteen we took apart IPsec and IKEv2 as protocols: packets, exchanges, keys. Today — what they turn into on the user's machine when they press "Connect", and how that differs from the other way of sending traffic through someone else's server — a proxy. The word "VPN" has been worn down beyond recognition: it is used for a tunnel between offices and for a button in a browser, and these are different things with different mechanics. This series is built on layers, and it is the layers that give the precise answer: a VPN works at the network layer, a proxy at the transport or application layer. Everything else follows from that.


What a VPN is

VPN, Virtual Private Network — a virtual network on top of another network. In the terms of this series: a tunnel, that is, a protocol inside a protocol, like PPPoE inside Ethernet in part four or a frame inside UDP in VXLAN, usually with encryption. A tunnel can be L2 (frames travel inside, as in VXLAN) or L3 (IP packets travel inside, as in IPsec); for remote access it is almost always L3.

The main practical property: from the operating system's point of view, a VPN is a network interface and routes. After connecting, the system gets an interface tun0, ipsec0, wg0 or ppp0 with an address from "that" network, and the routing table gets the prefixes that must be sent into it. Applications write to sockets as usual; the stack decides from the routing table that a packet goes to tun0; the VPN client picks it up there, encrypts it and sends it through the real interface to the server; the server decrypts it and sends it on, like a router. Not a single program on the machine knows anything has changed, and it works for any protocol — TCP, UDP, ICMP, anything over IP.

Two kinds:

  • site-to-site — a tunnel between two gateways; the hosts behind them do not take part at all, for them "that" network is just one more route via their router. This is how offices are connected.
  • remote access — a client on a laptop or phone connecting to a gateway. More on it below.

IKEv2 remote access, step by step

An employee in a café connects to the corporate network. The laptop got the address 192.168.1.10 from the café, the VPN gateway is vpn.example.com, the corporate network is 10.0.0.0/8.

1. The handshake. The client resolves vpn.example.com to 203.0.113.5 and goes through IKE_SA_INIT and IKE_AUTH from the previous part: the server presents a certificate with the name vpn.example.com, the client a login and password via EAP-MSCHAPv2. In IKE_AUTH the client asks for configuration (CP): "give me an address, DNS", and proposes traffic selectors 0.0.0.0/0 on both sides — "I want to protect everything".

2. The server answers. CP: internal address 10.0.5.17, DNS 10.0.0.53, domain example.com. Selectors: it either keeps 0.0.0.0/0 (full tunnel) or narrows them to 10.0.0.0/8 (split tunnel). A pair of Child SAs is created.

3. The client configures the system. Here is what changes in ip addr and ip route:

$ ip addr show ipsec0                    # or an address on the same eth0 in policy-based implementations
    inet 10.0.5.17/32 scope global ipsec0

$ ip route
default via 192.168.1.1 dev wlan0 metric 600          # still there
203.0.113.5 via 192.168.1.1 dev wlan0                 # (a) host route to the VPN server
10.0.0.0/8 dev ipsec0                                 # (b) corporate network — into the tunnel

For a full tunnel, instead of (b) there will be 0.0.0.0/0 dev ipsec0 with a better metric than the café's, or two routes 0.0.0.0/1 and 128.0.0.0/1: they are longer than /0 and, by the longest-prefix rule, beat the old default without removing it. Route (a) is mandatory: packets to the VPN server itself must go around the tunnel, otherwise an encrypted packet would enter the tunnel, be encrypted again, and so on forever.

DNS: in resolv.conf or systemd-resolved, 10.0.0.53 comes first, with the search domain example.com.

4. A packet to a corporate server. An application connects to 10.0.0.42:443:

 application  sendto(10.0.0.42:443)
      │
 stack: ip route get 10.0.0.42 → dev ipsec0, src 10.0.5.17
      │
 ┌────┴──────────────────────────────────────┐
 │ IP 10.0.5.17 → 10.0.0.42 │ TCP 443 │ TLS  │   ← what the application sees
 └────┬──────────────────────────────────────┘
      │ xfrm: policy "10.0.0.0/8 → protect", SA found
 ┌────┴────────────────────────────────────────────────────────────────────────────┐
 │ IP 192.168.1.10 → 203.0.113.5 │ UDP 4500 │ ESP spi, seq │ ░░ all of the above ░ │  ← what the café sees
 └────┬────────────────────────────────────────────────────────────────────────────┘
      │ dev wlan0 → café gateway → café NAT → internet → 203.0.113.5
      ▼
 server: decrypt, check selectors, route 10.0.5.17 → 10.0.0.42 into the corporate network

The café and the ISP see a single UDP flow to port 4500: neither the address 10.0.0.42, nor port 443, nor the content. The server 10.0.0.42 sees a connection from 10.0.5.17 — an address from its own network — and replies to it; the reply follows the corporate network's routes to the VPN gateway, which wraps it in ESP to 192.168.1.10 (more precisely, to the external address of the café's NAT that the client came from), and the laptop's stack decrypts it and hands it to the application.

5. A packet to the internet depends on the mode.


Full and split tunnel

Split tunneling — only the corporate prefixes go into the tunnel, everything else goes directly through the café. It is faster, doesn't load the corporate link with YouTube videos, and the server itself sets the list via selectors or the INTERNAL_IP4_SUBNET CP attribute. The price is two ways out at the same time, and hence leaks:

  • DNS. A query for intranet.example.com may go not to 10.0.0.53 but to the café's DNS, if the client failed to set up "this domain via this server, the rest as before". The café learns which internal names you look up, and the name doesn't resolve. Windows, with several interfaces and DNS servers on each, is especially inventive here.
  • IPv6. The tunnel is set up for IPv4 only, but the café has IPv6 (part thirteen). Everything that resolves to AAAA goes around the tunnel, in the clear. The client must either carry IPv6 too or block it while connected.
  • Applications that know their addresses. WebRTC in the browser (the calling mechanism from part nine) dutifully lists all of the machine's local addresses, including the café's one, to whoever it connects to.

Full tunnel — everything goes into the VPN. The café sees one encrypted flow and nothing else; websites see the VPN gateway's address. But all internet traffic goes through the corporate link and, if the VPN gateway is far away, with the corresponding latency. And even here there is a moment before connecting and after a drop when traffic flows in the clear. For that there is the kill switch: a firewall rule "let nothing out through wlan0 except packets to the VPN server", which stays until the VPN is explicitly disconnected.

Commercial "VPN services" are remote access with a full tunnel, where the "corporate network" is the internet behind the service provider's server. The mechanics are the same; the only thing that changes is who sees your traffic decrypted: not the café, but the service operator.


Other VPN protocols

IKEv2/IPsec is what is built into Windows, macOS, iOS and Android without extra clients. The rest, so it is clear what the settings say:

  • WireGuard — a tunnel over UDP with fixed cryptography (Curve25519, ChaCha20-Poly1305): each peer has a public key, each connection has a list of allowed prefixes per peer (which double as routes), and there is no handshake with algorithm negotiation. The wg0 interface lives in the Linux kernel, a few thousand lines of code versus hundreds of thousands for IPsec, and a client moves between addresses on its own. Thanks to its simplicity it is now the fastest and increasingly preferred; how to set up your own server and connect Windows and Android is covered in a separate article.
  • OpenVPN — a tunnel inside a TLS session over UDP or TCP, a user-space process and a tun interface. Older and slower, but it gets through where UDP 500/4500 is closed, because it can use TCP 443 and looks like HTTPS.
  • L2TP/IPsec — two things at once: L2TP, a tunnel for PPP frames over UDP 1701, protected by IPsec in transport mode. Inside is the same PPP from part four with authentication and IPCP. Built into older Windows, it lives on by inertia.
  • SSTP — PPP inside TLS on port 443, Microsoft's variant for getting through firewalls.
  • PPTP — PPP inside GRE with MPPE encryption, broken for good, do not use.

They are all the same in the main thing: an interface plus routes, traffic of all applications.


Proxies

A proxy is an intermediary at the transport or application layer. The application does not send a packet to the recipient; it establishes a connection to the proxy and asks it to connect to the recipient, and from then on the proxy shuffles bytes between the two connections. The operating system, the routing table and the interfaces do not change. Only the application it is configured in uses the proxy.

HTTP proxy and CONNECT

A browser with a proxy proxy.example.com:3128 configured, for plain HTTP (the protocol for requesting pages, where a request is a text line "method, address, version"), sends the proxy a request with the full URL:

GET http://www.example.com/page HTTP/1.1
Host: www.example.com

The proxy goes to www.example.com itself, gets the response and hands it to the browser, caching, filtering and logging anything it wants along the way: it reads the content.

For HTTPS there is nothing to read, and for it there is the CONNECT method:

CONNECT www.example.com:443 HTTP/1.1
Host: www.example.com:443
Proxy-Authorization: Basic dXNlcjpwYXNz

HTTP/1.1 200 Connection established

<from here on, in the same TCP connection — raw TLS bytes between the browser and the site>

The proxy opens TCP to www.example.com:443, answers 200 and from that moment simply forwards bytes in both directions. TLS is established through it, between the browser and the site; the proxy sees only the name and port from CONNECT. This is how most corporate proxies work, this is how a browser reaches HTTPS through them, and the same CONNECT can carry anything over TCP, SSH for example.

In curl -v -x http://proxy.example.com:3128 https://www.example.com the first lines are exactly this CONNECT and 200.

A transparent proxy is the same HTTP proxy, but the application does not know about it: the router redirects all traffic to port 80 into the proxy, which parses requests by the Host header. With HTTPS it no longer works transparently: without substituting certificates, the proxy only sees the server name at the start of TLS.

A reverse proxy is the same program on the other side: it stands in front of servers, accepts requests from all clients and distributes them among backends. That is nginx or Traefik in front of a website, and it has nothing to do with "sending my traffic through an intermediary"; it is mentioned so the word doesn't confuse you.

SOCKS5

SOCKS, RFC 1928, is a proxy one layer below HTTP: it doesn't understand what's inside and simply connects. The exchange:

 client → proxy:   05 02 00 02            version 5, I support two auth methods: none and login/password
 proxy → client:   05 00                  I choose "no authentication"
 client → proxy:   05 01 00 03 0f www.example.com 01 bb
                   │  │  │  └ address: type 3 — domain name of length 15, port 443
                   │  │  └── reserved
                   │  └── command 1 — CONNECT
                   └── version
 proxy → client:   05 00 00 01 <address the proxy connected from> <port>
                      └ status 0 — success
 then — raw bytes in both directions

Two properties that HTTP CONNECT lacks. First, SOCKS5 accepts the address as a name, and then it is the proxy, not the client, that resolves it: the name doesn't go to the local DNS, which closes that very DNS leak; in curl this is --socks5-hostname, in browsers "proxy DNS when using SOCKS". Second, there is the UDP ASSOCIATE command, which allows proxying UDP as well; implementations are few, but they exist.

The most accessible SOCKS proxy is ssh -D 1080 user@server: SSH, the encrypted remote terminal protocol, opens port 1080 on the local machine, and everything an application sends into it via SOCKS travels inside the SSH session and exits to the internet from the server. Between the client and the server there is encrypted SSH; beyond it, ordinary traffic on behalf of the server.


How a VPN differs from a proxy

VPN Proxy
Layer L3: interface and routes L4 (SOCKS) or L7 (HTTP): a connection to an intermediary
Who uses it everything on the machine, without knowing it only the applications where the proxy is configured
Protocols inside anything over IP: TCP, UDP, ICMP TCP; UDP only with SOCKS5, and rarely
Encryption part of the protocol (IPsec, WireGuard, TLS in OpenVPN) none by default: HTTP proxies and SOCKS are in the clear unless wrapped in TLS or SSH
What the intermediary sees all packets decrypted: addresses, ports, content unless TLS protects it destination addresses; content of plain HTTP; for HTTPS only the name and port
What the local network (café) sees one encrypted flow to the VPN server connections to the proxy; content if the proxy is not over TLS; DNS queries if the client resolves
What the website sees the VPN server's address the proxy's address
DNS through the tunnel (full) or by rules (split) the client's, except SOCKS5 with names
Setup a client with rights over interfaces and routes a line in the application's settings or the https_proxy variable
Where it runs in the kernel or with its involvement in user space
What breaks MTU 50–80 bytes smaller; split tunnel leaks applications without proxy support; UDP

Two practical conclusions.

A "VPN" in a browser extension is a proxy. An extension cannot create an interface or change routes; it configures an HTTPS or SOCKS proxy in the browser. Only that browser's traffic goes through it; other programs and other browsers bypass it. For a website in that browser there is no difference; for the picture "my whole device is behind a VPN" there is.

Neither is anonymity. The VPN server and the proxy see where you go and can log it. They move trust from the local network and the ISP to the server's operator. If the goal is for nobody to link a request to you, you need chains of intermediaries, as in Tor, where each node knows only its neighbors, and that is a separate story.

When to use which: access to a whole remote network from all programs — VPN; one program through a different exit — proxy; getting past a firewall that only lets HTTPS through — a proxy over TLS on 443 or a VPN that can pretend to be HTTPS (OpenVPN on TCP 443, SSTP); between offices — site-to-site VPN, and no proxies.


What the ISP sees

From the table above it is easy to conclude "a VPN encrypts everything, so the ISP knows nothing". That is wrong. It really doesn't read the content, but it sees that you are using a VPN or a proxy, and in some cases what exactly goes through them. The ISP sits on the path of every packet, and it has DPI for this — Deep Packet Inspection: equipment that looks not only at the IP and TCP headers but into the data, recognizes protocols and can let a connection through, slow it down or cut it (usually with a forged TCP RST — a "reset the connection" packet sent on behalf of one of the parties).

Without a VPN or proxy, even when everything is HTTPS, the ISP sees:

  • addresses and ports — whom you connect to, how much and when you transfer;
  • DNS — ordinary queries (part twelve) travel over UDP in plain text: the name of every site you open, and a response that can be spoofed;
  • the site name in TLS — in the first handshake message, the ClientHello (part twenty-three), the browser writes the server name in plain text in the SNI field (Server Name Indication): the server needs to know which certificate to present. Pages and data are encrypted, but which site you came to is visible, and blocking "by name" works exactly this way;
  • all the content of plain HTTP — pages, forms, files.

With a VPN, the ISP sees one encrypted flow to the VPN server's address — but recognizes the protocol by the shape of that flow:

  • IKEv2 uses UDP 500 and 4500; the first IKE_SA_INIT message begins with the initiator's eight-byte SPI, eight zero bytes of the responder's SPI and the version number 0x20; ESP is a separate IP protocol, 50;
  • the first WireGuard message is a UDP packet of type 1 and exactly 148 bytes long, the reply is type 2 and 92 bytes;
  • OpenVPN starts with the opcode P_CONTROL_HARD_RESET_CLIENT, and over TCP 443 it is preceded by a two-byte length — it looks nothing like real TLS;
  • the server address of a commercial VPN service is often already on lists: thousands of users go to the same address.

So the ISP knows you are connected to a VPN, and it may leave it at that — or it may cut such connections. It cannot see what's inside the tunnel; but if the VPN itself is forbidden, the fact of connecting is enough.

With a proxy the picture is worse:

  • an HTTP proxy without TLS is completely open: for plain HTTP the ISP sees full URLs and content, for HTTPS the line CONNECT www.example.com:443 with the site name, followed by the SNI as well;
  • SOCKS5 — the 05 02 00 02 handshake is recognizable by its first bytes, and the destination address, name or IP, goes in plain text;
  • ssh -D — the SSH session itself is encrypted, but the protocol is visible from the very first line (SSH-2.0-…), and a long SSH session with a lot of traffic to one server is a telltale sign of a tunnel.

Even without decryption, signs remain:

  • the client fingerprint — the ClientHello of Chrome, Firefox and a homemade program differ: the order of ciphers, the set and order of extensions. A hash fingerprint is computed from these fields (the best-known methods are JA3 and JA4), and an unusual fingerprint is suspicious in itself;
  • statistics — packet sizes and intervals, connection volume and duration. For example, when HTTPS runs inside an encrypted tunnel, the first packets of the tunnel repeat the sizes of the inner TLS handshake, and this pattern differs from an ordinary visit to a website;
  • active probing — having become suspicious of a server, the DPI connects to it itself and looks at what it answers: an ordinary web server, a proxy, or silence on a "wrong" request.

So if the goal is not only to hide the content but also to keep anyone from telling that you use a tunnel at all, encryption is not enough: the tunnel has to look like the most ordinary traffic, that is, like HTTPS to a big website, and behave like such a site when probed. This is how protocols built specifically against DPI work; and it also shows why even they are not almighty: no disguise gets around blocking by the server's address. How such a proxy works and how to set one up is covered in a separate article, "Your own proxy against DPI: Xray, VLESS and REALITY".


Hands-on

Before and after connecting the VPN — what changed in the system:

$ ip addr | grep -A2 'tun\|wg\|ipsec'
$ ip route
$ ip route get 10.0.0.42            # where a packet to the corporate network will go
$ ip route get 203.0.113.5          # and to the VPN server itself — must go around the tunnel
$ resolvectl status                 # which DNS servers and for which domains

WireGuard and some clients use policy routing: the routes live in a separate table, and an ip rule rule sends everything there except the tunnel's own packets (marked with an fwmark). Then ip route shows the old picture, while ip rule and ip route show table all show the real one.

Leaks: run dig intranet.example.com and watch in tcpdump which server the query went to and over which interface; curl -6 https://example.com — does IPv6 go around the tunnel; ip route get 2001:db8::1.

Proxies in action:

$ curl -v -x http://proxy.example.com:3128 https://www.example.com      # you will see CONNECT and 200
$ ssh -D 1080 -N user@server.example.com &                              # SOCKS on 1080
$ curl --socks5-hostname 127.0.0.1:1080 https://www.example.com         # the server resolves the name

and tcpdump on the local interface will show the difference: with a VPN — only ESP or UDP to a single address; with a proxy — a TCP connection to the proxy with CONNECT in the clear (if the proxy has no TLS) or an SSH session.


Key takeaways

  • A VPN is a network-layer tunnel: an interface and routes, all applications, any protocols over IP, encryption built in. IKEv2 remote access: address and DNS via CP, prefixes via selectors, a host route to the server around the tunnel.
  • A full tunnel hides everything from the local network; a split one is faster but leaks through DNS, IPv6 and WebRTC. A kill switch closes the windows before and after.
  • WireGuard, OpenVPN, L2TP/IPsec, SSTP — the same interface and routes with different packaging; PPTP — no.
  • A proxy is an intermediary for connections: HTTP with a full URL or CONNECT for TLS, SOCKS5 with name resolution on the proxy side and UDP; it works only for configured applications, and encryption is separate.
  • A "VPN" in a browser is a proxy for one browser. Neither a VPN nor a proxy is anonymity: they change who sees your traffic.
  • The ISP doesn't read the tunnel's content, but sees addresses, DNS, the site name in SNI, and recognizes VPNs and proxies by the shape of the traffic: IKEv2, WireGuard, OpenVPN and SOCKS5 have recognizable first packets, and an HTTP proxy without TLS is completely open. Hiding the very fact of a tunnel is only possible by disguising it as ordinary HTTPS.

Next — Part 21: OSPF: how routers within one network learn about each other and about all subnets on their own, build a shared map and compute shortest paths over it. Hello and adjacency, LSAs and the link-state database, Dijkstra's algorithm, link costs, areas and what happens when a cable is cut.

Only registered users can leave a comment.

Sign in or register to leave a comment.