Edgerouter Firewall Control with MQTT
Changes
2021-06-15 V0 – zorruno – Started Doc
2022-22-15 V1 – zorruno – Tidyup and full documentation
Summary
Use of an Edgerouter X router with MQTT (& mqcontrol) to block devices from a network on commands from a home automation system.
Goals
This came out of a discussion with a friend that they ‘block’ their kids devices when they need rooms tidied etc with various apps. I’m not huge on spyware and controlling children via nanny apps (and especially ones that are cloud or vendor based), but we do have the occasional issue of a child using their device into the wee hours when not checked.
I thought a good way to do this was via firewall rules, controlled via the MQTT service. I can block based on MAC address of device, which isn’t perfect, however I monitor devices that connect without an approved MAC. I’ll likely turn off access to all non whitelisted MACs in future.
Also, note that on many devices these days (especially Apple devices) you need to turn of the random generation of MAC addresses (their privacy mode). An article from Apple on how to do this is here. https://support.apple.com/en-us/HT211227
Docker, mqcontrol and S6
Mqcontrol is a a useful tool to run scripts when MQTT commands are given. It is a script that really needs to run as a daemon, and the S6 overlay for docker is a great way of taking the had work or supervising scripts that need to run, and keep running.
I could have used the mqcontrol docker container (from albertnis github) but used plain alpine and added the S6 overlay, then pushed the mqcontrol binary and other items to the image.
Mqcontrol: https://github.com/albertnis/mqcontrol
S6-Overlay: https://github.com/just-containers/s6-overlay
Building the container is done as usual with
docker-control build
Remember to rebuild it any time you add a new script.
FROM alpine:3.12.0 AS s6-alpine
# Build the S6 Overlay Stuff https://github.com/just-containers/s6-overlay
ARG S6_OVERLAY_VERSION=3.1.0.1
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
# Upgrade and add extra packages needed
RUN apk upgrade --update --no-cache
RUN apk add --no-cache openssh
# Authorize SSH Host
RUN mkdir -p /root/.ssh && \
chmod 0700 /root/.ssh && \
ssh-keyscan 192.168.1.256 > /root/.ssh/known_hosts
# Move app components into container
RUN mkdir -p /app
COPY ./app/mqcontrol /app/mqcontrol
RUN chmod +x /app/mqcontrol
COPY ./app/automated_rules_key /app/automated_rules_key
RUN chmod 600 /app/automated_rules_key
# Copy all the scripts into the services directory for S6
COPY ./app/scripts/ /etc/services.d/
RUN chmod -R +x /etc/services.d/
# Init
ENTRYPOINT [ "/init" ]
version: '3.3'
services:
mqcontrol-scripts:
build: .
volumes:
- "/dockervolumes/mqcontrol-scripts/data:/data"
- "/etc/localtime:/etc/localtime:ro"
- "/etc/timezone:/etc/timezone:ro"
container_name: mqcontrol-scripts
hostname: mqcontrol-scripts
restart: unless-stopped
Mqcontrol control scripts to run with S6
I ran a bunch of scripts that were controlled by the S6 supervisor. The simplest way to do this is to pop a script in /etc/services.d/ , and in a subfolder under that. Call the script “run”, and make it executable. You can see by the dockerfile above, I store all my scripts in ./app/scripts (in subfolders), and when you build it moves them to the right place.
My structure for the app directory is this:
├── automated_rules_key ├── mqcontrol ├── mqcontrol_0.4.1_linux_amd64 └── scripts ├── mqc-block-kids-ipadmini │ └── run ├── mqc-block-kids-iphone7 │ └── run ├── mqc-block-kids-macbook │ └── run ├── mqc-unblock-kids-ipadmini │ └── run ├── mqc-unblock-kids-iphone7 │ └── run └── mqc-unblock-kids-macbook └── run
Mqcontrol script examples
I won’t go into the Mqcontrol format too much, but essentially this script is activated when the topic commands/firewall/block-kids-iphone7 on the MQTT server is written to. The action for the script is to SSH into the Edgerouter (using the private key for auth). The 256 address here represents the Edgerouter address, and 257 the MQTT server.
I have more info on mqcontrol here
https://zorruno.com/2021/controlling-a-computer-remotely-with-mqtt/
#!/bin/sh
/app/mqcontrol -i mqc-block-kids-iphone7 -c "ssh 192.168.1.256 -l automated_rules -i /app/automated_rules_key /config/scripts/block-kids-iphone7" -h 192.168.1.257:1883 -t commands/firewall/block-kids-iphone7
#!/bin/sh
/app/mqcontrol -i mqc-unblock-kids-iphone7 -c "ssh 192.168.1.256 -l automated_rules -i /app/automated_rules_key /config/scripts/unblock-kids-iphone7" -h 192.168.1.257:1883 -t commands/firewall/unblock-kids-iphone7
I could have easily made one script to control all devices, but I decided to separate them in case I wanted individual control. I also found that running scripts on the router can be a bit slow at times, so I allow for this later in Node Red, but delaying each script action slightly.
Edgerouter Firewall setup
The above is all you need for running daemon scripts in a docker container, in this case a number of instances of mqcontrol.
We now need to set up the actual firewall on the Edgerouter to a default state, for each device we are going to control.
We can do that with the Edgerouter GUI. In the Firewall, if you don’t already have a WAN_OUT section, create one (on my case, the interface is PPPoE/out). We can then make a number of rules under this, one for each device we with to control.
EDIT the ruleset for WAN_OUT and add a rule for a device. Each will be numbered, and I’ll use the example of Rule 2. All you need to change is below, and just under the BASIC and SOURCE tabs.
BASIC
- Give it a Description, (eg Drop access to Kid’s Iphone7)
- Enable the tick box
- Choose action ‘Accept’ (The commands we give later will set this to drop)
- Choose Protocol ‘All Protocols”
SOURCE
- Add the MAC Address of the device eg “02:df:76:b9:b7:1c”
Edgerouter scripts
Now we need some scripts on the router (that we are calling via ssh from mqcontrol) to do the firewall modification. Firstly, you have to be able to able to shell into the edgerouter and scripts live in /config/scripts. You can shell in if you have turned the SSH option on in the menu, with an admin account.
My scripts for the device control looks like this. Note I cam using rule 2, to match the firewall settings above.
#!/bin/bash
run=/opt/vyatta/bin/vyatta-op-cmd-wrapper
cfg=/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper
$cfg begin
$cfg set firewall name WAN_OUT rule 2 action drop
$cfg commit
logger -s "Blocking: Kids Iphone7 Blocked"
exit 0
#!/bin/bash
run=/opt/vyatta/bin/vyatta-op-cmd-wrapper
cfg=/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper
$cfg begin
$cfg set firewall name WAN_OUT rule 2 action accept
$cfg commit
logger -s "Blocking: Kids Iphone7 Unblocked"
exit 0
Control and Automation
With all the above, now there is a setup where if a topic on the MQTT is written to, the firewall will be controlled.
A write to commands/firewall/block-kids-iphone7 will run the firewall script to drop packets for that device’s MAC address.
A write to commands/firewall/unblock-kids-iphone7 will run the firewall script to accept packets for that device’s MAC address.
There are now plenty of ways to automate this, eg with a wall switch, timers to allow use when chores are done etc using tools such as home assistant, node red etc.
Node Red example
This is my setup I put in place (initially – as I suspect there will be tweaks. It allows
- Toggle action from a phone or touchscreen to block/unblock all devices
- Toggle button to allow a ‘2 Hour’ use of devices, and another to deny device internet access for 2 hours.
- A scheduler that can be enable and disabled via MQTT. This allows blocking and unblocking (once of each per day) device internet access. The start/end time can be displayed and written to via MQTT.