Block kids devices using OPNsense

23 Sep 2022

So I’m working on replacing my Edgerouter X with OPNsense and one key important thing I need is to be able to disable my kids devices. That is per kid, not all of them at once, just one kid if needed.

Basic setup

I will not go into details of how to setup your OPNsense box, there are already plenty of guides for this. I will go through a short summary of what I have setup for my kids blocking feature.

Firewall Alias

The first thing I did was to start mapping my kids devices to DHCP static IP addresses like I have on my Edgerouter, but then I realized I can use Firewall Alias using MAC addresses, this way the IP address doesn’t matter. It’s way faster to setup because I can create one MAC Address Alias for each kid. Easy to add and remove a device. So I removed all the static DHCP mappings and created one MAC Address Alias for each of my kids.

OPNsense API

To be able to access the OPNsense API you must create an API key for a user in System -> Access -> Users. I did create a separate “remote” user for this

system -> access -> users
System Access Users

My remote user settings with the API key. I did add my remote user to the Admin group, I don’t know if this is needed though but the user is going to change the firewall settings so I just assumed it’s needed.

edit-remote-user
Edit Remote User
Just hit the + button to create an API key, just remember only one part of the Key and Secret is visible in the GUI. When you hit the button you will download a file with both keys and AFAIK this is the only way to get the Secret key. If you loose the Secret key you will have to create a new API Key.

Firewall API plugin (not a solution for me)

At first glance this API pluging looks awesome, so this was the first thing I downloaded after installing OPNsense and finding my way through the web GUI. But when I actually tried to set it up to block devices I found out that, first this plugin has it’s own rules so it’s harder to order other rules around the block rule. The second but in my opinion the most important thing, I can’t use Firewall Alias for my source. This makes the Firewall API plugin unusable for me.

Core Firewall API

After my failure with the Firewall API plugin I stumbled over a post somewhere that said it’s possible to enable/disable an Firewall Alias and this actually affects the rule(s) using that alias. The OPNsense core firewall API already have the functionality to enable/disable a Firewall Alias, so I tried this.

The OPNsense firewall API to toggle the state of an Alias needs the Alias UUID, so first we must query the firewall for the UUID of the rule we are looking for. I made a small bash script to query the firewall API using curl and I’m going to assume the OPNsense firewall IP is 192.168.1.1

#!/bin/bash
KEY=<...>
SECRET=<...>

ROUTER=192.168.1.1

REQUEST="api/firewall/alias/searchitem"

curl -k -u "$KEY":"$SECRET" https://$ROUTER/$REQUEST

This will return a JSON string with all the aliases on the firewall.

For my testing purpose I did not bother to write a parser, I manually looked up the Alias rule UUID I needed and moved on to the next bash script to toggle an Alias state.

#!/bin/bash
KEY=<...>
SECRET=<...>
UUID=<...>

ROUTER=192.168.1.1

REQUEST="api/firewall/alias/toggleitem/$UUID"

curl -k -u "$KEY":"$SECRET" https://$ROUTER/$REQUEST --data-raw '{}'

This will actually toggle the Alias between Enabled and Disabled, you have to keep track of the Enabled/Disabled state your self. You can see the state in the first alias query when we looked for the UUID. The respons of this query will also notify you if the Alias was enabled or disabled.

But this does not affect any rules yet, the firewall must be reconfigured (hitting the apply button in the GUI) so another API command must be sent after the above, I included this in the same toggle bash script, so the new script looks like this

#!/bin/bash
KEY=<...>
SECRET=<...>
UUID=<...>

ROUTER=192.168.1.1

REQUEST="api/firewall/alias/toggleitem/$UUID"

curl -k -u "$KEY":"$SECRET" https://$ROUTER/$REQUEST --data-raw '{}'

sleep 1

REQUEST="api/firewall/alias/reconfigure"
curl -k -u "$KEY":"$SECRET" https://$ROUTER/$REQUEST --data-raw '{}'

Now the firewall is using the changes. But, yes another but, only for new connections. Already established traffic is not interrupted by applying new rules, so Enabling a block rule to disable the network connection will not stop existing gaming session or tiktok stream etc.

The OPNsense documentation have the diagnostics firewall killstates API command to use. So bash script again

#!/bin/bash
KEY=<...>
SECRET=<...>

ROUTER=192.168.1.1

REQUEST="api/diagnostics/firewall/killstates"

curl -k -u "$KEY":"$SECRET" https://$ROUTER/$REQUEST --data-raw '{}'

But this failed, big fat error response. All I could find with my “google skills” was just other forum posts with the same error but no solution. So I decided to look in to the actual PHP code for the OPNsense diagnostics firewall killstates API and the documentation of the built in kill state API is not correctly documented on the OPNsense website. You need to pass a “filter” parameter during your POST request

You can find what filter parameter you can use by going to the Firewall -> Diagnostics -> States

firewall-diagnostics-states-filter
Firewall Diagnostics States Filter
The filter box will filter on the Rule column and this is the same filter as the parameter we are going to pass to the API command.

I have my kids (and their friends, guests etc. ) on their own Vlan,

firewall-rules-10_kids
Firewall Rules 10_kids
You can see the kids alias block rules and I have some block rules for other Vlan and at the bottom the “Allow All” rule for the internet access. The description for this rule is what appears in the Rule column in Firewall -> Diagnostics -> States, so setting this to something unique you can filter on this text. You don’t need a full match, partial match is enough. My rule says “Allow 10_kids” but I actually do filter on the “10_kids” part.

So back to the bash script, to finally get the kill states working we have to send the filter as well

#!/bin/bash
KEY=<...>
SECRET=<...>

ROUTER=192.168.1.1

REQUEST="api/diagnostics/firewall/killstates"

curl -X POST -k -u "$KEY":"$SECRET" -d "filter=10_kids" https://$ROUTER/$REQUEST

So finally I can disable my kids Internet access and kill open sessions so they can stop browsing TikTok and actually go to sleep.