Setting up an outbound Internet of Things (IoT) firewall with OpenWRT
In my last post, I talked about my new home internet router, which is a Raspberry Pi running OpenWRT. In this post, we'll look at how OpenWRT's features can be used to improve security of Internet of Things (IoT) devices on a home network.
Home routers block inbound traffic (from the internet to LAN devices) by default — isn't that enough to protect IoT devices on a home network?
Sort of — even with inbound traffic blocked, IoT devices can do some undesirable things.
Privacy: devices phoning home
First, it's very common for IoT devices to send usage analytics data up to cloud servers managed by their manufacturers. Some of those analytics are harmless, like device software and hardware versions. Other types of analytics are more questionable, like local network details transmitted by routers. Others yet are downright invasive, like those transmitted by smart TVs. These behaviours are often poorly documented and/or impossible to completely disable.
Security: botnets and malware
Then, there's the issue of security vulnerabilities in IoT devices. IoT devices tend to be more prone to security vulnerabilities than computers:
- Often in IoT product development, as with much of the tech industry, the main priority is delivering desirable features at a quick pace. Building things quickly usually means making compromises, and security is often one of them.
- They're usually "set and forget": most people don't spend time updating firmware or rotating passwords on their coffee machine or flood sensor. As such, vulnerabilities that ship with the product are often left open, even after they become publicly known and patched by the manufacturer.
That's why a typical home network with IoT devices almost certainly has at least one vulnerable device. IoT devices are increasingly targeted by security exploits, usually with the purpose of pulling them into a botnet, where they can be remotely directed to take part in distributed denial-of-service (DDoS) attacks and other nefarious activities. In 2021, botnets comprised of devices like security cameras and routers were used to break the record for largest DDoS attack twice, first in July and then again in September.
IoT botnet malware typically spreads via devices with ports exposed publicly on the internet, e.g. a security camera with port 443 (HTTPS) exposed on a public IP address. The default inbound firewall behaviour of home internet routers protects against this type of spread. Even with an inbound firewall, though, there are ways that malware could spread to IoT devices on a local network:
- Many IoT devices employ hole punching to bypass the inbound firewall, e.g. to make it easier to connect to the device via a mobile app. This behaviour could be exploited by an attacker to gain network access to the device from the internet. (Somewhat reassuringly, I haven't found any examples of this specific type of attack yet.)
- Botnet malware generally shows worm-like behaviour: once it infects a new device, it uses that device to infect yet more devices using network exploits. A single infected device on a LAN could propagate its malware to vulnerable peers, even if they're not accessible from the internet.
- Gitpaste-12 is a recent example of "worming" (self-propagating) malware that targets IoT devices, though it only targets internet-accessible hosts. DirtyMoe, another recent example of worming malware, spreads itself on the local network, though it doesn't target IoT devices. Malware combining these 2 behaviours (targeting IoT devices and spreading on the local network) is just one small step away, if it does not exist already.
Setting up an outbound firewall
To protect against these issues, I decided to set up a firewall rule to selectively block outbound network traffic by MAC address. This is a relatively simple technique — it's an 80/20 solution, where a relatively small effort (20%) covers most of my concerns (80%).
1. Catalog devices on the local network
To set up the firewall, I first needed to know the devices on the network and their respective MAC addresses so that I could know which MAC addresses to block.
I started on the homepage of the OpenWRT web interface (LuCI), which can also be reached by navigating to "Status" → "Overview" in the top navigation bar. I scrolled to the "Active DHCP Leases" section and found the table below:
Some of the rows here had hostnames — those devices were generally easy to identify, except for some that had a cryptic hostname set by the manufacturer. Other rows, though, did not show a hostname at all, only an IP address and MAC address. To identify those, I had to get a bit creative by doing a manufacturer lookup on each MAC address.
First, I copied that whole table into a spreadsheet — the spreadsheet's HTML import function helped with this. (Some data is modified in the screenshots below for privacy.)
Next, I added a new column with title "MAC address first 6" — only the first six characters of the MAC address are required for identifying the manufacturer, and I didn't want to share the complete addresses when looking them up in the next step. I used the
LEFT function to get the first 6 characters (actually the first 8, including colons).
I created a new "Manufacturer" column and filtered the list down to rows with empty hostnames. I then copied the cells under "MAC address first 6", pasted them into the input field in Wireshark's OUI Lookup Tool, and clicked "Find".
Then, I copied the output from the OUI Lookup Tool and pasted it into the respective cells in the "Manufacturer" column.
Knowing the manufacturer for almost all the devices, it was relatively easy to identify each one. A few MAC addresses didn't return results in the OUI search though — what was going on with those? They turned out to be iOS devices spoofing their MAC addresses to preserve privacy, a behaviour called "private addresses" introduced in iOS 14.
I now had a complete list of devices on my network and their MAC addresses. I thought about which ones didn't need internet access and made a note of them for step 3 below.
2. Optional: Review current outbound traffic
OpenWRT has a really handy realtime visualization of network traffic and active connections. Before setting up the firewall, I reviewed the active connections originating from devices on my network. I used the "Enable DNS lookups" feature, which made the results much easier to understand. What I found was interesting, including a surprising example of NAT hole punching that I wasn't previously aware of.
Here's what I did:
- Using the top navigation bar, navigate to "Status" → "Realtime Graphs"
- Open the "Connections" tab
- Click "Enable DNS lookups"
3. Create the firewall rule
With the list of MAC addresses in hand from step 1, I followed these steps to create the firewall rule itself:
- In the top navigation, navigate to "Network" → "Firewall"
- Open the "Traffic Rules" tab
- Click "Add" at bottom left
- Fill out the form to create the new rule (see below)
Under the "General Settings" tab:
- Source zone:
- Destination zone:
Under the "Advanced Settings" tab:
- Source MAC address: Enter the list of MAC addresses that was prepared earlier
When finished filling out the form, press "Save" and then "Save & Apply" at the bottom of the screen.
That's it! 🎉
As mentioned above, this technique isn't perfect and still leaves some gaps in our network security:
- MAC address spoofing: It's trivial for a device to spoof its MAC address, which would bypass the outbound firewall rule. Spoofing is not something I would expect an IoT device to do under most circumstances though — it would take either some sophisticated malware or a malicious actor targeting the device for that to happen. Either of those scenarios is extremely unlikely to happen while inbound and outbound internet traffic to the device is blocked.
- Bad actors on the LAN: This technique controls traffic between LAN devices and the internet, but doesn't do anything to stop malicious activity between devices on the LAN. If a bad actor became connected to the LAN, it would be free to run network exploits against peer devices. This could happen if a bad actor physically connected to the LAN (either by wired or wireless connection), but could also happen if another non-firewalled LAN device became infected with malware.
A "100% solution" that would cover the above scenarios would be VLAN network segmentation: using virtual LANs to divide the home network into different segments, with untrusted IoT devices in a separate segment from trusted devices. That technique is significantly more complex and expensive than the one discussed here, though, requiring pricier VLAN-capable network hardware and more advanced configuration. It can also significantly complicate things in scenarios where communication with LAN peers is desired, but communication with the internet is not (e.g. with a smart light bulb).
Considering the above, the technique explained in this post should be a good middle-ground solution for most people.