Skip to content

What is a VPN?

💡
Before you start

A Linux terminal. The lab builds and destroys a private network inside a single command, so nothing on your system is changed and there is nothing to undo.

🔴 You do not need a VPN, a subscription, or root. That is the point: you build the part a VPN installs, and see exactly what it does and does not cover.

macOS and Windows do not have network namespaces, so steps 2–5 will not run there. The outputs are printed in full below, so the lesson still lands — and step 1 works everywhere with the alternatives noted.

What is a VPN?

A VPN (Virtual Private Network) creates an encrypted tunnel between your device and a VPN server. All your internet traffic passes through this tunnel, hiding your activity from your ISP and making it appear as if you are browsing from the VPN server's location.

Think of it like mailing a letter inside a sealed, opaque envelope instead of on a postcard. Your mail carrier (ISP) can see the envelope exists but cannot read the contents.

What VPNs Protect

  • Hides your IP address from websites you visit
  • Encrypts traffic on public WiFi networks (cafes, airports, hotels)
  • Prevents ISP monitoring of your browsing activity
  • Bypasses geographic restrictions on content
  • Protects against some network attacks on untrusted networks

What VPNs Do NOT Protect

⚠️
VPNs are not a silver bullet

A VPN does not make you anonymous or protect against all threats. Understand the limitations.

  • Does not prevent tracking cookies or browser fingerprinting
  • Does not protect against malware or phishing
  • Does not make you anonymous if you log into personal accounts
  • Your VPN provider can see your traffic instead of your ISP (you are shifting trust, not eliminating it)
  • Does not protect data you willingly share with websites

VPN Protocols

The protocol determines how the encrypted tunnel is built. Here are the most common:

WireGuard Modern, fast, and lightweight. Uses state-of-the-art cryptography. Recommended for most users.
OpenVPN Battle-tested and widely supported. Slightly slower than WireGuard but very reliable and configurable.
IKEv2/IPsec Good for mobile devices as it handles network switching (WiFi to cellular) smoothly.
PPTP Outdated and insecure. Never use this protocol.

When to Use a VPN

  • Public WiFi: Always use a VPN on untrusted networks
  • ISP privacy: When you do not want your ISP logging your browsing history
  • Traveling: Protect your traffic on hotel and airport networks
  • Accessing restricted content: When legitimate content is geo-blocked in your location
💡
Do you need a VPN at home?

If you trust your ISP and are on your own network, a VPN is less critical at home. It adds latency and may slow your connection. Evaluate your personal threat model.

Choosing a VPN Provider

Not all VPN providers are trustworthy. Look for:

  • No-logs policy: The provider should not store records of your activity (verified by independent audits)
  • Jurisdiction: Providers based in privacy-friendly countries are preferable
  • Open-source clients: Allows security researchers to verify the code
  • WireGuard or OpenVPN support: Avoid providers using proprietary protocols only
  • Paid service: Free VPNs typically monetize your data. If the product is free, you are the product

Now Do It Yourself: Build a VPN's Routing Table Without a VPN

A VPN is sold as an encrypted tunnel, which is true and not very useful for deciding what it protects. What it actually does is change one line in your routing table. You can build that line yourself in about ten minutes — no VPN, no subscription, no root — and the two limitations that matter most become obvious rather than theoretical.

This lab is Linux-only: it uses network namespaces, which macOS and Windows do not have. Every output below was produced by running these exact commands. Nothing connects to the internet — ip route get is a kernel lookup, not a connection, and the namespace has no route to anywhere real.

1
See the door your traffic leaves by

Go: open a terminal on Linux. (On macOS use netstat -rn; on Windows PowerShell use Get-NetRoute. The rest of this lab is Linux-only — it builds a throwaway network to experiment in.)

Do: ask the kernel which route it would use to reach a public address. This sends no packet — it is a lookup, not a connection:

ip route show
ip route get 1.1.1.1

You should see: your routes, then one line naming the interface and gateway that would carry the traffic. On the machine this was written on:

default via 192.168.1.1 dev enp2s0 proto static metric 100
default via 192.168.1.1 dev wlp3s0 proto dhcp src 192.168.1.104 metric 600
192.168.1.0/24 dev enp2s0 proto kernel scope link src 192.168.1.115 metric 100
192.168.1.0/24 dev wlp3s0 proto kernel scope link src 192.168.1.104 metric 600

1.1.1.1 via 192.168.1.1 dev enp2s0 src 192.168.1.115 uid 70004
    cache

Yours will differ — the gateway and interface names are whatever your network uses.

Note there are two default routes here, because this machine has both ethernet and wifi plugged in. They are not a conflict: metric 100 beats metric 600, so ethernet wins, and the final lookup confirms it chose enp2s0. Lowest metric wins — remember that, because it is how a VPN takes over in step 3.

That gateway is your router, and everything beyond it is your ISP. This single line is what a VPN changes. Not your browser, not your accounts — the route.

If not: ip: command not found means you are not on Linux, or iproute2 is missing (sudo apt install iproute2). If ip route get prints RTNETLINK answers: Network is unreachable, you have no network connection — the rest of the lab still works, because it builds its own.

2
Build a throwaway network with no VPN and no root

Go: the same terminal.

Do: create an isolated network namespace — a private, empty network that exists only inside this command and vanishes when it exits. Nothing on your real system is touched. Paste this as one block:

unshare --user --map-root-user --net -- sh -c '
ip link add dummy0 type dummy
ip addr add 10.8.0.2/24 dev dummy0
ip link set dummy0 up
echo "=== BEFORE the VPN route ==="
ip route show
'

You should see: one route and no default. This is a machine that can talk to its own little network and nowhere else:

=== BEFORE the VPN route ===
10.8.0.0/24 dev dummy0 proto kernel scope link src 10.8.0.2

10.8.0.2 is deliberately the kind of address VPN clients hand out. You have just built the interface half of a VPN — without a VPN.

If not: unshare: unshare failed: Operation not permitted means unprivileged user namespaces are disabled on your system. On Debian/Ubuntu check with sysctl kernel.unprivileged_userns_clone. You can still read the rest; the outputs below are complete.

3
Add the VPN's route and watch traffic switch

Go: the same terminal.

Do: run it again with one line added — the default route a VPN client installs when it connects:

unshare --user --map-root-user --net -- sh -c '
ip link add dummy0 type dummy
ip addr add 10.8.0.2/24 dev dummy0
ip link set dummy0 up
ip route add default via 10.8.0.1 dev dummy0
echo "=== AFTER adding the VPN default route ==="
ip route show
echo
echo "=== which route wins for a public address? ==="
ip route get 1.1.1.1
'

You should see: a default route appears, and the lookup for a public address now goes through it:

=== AFTER adding the VPN default route ===
default via 10.8.0.1 dev dummy0
10.8.0.0/24 dev dummy0 proto kernel scope link src 10.8.0.2

=== which route wins for a public address? ===
1.1.1.1 via 10.8.0.1 dev dummy0 src 10.8.0.2 uid 0
    cache

That is the entire mechanism. A VPN is one route with a better claim on your traffic than the old one. There is no magic and no anonymity in it — packets that used to go to your router now go to the VPN server instead, which sees exactly what your router used to see.

If not: if ip route get still names your real interface, the ip route add default line did not run — check it is inside the quoted block and that you pasted the whole thing.

4
Find the leak: routing moved, DNS did not

Go: the same terminal.

Do: run the tunnel again, and this time also print the resolver configuration:

unshare --user --map-root-user --net -- sh -c '
ip link add dummy0 type dummy
ip addr add 10.8.0.2/24 dev dummy0
ip link set dummy0 up
ip route add default via 10.8.0.1 dev dummy0
echo "routing now goes through the tunnel:"
ip route get 1.1.1.1 | head -1
echo
echo "but the resolver config did not change with it:"
grep -v "^#" /etc/resolv.conf | grep -v "^$" | head -3
'

You should see: the route changed and the DNS settings did not:

routing now goes through the tunnel:
1.1.1.1 via 10.8.0.1 dev dummy0 src 10.8.0.2 uid 0

but the resolver config did not change with it:
nameserver 127.0.0.53
options edns0 trust-ad
search .

🔴 This is a DNS leak, in two lines. Routing and name resolution are separate systems. If a VPN moves your traffic but leaves your resolver pointing at your ISP, then your ISP still receives the name of every site you visit — before any encrypted traffic is sent. The connection is private; the question is not. A VPN that does not also replace your DNS has hidden almost nothing worth hiding.

Your nameserver line will differ. 127.0.0.53 is a local stub that forwards elsewhere — run resolvectl status to see where it actually goes.

If not: if /etc/resolv.conf is missing, your system uses a different resolver stack; try resolvectl status. The point holds either way: nothing you did to the routing table changed it.

5
Prove that not everything goes through the tunnel

Go: the same terminal.

Do: add a second network, as a real machine has for its local LAN, then ask which route a local address uses:

unshare --user --map-root-user --net -- sh -c '
ip link add dummy0 type dummy
ip addr add 10.8.0.2/24 dev dummy0
ip link set dummy0 up
ip route add default via 10.8.0.1 dev dummy0
ip link add lan0 type dummy
ip addr add 192.168.50.2/24 dev lan0
ip link set lan0 up
echo "public address -> tunnel:"
ip route get 1.1.1.1 | head -1
echo "local address  -> NOT the tunnel:"
ip route get 192.168.50.10 | head -1
'

You should see: the public address goes through the tunnel and the local one does not:

public address -> tunnel:
1.1.1.1 via 10.8.0.1 dev dummy0 src 10.8.0.2 uid 0
local address  -> NOT the tunnel:
192.168.50.10 dev lan0 src 192.168.50.2 uid 0

A more specific route always beats a default one, whatever the VPN wants. That is why your printer still works with the VPN on — and it is also "split tunnelling", which some VPN apps offer as a feature. It is not a feature the VPN adds; it is how routing already works. Anything matching a more specific route never enters the tunnel at all.

If not: if both lines name dummy0, the lan0 interface did not come up — check the ip link set lan0 up line is present.

🎉
Check yourself before moving on

Your VPN is connected and its app shows a green tick. Your ISP can still see a list of every website you opened. From step 4, how? Answer: The VPN changed your routing but not your DNS resolver. Every site name is still looked up through your ISP's resolver before any encrypted traffic is sent, so it sees the questions even though it cannot read the answers. A green tick reports the tunnel is up — it says nothing about which resolver you use.

Now do it without the page: explain to someone, without this page, why a VPN cannot hide what you do while logged into an account you own. If you can connect that to step 3 — the tunnel changes where packets go, not who is sending them — you understand the boundary better than most VPN marketing does.

Summary

In this tutorial, you learned:

  • How VPNs work and what they protect
  • Important limitations of VPNs
  • Common VPN protocols and which to prefer
  • When using a VPN makes sense
  • What to look for when choosing a provider
🎉
You now understand VPN fundamentals!

With this knowledge, you can make informed decisions about whether and when to use a VPN.