Introduction
When connecting to public networks — hotels, airports, cafés — you are effectively placing your devices in an untrusted environment. While VPNs help, they do not give you full control over how your devices communicate with the outside world.
In this project, I built a portable travel router using a Raspberry Pi that:
- creates its own secure Wi-Fi network
- strictly controls outbound traffic
- enforces DNS filtering via Pi-hole
- adapts to different uplinks (Wi-Fi or Ethernet)
- remains observable and debuggable at all times
This article explains both the design philosophy and the implementation approach, with key snippets to illustrate the setup.
If you just want the summary:
TL;DR
🔒 Secure Wi-Fi network
🛡️ DNS filtering via Pi-hole
🚫 Strict outbound firewall
🌍 Works in hotels and public networks
High-Level Architecture
At a high level, the router sits between your devices and the internet:
- Devices connect to a local Wi-Fi network (
wlan1) - The Raspberry Pi routes traffic through:
wlan0(Wi-Fi uplink), oreth0(wired / hotel network)
The system enforces strict firewall rules and DNS policies before traffic leaves the device.
Design Goals
This project was guided by a few key principles:
- Default deny: nothing leaves the network unless explicitly allowed
- Deterministic behavior: no hidden system magic — everything is script-controlled
- Portability: works in hotels, homes, and mobile setups
- Observability: every decision can be inspected and verified
- Safe switching: uplink changes should never break connectivity silently
Core Components
The system combines a few well-known tools with custom logic:
- hostapd → provides the Wi-Fi access point
- Pi-hole → DNS filtering and control
- CSF (iptables) → firewall and traffic policy
- Custom scripts:
- uplink switching
- mode control (strict vs bootstrap)
- diagnostics
Network Modes
Egress Mode
The router operates in two outbound modes:
- Normal mode
- strict firewall rules
- DNS forced through Pi-hole
- Bootstrap mode
- relaxed rules
- useful for captive portals (hotels)
Example:
sudo egress-mode normal
sudo egress-mode bootstrap
Uplink Switching
The router can dynamically switch its internet source:
sudo uplink-switch wlan0
sudo uplink-switch eth0
sudo uplink-switch none
This updates:
- default route
- NAT configuration
- firewall rules
Hotel Mode (Ethernet DHCP)
Some hotel networks require DHCP and captive portals.
The router supports this with:
sudo hotel-hard-wired-mode on
This:
- switches
eth0to DHCP - waits for a valid lease
- verifies routing before switching over
Firewall Philosophy
The firewall is the most critical part of the system.
Key rules:
- Default: DROP everything
- Only allow:
- explicitly whitelisted outbound ports
- established connections
- Block:
- direct DNS (force Pi-hole)
- lateral movement between clients
Example Firewall Logic
Allow outbound HTTPS:
-A FORWARD -s 192.168.4.0/24 -o wlan0 \
-p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT
Allow established connections:
-A FORWARD -i wlan1 -o wlan0 \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Without this rule, even valid HTTPS sessions break — a subtle but critical detail.
Handling Real-World Traffic
One of the most interesting aspects of this setup is observing real device behavior.
Example: Apple Devices
Apple devices use:
- TCP 5223 → push notifications (APNs)
If blocked:
- notifications are delayed
- battery usage increases
Conclusion:
→ this port should be allowed
Diagnostics and Observability
A dedicated script (router-health) provides a full system overview:
- interface status
- routing
- firewall state
- DNS enforcement
- access point health
This avoids the typical “black box” feeling of network setups.
Lessons Learned
A few key insights from building this:
- Stateful firewalling is essential
MissingESTABLISHED,RELATEDrules breaks everything in subtle ways - Devices use unexpected ports
Not all traffic is HTTP/HTTPS - Strict control requires iteration
You observe → adjust → refine - Separation of config and state is critical
Avoid mixing scripts and runtime values
Conclusion
This travel router provides:
- a secure networking layer on top of untrusted networks
- full control over outbound traffic
- a transparent and inspectable system
It strikes a balance between security, usability, and flexibility.
Source Code
The full implementation (scripts, configuration, firewall rules) is available here:
Final Thoughts
This project started as a simple idea — “make public Wi-Fi safer” — but quickly turned into a deep dive into networking, firewalling, and real-world device behavior.
If you enjoy understanding how systems actually behave under the hood, this is a very rewarding build.
