Module 5 · Labs
These five labs take you from a hand-written WireGuard config to a fully designed remote-access architecture — including the satisfying proof at the end: scanning your own home IP from outside and seeing zero open ports while your services remain reachable through overlay tunnels. Commit configs (secrets stripped!) and writeups as you go.
Lab 1 · Raw tunnel
Section titled “Lab 1 · Raw tunnel”Goal: hand-build a working WireGuard tunnel from a remote network, with no GUI tools. Exercises Lesson 5.1.
- Generate key pairs on both your server and a client (laptop). Keep private keys secret.
- Hand-write
/etc/wireguard/wg0.confon each peer: overlay addresses (e.g.10.10.0.1/24and10.10.0.2/24), the server’sListenPort, correct public keys, the client’sEndpointpointing at your home, sensibleAllowedIPs, andPersistentKeepaliveon the client. - Forward one UDP port (51820) on your router to the server — the only inbound hole, and you’ll remove it after later labs replace it.
- Bring it up:
sudo wg-quick up wg0on both. Verify withsudo wg(look for a recent handshake). - Test from a genuinely remote network — a phone hotspot or a café, not your own LAN — and
ping 10.10.0.1across the tunnel. Bonus: add your LAN subnet toAllowedIPsand reach another home device through the tunnel.
Verify
Section titled “Verify”-
sudo wgshows a completed handshake and transfer in both directions. - You reached the server over the tunnel from an external network.
- You can explain every line of both configs, especially
AllowedIPs.
Commit
Section titled “Commit”labs/05-01-wireguard.md with sanitized configs (placeholder keys) and an explanation of
each directive. No real keys.
Lab 2 · Break it on purpose
Section titled “Lab 2 · Break it on purpose”Goal: build the WireGuard diagnostic instinct by causing and identifying failures. Exercises Lessons 5.1 and 1.5.
Starting from your working Lab 1 tunnel, introduce these faults one at a time, and for each: predict the symptom, observe it, then diagnose it from evidence before fixing:
- Wrong
AllowedIPs(too narrow) — tunnel handshakes but your traffic doesn’t route. What doessudo wgshow vs.ping? - Wrong endpoint / blocked UDP port — no handshake at all. Use
sudo tcpdump -i any udp port 51820 -n(Lesson 1.5) to see whether packets even arrive. - Swapped/typo’d public key — handshake never completes though packets arrive.
- For each: which tool revealed the cause (
wg,tcpdump,ping)?
Then assemble your findings into a WireGuard troubleshooting flowchart (in Mermaid — see Diagrams as Text, and the example tree in Lesson 5.1).
Verify
Section titled “Verify”- You reproduced at least three distinct failure modes and diagnosed each from evidence.
- You used
tcpdumpto distinguish “packets not arriving” from “packets arrive but no handshake.” - You produced a diagnostic flowchart you’d actually use.
Commit
Section titled “Commit”labs/05-02-wg-debugging.md with the failure/symptom/diagnosis table and your Mermaid flowchart.
Lab 3 · Tailnet
Section titled “Lab 3 · Tailnet”Goal: stand up a Tailscale tailnet across your devices and restrict it with an ACL. Exercises Lesson 5.2.
- Install Tailscale on your laptop, phone, and server;
tailscale upand authenticate each. - Confirm the mesh:
tailscale status; reach your server by its tailnet IP from your phone on cellular (off your home WiFi). - Enable MagicDNS and reach the server by name (
ssh homelab) from a remote network. - Configure a subnet router so a device can reach a whole home subnet/VLAN (Lesson 3.3) through one node.
- Write an ACL that meaningfully restricts access — e.g. the phone may reach only the server’s web port, not SSH. Apply it and prove the restriction holds (the allowed thing works, the denied thing is refused).
Verify
Section titled “Verify”- Your devices form a working tailnet reachable from off-network.
- MagicDNS lets you use names, not IPs.
- A subnet router exposes a LAN/VLAN to the tailnet.
- You wrote and applied an ACL and demonstrated it actually restricts access.
Commit
Section titled “Commit”labs/05-03-tailscale.md with your ACL (it’s policy-as-code, safe to commit) and evidence the
restriction works.
Lab 4 · Publish
Section titled “Lab 4 · Publish”Goal: publish a service to the public internet through Cloudflare Tunnel with an Access policy — and prove your home IP has zero open ports. Exercises Lesson 5.3.
- With a Cloudflare-managed domain, install
cloudflaredon your server and authenticate it. - Create a tunnel and route a public hostname (e.g.
demo.yourdomain.com) to a local service (a simple web page, or the nginx from Lab 2.4). Runcloudflaredas a systemd service so it survives reboots. - Confirm the service is reachable at
https://demo.yourdomain.comfrom anywhere — with no port forwarded for it. - Put Cloudflare Access in front: a policy allowing only your email (one-time code). Confirm an unauthenticated visitor is challenged and only you get through.
- The proof: from an external network, scan your home’s public IP:
Confirm it shows no open ports (once you’ve also torn down the Lab 1 port-forward), even though your services are reachable through the tunnels.
Terminal window nmap -Pn <your-home-public-ip>
Verify
Section titled “Verify”- A service is publicly reachable via Cloudflare Tunnel with no inbound port for it.
- Cloudflare Access gates it to your identity; unauthenticated visitors are blocked.
- An external
nmapof your home IP shows zero open ports. - You can state the trade-off you accepted (Cloudflare sees plaintext at its edge).
Commit
Section titled “Commit”labs/05-04-cloudflare.md with the setup (sanitized — no tunnel token), the Access policy, and
the nmap output proving zero open ports.
Lab 5 · The design doc
Section titled “Lab 5 · The design doc”This is the module’s deliverable. Turn your three tools into a documented architecture with a justified decision. Exercises Lesson 5.4 and 0.5.
The assignment
Section titled “The assignment”Write blog/05-remote-access-adr.md — an ADR (Lesson 5.4)
for your homelab’s remote access, containing:
- Context — what you need (private admin access; public publishing) and your constraints (behind NAT, no inbound ports).
- Decision — which tool you use for what (e.g. Tailscale for admin, Cloudflare Tunnel + Access for the public site), with reasoning.
- Alternatives considered — including raw WireGuard and port forwarding, and why you rejected/kept each.
- Consequences & trade-offs — honestly stated, including who’s in the data path and what happens if a token leaks.
- A topology diagram in Mermaid showing your devices, the tunnels, and the (zero) open ports.
- The proof — your external
nmapoutput showing no open ports.
Verify
Section titled “Verify”- The ADR follows the standard shape and justifies each choice.
- Trade-offs are stated honestly, not glossed over.
- The Mermaid diagram matches your real setup.
- The
nmapproof is included.
Commit
Section titled “Commit”git add blog/05-remote-access-adr.md labs/git commit -m "Add remote-access ADR: WireGuard, Tailscale, Cloudflare Tunnel"git pushPublish it. “Here’s how I give secure, zero-port remote access to my infrastructure, and why I
chose each piece” — with a diagram and an nmap proof — is precisely the answer to a very common
interview question, already written and demonstrated.
Module 5 checkpoint
Section titled “Module 5 checkpoint”- I can hand-write a working WireGuard config and explain every line (Lab 1)
- I can debug a failed handshake methodically, not by config-shuffling (Lab 2)
- My devices form a tailnet with ACLs I wrote (Lab 3)
- I have a public service reachable through a tunnel with zero inbound ports open (Lab 4)
- An external port scan of my home IP shows nothing (Lab 4)
- I’ve written an ADR comparing the three approaches for my own use (Lab 5)
Deliverable
Section titled “Deliverable”Your remote-access design doc (Lab 5): an ADR comparing WireGuard, Tailscale, and Cloudflare
Tunnel, with a Mermaid topology diagram and an external nmap scan proving zero open ports —
demonstrating you can not only operate modern zero-trust access but reason about and justify an
architecture.
Next module: Module 6 · Self-Hosting → — where everything converges: your own public site and git server, reachable through the tunnels you just built.