Hi dear community.
Even if I’ve been a cybersecurity specialist (red & blue teamer) which doesn’t grant me any authority on the topic, even less so because I’m no longer professionaly on the tech playground, but let’s say I’m sensitive and experienced on the subject.
This guide isn’t about your physical HA security. (<TL/DR> Reliable power supply + UPS, run it on an SSD and not an SDcard, put it in a safe place and in a hard case). This post requires some Linux / network / security basic understanding since it could confuse untrained users. OpenWRT or Opnsense won’t get you as far as this setup, but are much more accessible for beginners.
[EDIT 13/05/2024]: I updated this howto to replace Knocked by Tailscale. It’s so rich, versatile and secure and became a comfortable replacement for Knockd.
Table of content
1/ Goals of the project
2/ Bill of Materials (BoM)
3/ Setting up your firewall box
4/ Connexion redundancy, Tailscale & CrowdSec
5/ Configuring your switch, Wifi and separating your networks
6/ Securing your HA
1. Goals of the project
We all have different goals and sensibilities, but most HA users like privacy and cloud independence.
We just want their hardware, we don’t need their apps since we want to control everything through HA.
Why no cloud dependency? When a provider imposes its (average) API and forces you to use their app, you’re at risk that:
- They are down for maintenance
- They are down because they were hacked
- They will lose your private data/credentials (not if, when)
- That some attacks can be carried to your home through their hardware, API or services
→ I wish for my security to rely as little as possible on 3rd parties and be able to control them when I’ve no choice.
No connected microphone: Also, no mic in my home. No alexa, siri, google or whatever else. Voice activation is cute, but is mainly fulfilling 3 letter agencies’ wet dreams of having people install connected microphones in their homes and at their expense…
Besides I 100% prefer an APP to a voice activation.
Here is what we’ll achieve:
- Solid Ingress filtering (from the Internet toward my LANs)
- Separated networks for IoT devices
- Guests isolated on a subnet (& their traffic going through a VPN)
- Less advertisement & trackers
- Dynamic routing of what goes through regular or VPN connection based on protocol
- Resilience to a any software or daemon potential vulnerability
- Robust Wifi security
- Connexion redundancy
- Limit IoT communication capacities to the strictly necessary (you really want your cameras exposed to other eyes?)
- Long lasting, this is a budget, so it should last
- Real-time notifications if anything suspicious happens
- user friendliness (Non-user-friendly security usually is a highway to upset users or get them to circumvent it)
2. Bill of Materials (BoM)
We’ll need a bit of hardware if we want to be serious about security. I’ll stick to affordable component. You can swap any of those by another brand or model, but my explanations and screenshot will be made on this exact hardware.
- 4 hours of your time
- A “smart” switch, here a Netgear GS324TP ~ $250
- Serious Wifi Access Point, here Unifi UAP-AC-pro or LR ~ $120 per AP
- A firewall box, here a Quotom Qotom Mini PC (I5 Core, 8 Gb and 128 Gb SSD i5, 4 Gigabit Ethernet) $233
You’re looking at something between $500 and $1K to get a real, secure, resilient network, but this investment should hold a decade.
3. Setting up your firewall
Take your Qotom box and install a Linux distro. I’d weigh in for a Debian one. It comes with pros & cons, but there are many pros for a “server” context. There are hardened, more secure distros, but you may fight more than necessary here. Install your favorite jed or emacs editor, prompt, ssh key, etc. Not my circus, not my monkeys.
I understand the move for the dynamic naming of ethernet interfaces, but I’m not a big fan.
In your /etc/default/grub, add an extra:
GRUB_CMDLINE_LINUX_DEFAULT="net.ifnames=0 biosdevname=0 ipv6.disable=0 reboot=acpi"
Next, let’s deal with our 4 ethernet interfaces:
- on eth0, we’ll plug our DMZ
- on eth1, our Home LAN
- on eth2.104, our Guests LAN
- on eth3.105, our IoT devices
.104 means this ethernet interface will be listening to VLAN 104
.105 for VLAN 105, vlans that we’ll define on our smart switch, more about it soon.
In /etc/network/interfaces:
source /etc/network/interfaces.d/*
iface lo inet loopback
auto lo eth0 eth1 eth2.104 eth3.105
allow-hotplug tun0
iface eth0 inet static
address 192.168.0.1/24
gateway 192.168.1.2
dns-nameservers 8.8.4.4 8.8.8.8
iface eth1 inet static
address 192.168.1.1/24
dns-nameservers 8.8.4.4 8.8.8.8
iface eth2.104 inet static
address 192.168.2.1/24
dns-nameservers 8.8.4.4 8.8.8.8
iface eth3.105 inet static
address 192.168.3.1/24
dns-nameservers 8.8.4.4 8.8.8.8
iface tun0 inet dhcp
We’ll define some routes in /etc/iproute2/rt_tables:
100 lan
101 vpnclient
103 guestsvlan
104 iotvlan
105 altcnx
255 local
254 main
253 default
add vlan support module in our load list in /etc/modules and reboot:
8021q
We also want devices to have proper IP addresses in the various ranges using dhcp.
apt install kea-dhcp4-server/stable
Note: ISC DHCP was recently phased out on Debian and can be replaced by KEA DHCP.
A tool to convert from the initial ISC to KEA is available here.
I attribute a fixed IP to all my LAN and IoT VLAN devices and leave guests on dynamic attribution.
I require doing so to be able to count on one IP to be attributed to the intended person/use.
You still can stick to just dynamic pools (and guests are on dynamic) but it’s less secure.
{
"Dhcp4": {
"authoritative": true,
"client-classes": [
{
"name": "unifi"
},
{
"name": "sub#unifi#0",
"test": "concat(substring(pkt4.htype,-1,all), pkt4.mac) == 0x01802aa8d0fc47"
},
{
"name": "sub#unifi#1",
"test": "concat(substring(pkt4.htype,-1,all), pkt4.mac) == 0x01e063dab64a88"
},
{
"name": "sub#unifi#2",
"test": "concat(substring(pkt4.htype,-1,all), pkt4.mac) == 0x017483c233c860"
},
{
"name": "sub#unifi#3",
"test": "concat(substring(pkt4.htype,-1,all), pkt4.mac) == 0x017483c220c101"
},
{
"name": "gen#_AND_#!unifi#",
"test": "not member('unifi')"
}
],
"subnet4": [
{
"id": 1,
"subnet": "192.168.0.0/24",
"interface": "eth1",
"pools": [
{
"pool": "192.168.0.224 - 192.168.0.253"
}
],
"option-data": [
{
"space": "dhcp4",
"name": "routers",
"code": 3,
"data": "192.168.0.1"
},
{
"space": "dhcp4",
"name": "broadcast-address",
"code": 28,
"data": "192.168.0.255"
},
{
"space": "dhcp4",
"name": "domain-name-servers",
"code": 6,
"data": " 94.140.14.14, 94.140.14.14"
}
],
"valid-lifetime": 259200,
"max-valid-lifetime": 604800
},
{
"id": 2,
"subnet": "172.16.0.0/24",
"interface": "eth3.105",
"pools": [
{
"pool": "172.16.0.224 - 172.16.0.255",
"client-class": "gen#_AND_#!unifi#"
}
],
"option-data": [
{
"space": "dhcp4",
"name": "routers",
"code": 3,
"data": "172.16.0.1"
},
{
"space": "dhcp4",
"name": "broadcast-address",
"code": 28,
"data": "172.16.0.255"
},
{
"space": "dhcp4",
"name": "domain-name-servers",
"code": 6,
"data": "8.8.8.8, 8.8.9.9"
}
],
"valid-lifetime": 259200,
"max-valid-lifetime": 604800
},
{
"id": 3,
"subnet": "192.168.2.0/24",
"interface": "eth2",
"pools": [
{
"pool": "192.168.2.3 - 192.168.2.15"
}
],
"option-data": [
{
"space": "dhcp4",
"name": "routers",
"code": 3,
"data": "192.168.2.1"
},
{
"space": "dhcp4",
"name": "broadcast-address",
"code": 28,
"data": "192.168.2.255"
},
{
"space": "dhcp4",
"name": "domain-name-servers",
"code": 6,
"data": "84.200.69.80, 84.200.70.40"
}
],
"valid-lifetime": 259200,
"max-valid-lifetime": 604800
}
],
"interfaces-config": {
"interfaces": [
"eth1",
"eth3.105",
"eth2"
]
},
"host-reservation-identifiers": [
"hw-address"
],
"reservation-mode": "global",
"reservations": [
{
"hostname": "Imac",
"hw-address": "80:4a:14:65:ac:de",
"ip-address": "192.168.0.4",
},
{
"hostname": "PC",
"hw-address": "58:af:34:42:21:11",
"ip-address": "192.168.0.5",
},
...
{
"hostname": "Guest1",
"hw-address": "aa:d1:77:77:c4:48",
"ip-address": "172.16.0.39"
}
]
}
}
On your ISP router, find where you can configure a DMZ IP address and input there 192.168.0.1, the IP of your firewall. If asked for ports, redirect them all toward 192.168.0.1. While you’re there, turn off WPS for the WIFI (often very vulnerable and can leak your WIFI WPA2 key). Once your Unifi APs are up and running, you’ll disable your router WIFI to avoid perturbations since embedded components are less effective.
Install openvpn (apt install openvpn), buy a nordvpn subscription (or whichever you want), configure it and enable it in /etc/defaults.
Next, we prepare the multirouting and setup a firewall.
For this example, I’ve installed a 4G modem/router on the DMZ ethernet. You can add it to your IoT VLAN or to a free ethernet port of the machine. You’ll have a 4G backup if the primary connexion is down.
What this script does, ultimately, is to create different routing tables and associate them with a mark.
This mark will later be used in our firewall to know when to send traffic XYZ through one or the other routing table. Here our FDDI/DSL router is sitting on 192.168.0.2, our 4G is on 192.168.0.3, and the firewall is on 192.168.0.1.
Ok in some /etc/init.d/multiroute.sh file, dump this:
#!/bin/bash
### BEGIN INIT INFO
# Provides: multiroute
# Required-Start: $network
# Required-Stop: $network
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Multiroute manager
# Description: Manage multi-routing
### END INIT INFO
Set_variables()
{
BLUE="\e[1;36m" && YELLOW="\e[1;33m" && ORANGE="\e[38;5;208m" && PURPLE="\e[1;35m"
GREEN="\e[1;32m" && RED="\e[1;31m" && END="\e[0m" && OLDIFS="$IFS"
DATE=`date +'%b %d %k:%M:%S'`
WAN="eth0"
LAN="eth1"
GUESTVLAN="eth2.104"
IOTVLAN="eth3.105"
VPNCLIENT=`ifconfig|grep tun0`
[[ ! -z "$VPNCLIENT" ]] && VPNCLIENTIF="tun0" && VPNCLIENTIP=`ip -o addr | grep -v inet6 | grep $VPNCLIENTIF | awk '{split($4, a, "/"); print a[1]}'` && VPNCLIENTROUTE=`ip route show|grep -v inet6 | grep "tun0 proto" | cut -f 1 -d " "`
}
Env_Cleanup()
{
echo -e "$RED -> CLEANING UP FWMARKS$END"
ip rule del from all fwmark 1 2>/dev/null
ip rule del from all fwmark 2 2>/dev/null
ip rule del from all fwmark 3 2>/dev/null
echo -e "$RED -> CLEANING UP IP RULES$END"
ip rule del lookup lan 2>/dev/null
ip rule del lookup vpnclient 2>/dev/null
ip rule del lookup guestvlan 2>/dev/null
ip rule del lookup iotvlan 2>/dev/null
ip rule del lookup altcnx 2>/dev/null
echo -e "$RED -> FLUSHING ROUTING TABLES$END"
ip route flush table lan 2>/dev/null
ip route flush table vpnclient 2>/dev/null
ip route flush table guestvlan 2>/dev/null
ip rule del lookup iotvlan 2>/dev/null
ip route flush table altcnx 2>/dev/null
}
Routing_Init()
{
echo -e "$ORANGE -> CREATING MULTI-ROUTING TABLE $END"
[[ $VPNCLIENT ]] && echo -e "$ORANGE -> VPN IS UP (route: $VPNCLIENTROUTE, on dev: $VPNCLIENTIF, ip: $VPNCLIENTIP) $END
echo -e "$GREEN -> CREATING LAN ROUTING TABLE $END"
ip route add table lan default dev $WAN via 192.168.0.2
ip route add table lan 192.168.1.0/24 dev $LAN src 192.168.1.1
ip route add table lan 192.168.2.0/24 dev $GUESTVLAN src 192.168.2.1
ip route add table lan 192.168.3.0/24 dev $IOTVLAN src 192.168.3.1
[[ $VPNCLIENT ]] && ip route add table lan $VPNCLIENTROUTE dev $VPNCLIENTIF src $VPNCLIENTIP
ip rule add table lan from 192.168.1.2
echo -e "$GREEN -> CREATING VPN CLIENT ROUTING TABLE $END"
[[ $VPNCLIENT ]] && ip route add table vpnclient default dev $VPNCLIENTIF via $VPNCLIENTIP
[[ $VPNCLIENT ]] && ip route add table vpnclient $VPNCLIENTROUTE dev $VPNCLIENTIF src $VPNCLIENTIP
[[ $VPNCLIENT ]] && ip rule add table vpnclient from $VPNCLIENTIP
echo -e "$GREEN -> CREATING 4G BACKUP CONNEXION ROUTING TABLE $END"
ip route add table altcnx default dev $WAN via 192.168.0.3
ip rule add table altcnx from 192.168.0.3
}
Env_Init()
{
echo -e "$BLUE -> SETTING UP KERNEL IPV4 PARAMETERS$END"
for i in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 | tee "$i" >/dev/null; done
for i in /proc/sys/net/ipv4/conf/*/arp_filter; do echo 0 | tee "$i" >/dev/null; done
echo 1 | tee /proc/sys/net/ipv4/ip_forward > /dev/null
echo -e "$BLUE -> TAGGING DEFAULT FWMARKS PER NETWORK $END"
ip rule add from all fwmark 1 table lan
ip rule add from all fwmark 2 table guestvlan
[[ $VPNCLIENT ]] && ip rule add from all fwmark 2 table vpnclient
ip rule add from all fwmark 2 table iotvlan
ip rule add from all fwmark 3 table altcnx
ip route flush cache
echo -e "$BLUE -> ADDING TEST ROUTE (1.1.1.1) FOR FDDI TO 4G BACKUP $END"
route add -host 1.1.1.1 gw 192.168.0.3 eth0 &> /dev/null # FDDI test host & route when on 4G backup
}
case "$1" in
start)
Set_variables
[[ -z $VPNCLIENT ]] && sleep 5 # Wait for VPN to be up if not yet started when the firewall is started at boot time
/usr/bin/logger -t "Multi route" "Starting" -p4
/usr/bin/logger -t "Multi route" "VPN CLIENT DETECTED, ADDING RULES" -p4
echo -e "$PURPLE --------------> $DATE: Multi routing Start <-------------$END"
Env_Cleanup
Routing_Init
Env_Init
echo -e "$PURPLE ----------------------> Multi routing active <---------------------$END"
exit 0
;;
stop)
Set_variables
echo -e "$RED ------------------> Deactivating Multi Route ! <-------------------$END"
/usr/bin/logger -t "Multi route" "Stopped" -p4
echo 0 | sudo tee /proc/sys/net/ipv4/ip_forward > /dev/null
ip rule del from all fwmark 1 2>/dev/null
ip rule del from all fwmark 2 2>/dev/null
ip rule del from all fwmark 3 2>/dev/null
ip route flush cache
echo -e "$RED ----------------------> Firewall disabled <------------------------$END"
exit 0
;;
restart)
/usr/bin/logger -t "Multiroute" "restart initiated" -p4
$0 stop
sleep 1
$0 start
;;
*)
echo -e "$YELLOW Usage: /etc/init.d/multiroute.sh {start|stop|restart}$END"
exit 1
;;
esac
exit 0
Ok nothing fancy, to finish the job, simply run:
update-rc.d multiroute defaults
Now the firewall (adapt to your needs), I included a few examples:
#!/usr/sbin/nft -f
define wan = eth0
define lan = eth1
define guestvlan = eth2.104
define iotvlan = eth3.105
define vpn = tun0 # created by OpenVPN client
define localhost = lo
define public_eths = { $wan, $vpn }
define local_eths = { $localhost, $lan, $guestvlan, $iotvlan } # local area eth
define workstation = 192.168.1.4
define gamestation = 192.168.1.5
define fwopenports = { 22, 80, 443, 8080 }
define tcp_game_ports = { 1024-1124, 2456-2458, 3216, 9876, 9960-9969, 11100, 18000, 18060, 18120, 27900, 28910, 29900 }
define udp_game_ports = { 1024-1124, 3074, 3478, 4379-4380, 9876, 18000, 27000-27031, 27036, 29900, 37000-40000 }
define cameras = { 192.168.1.8, 192.168.1.9, 192.168.1.10 }
define lanservices = { 192.168.1.3, 192.168.1.5, 192.168.1.7 }
define multicasts = { 239.255.255.250, 224.0.0.251 }
define bogons = { 0.0.0.0/8,10.0.0.0/8,100.64.0.0/10,127.0.0.0/8,169.254.0.0/16,172.16.0.0/8,192.0.0.0/24,192.0.2.0/24,192.168.0.0/16,198.18.0.0/15,198.51.100.0/24,203.0.113.0/24,224.0.0.0/3 }
define icmp_v6 = { destination-unreachable, echo-reply, echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert, packet-too-big, parameter-problem, time-exceeded }
flush ruleset
table netdev retag {
chain QoS {
type filter hook ingress device $lan priority -149; policy accept;
ip saddr $workstation ip dscp set cs6 # pro traffic, highest priority
ip saddr $gamestation tcp dport $tcp_game_ports ip dscp set cs5 # gaming fairly high
ip saddr $gamestation udp dport $udp_game_ports ip dscp set cs5 # gaming fairly high
ip saddr $gamestation udp sport {50000, 50001} ip dscp set cs1 # bittorrent, very low
ip saddr $gamestation tcp sport {50000, 50001} ip dscp set cs1 # bittorrent, very low
ip dscp set cs3 # all the rest default priority
}
}
table ip mangle {
chain prerouting {
type filter hook prerouting priority -150; policy accept;
mark != 0x0 accept # if no mark is set, pass
iifname $guestvlan meta mark set 0x2 # send guest vlan traffic through VPN
meta l4proto tcp tcp dport 50001 meta mark set 0x2 # send bittorrent traffic through VPN
meta l4proto udp udp dport 50001 meta mark set 0x2 # send bittorrent traffic through VPN
#ip saddr 192.168.1.22 tcp dport {80,443} meta mark set 0x2 # send all web traffic from given IP through VPN
}
chain postrouting {
type filter hook postrouting priority -150; policy accept;
ct mark set mark
}
}
table ip nat {
chain prerouting { # Public IP inbound traffic routing
type nat hook prerouting priority -100; policy accept;
iif $wan tcp dport 443 dnat to 192.168.1.49 # HA, not used if you install tailscale
iif $wan tcp dport 5000 dnat to 192.168.1.75 # Frigate, not used if you install tailscale
iif $wan tcp dport 50001 dnat to $gamestation # torrent
iif $wan udp dport 50001 dnat to $gamestation # torrent
}
chain output {
type nat hook output priority -100; policy accept;
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oif $wan snat to 192.168.0.1
oifname $vpn masquerade
}
}
table inet filter {
set prewhitelist {
type ipv4_addr . inet_service
flags timeout
}
chain input {
type filter hook input priority 0; policy drop;
ct state related,established accept # already established connexions are preserved
iif $local_eths accept # local IPs are allowed to reach the firewall
iif $public_eths ip saddr != @whitelist drop # log prefix "Not whitelisted:"
iif $lan tcp dport != $fwopenports log prefix "LAN SCAN: " drop
iifname $guestvlan tcp dport != $fwopenports log prefix "GUEST VLAN SCAN: " drop
iif $public_eths ip frag-off & 0x1fff != 0 log prefix "Frags: " drop
iif $public_eths ip saddr $bogons log prefix "Bogons: " drop
iif $public_eths ct state invalid log prefix "Invalid packet: " drop
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept # preserve established traffic
iif $local_eths udp dport {53,123} accept # dns & ntp
iif $lan ip saddr $cameras tcp dport {465,587} accept # cam stmps
iif $lan ip saddr $cameras drop
iif $lan accept # forward any other lan born traffic
iif $local_eths oif $public_eths accept # accept to forward any local traffic to Wan
iifname $guestvlan ip daddr $lanservices accept # accept vlan ip to access certain lan services
iifname $guestvlan oifname $wan accept # accept guest vlan access to the internet
iifname $iotvlan oifname $wan accept # accept iot vlan access to the internet
iifname $vpn ip daddr $gamestation meta l4proto {udp,tcp} th dport 50001 accept # bittorrent
oifname $guestvlan ip daddr 192.168.1.0/24 log prefix "FORWARD VLAN IP trying to reach LAN:" drop
iif $public_eths ct state invalid log prefix "Invalid packet:" drop
}
chain output {
type filter hook output priority 0; policy accept;
}
}
4. Connexion redundancy, Tailscale & CrowdSec
Tailscale
Tailscale is super simple to install. Either download the APP on your phones (iOS and Android have the app on their store), it’s available too for docker containers and on a debian:
curl -fsSL https://tailscale.com/install.sh | sh
The installer will then tell you to login on a specific URL, do it, and your phones and firewall, frigate, containers, etc will all be joining a private VPN allowing everyone to communicate nicely.
For HA, there is even an already existing addon, it’s just that simple. Blackmagic this thing really… And then in your HA app on your phone, just install tailscale, input your 100.xxx.yyy.zzz:8123 IP in your HA server config and you’ll seamlessly, securely, access your HA APP whether at home on when roaming around.
CrowdSec
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
apt install crowdsec crowdsec-firewall-bouncer-nftables crowdsec-custom-bouncer
What CrowdSec will do is to prevent bruteforce attacks on your ssh, port scans and also LAN initiated scans…
Remember these lines in our firewall?
iif $lan tcp dport != $fwopenports log prefix "LAN SCAN: " drop
iifname $guestvlan tcp dport != $fwopenports log prefix "GUEST VLAN SCAN: " drop
Well, this is all about Local machines, guests or IoT going rogue on you and trying to scan your firewall. Quite a classical lateral-move tactic, but they won’t expect your firewall to actually catch their attempts and tell you. So if one day Daikin, Sonos, an IoT object or a friend is compromised and start trying to hack you, they’ll fail and you’ll just know. For that, we need to configure a “custom bouncer”.
The classical bruteforce, port scans, http scans and all will be dealt with out of the box, but this one needs a bit of config.
From /etc/crowdsec/bouncers/crowdsec-custom-bouncer.yaml:
bin_path: /usr/local/scripts/pushover_crowdsec.sh
piddir: /var/run/
update_frequency: 10s
cache_retention_duration: 10s
daemonize: true
log_mode: file
log_dir: /var/log/
log_level: info
api_url: http://localhost:8080/
api_key: [your API key here]
Ok and guess what is in /usr/local/scripts/pushover_crowdsec.sh ?
#!/bin/bash
[[ `echo $2 | cut -f 1,2 -d"."` == "192.168" ]] && curl -s -F "token=[your pushover token here]" -F "user=[your pushover user token here]" -F "title='LAN Scan'" -F "message=Scanned by $2" https://api.pushover.net/1/messages
A simple pushover notification. Obviously, you can use any script or notification engin you’d fancy.
Also, you can install CrowdSec on your HA itself with the addon and it will take care of login/pass bruteforce on your HA instance.
(GitHub - crowdsecurity/home-assistant-addons: Home Assistant Crowdsec Addons)
Connexion redundancy
#!/bin/bash
Push()
{
curl -s -F "token=[your pushover token here]" -F "user=[your pushover user token here]" -F "message=$1" https://api.pushover.net/1/messages
}
MQTT() {
mosquitto_pub -h localhost -p 1883 -u [YOUR MQTT USER]] -P [YOUR MQTT PASS] -t $1 -m $2
}
Switch_4G() {
touch /tmp/SWAP_4G
route del default gw 192.168.1.2 eth0
route add default gw 192.168.2.2 eth2
nft insert rule ip mangle prerouting position 4 ip saddr 192.168.0.0/24 meta mark set 0x00000004 return
Push "[FDDI: Outage confirmed, 4G backup active $TEST_FDDI]"
}
Switch_FDDI() {
Handle=`nft -a list table ip mangle | grep "00000004" | awk '{print $NF}'`
route del default gw 192.168.2.2 eth2
route add default gw 192.168.1.2 eth0
nft delete rule ip mangle prerouting handle $Handle
[[ -f "/tmp/SWAP_4G" ]] && rm /tmp/SWAP_4G
[[ -f "/tmp/FDDI_FLAP" ]] && rm /tmp/FDDI_FLAP
Push "[FDDI: Outage ended, back to FDDI connexion $TEST_FDDI]"
}
Check_connexion()
{
echo "[ Testing FDDI connexion ]"
TEST_FDDI=`ping -c 3 -m 1 9.9.9.9 | grep rtt | cut -f 6 -d '/' | awk '{print int($1+0.5)}'`
MQTT "homeassistant/connexion/ping" $TEST_FDDI
if [ -z $TEST_FDDI ]; then
[[ ! -f "/tmp/FDDI_FLAP" ]] && touch /tmp/FDDI_FLAP && Notify "[FDDI: Potential outage detected, switching to 4G if confirmed (ping: $TEST_FDDI)]"
[[ -f "/tmp/FDDI_FLAP" ]] && [[ ! -f "/tmp/SWAP_4G" ]] && Switch_4G
MQTT "homeassistant/connexion/state" "KO"
fi
[[ "$TEST_FDDI" -gt "18" ]] && touch /tmp/FDDI_SLOW && Notify "[FDDI: connexion is lagging (ping: $TEST_FDDI)]"
if [ "$TEST_FDDI" -le "19" ]; then
[[ -f "/tmp/FDDI_FLAP" ]] && [[ ! -f "/tmp/SWAP_4G" ]] && rm /tmp/FDDI_FLAP && Notify "[FDDI connexion back to normal, 4G not activated (ping: $TEST_FDDI)]"
[[ -f "/tmp/SWAP_4G" ]] && Switch_FDDI
[[ -f "/tmp/FDDI_SLOW" ]] && rm /tmp/FDDI_SLOW && Notify "[FDDI: connexion recovered from laggy state (ping: $TEST_FDDI)]"
MQTT "homeassistant/connexion/state" "OK"
fi
}
Check_connexion
5. Configuring your switch & Wifi to separate your networks
Let’s define VLANs in our switches.
Go to your admin interface and head to switching → VLAN → advanced. In VLAN configuration, add a 104 guestvlan and a 105 iotvlan.
Then in advanced → vlan membership, untag all port of vlan ID 1 (the image shows U) and the port where the cable connected to your firewall port for guest and iot vlan should be “blank”, have no letter on them. (you can click on them to change their letters)
Then change VLAN ID to 104 and tag the port where you have the cable coming from your firwall guest vlan to your switch with a “T”
Switch to VLAN ID 105 and do the same with the port where the cable coming from your firewall will receive IOT Vlan traffic, place the “T”.
CRITICAL: On both VLAN 104 and 105 place a “T” letter on the ports where your UNIFI wifi APs are connected.
In VLAN 104 & 105: all ports are blank but the one where your AP are connected and the firewall cable is linked to your switch.
Ok you can check in the menu below “Port PVID configuration”, you should see ports belonging to 1,104,105 for APs and just 104 or 105 for the cable connexion going to your firewall.
If you have some Sonos, head to multicast router configuration, enable on the port of your Unifi APs and the IoT port.
Alright, nearly there.
Now let’s install and configure the unifi manager.
Go to your firewall, and type the following:
sudo apt-get update && sudo apt-get install ca-certificates apt-transport-https
echo 'deb https://www.ui.com/downloads/unifi/debian stable ubiquiti' | sudo tee /etc/apt/sources.list.d/100-ubnt-unifi.list
sudo wget -O /etc/apt/trusted.gpg.d/unifi-repo.gpg https://dl.ui.com/unifi/unifi-repo.gpg
sudo service unifi start
Now head to the Unifi manager, and Adopt & configure your Unifi Wifi APs.
Once done, click the settings button on the left hand side and click it.
Then from there, create 3 networks. Home, Guests, IoT.
Needless to say, pick a decent WPA3 key to protect your Wifis.
Then create 3 networks, Home, Guests, IoT.
Give Home 192.168.1.0/24, Guests 192.168.2.0/24, IoT 192.168.3.0/24 and in Advanced, put the VLAN ID 104 for Guests and 105 for IoT. Back in Wifi configuration, bind each wifi network to its IP network (Home → home, Guests → guests, IoT → IoT)
For IoT I recommend to stick to WPA2, since most won’t support WPA3, broadcast only on 2.4 Ghz and avoid setting some minimum data rate control. You can leave multicast enhancement and the multicast and broadcast control to OFF. Later on, you can use this script to update your AP centrally (or Home assistant): wget https://get.glennr.nl/unifi/install/unifi-6.5.55.sh
6. Securing your HA
HA team pays a lot of attention to the security of HA itself and gives you tools for your password safety level, the risk of using an extension or another. They also cautiously maintain their code base and OS and are very well taken care of. Yet this is also an open-source tool, with external contributions and users of varying levels.
You can harden your HA by taking those simple steps:
- Have strong passwords, on HA but also any Wifi, APP or API dependency
- Install CrowdSec security engine + bouncer in HA also (to deter bruteforce attempts)
- Install the least possible amount of 3rd party integrations and add-ons
- Limit the number of users and their rights, give them good passwords
- Not installing any add-ons with bad security requirements or ratings
- Avoid dependency on 3rd party cloud-based tools
- Stick with the updates, the more catchup you have, the more complex and most painful it becomes
- Have an automated backup system, encrypted, exported to the cloud if the place takes fire or gets flooded (Google Cloud backup addon is gold)
Conclusion
- LAN security: check
- Guests & IoT isolation: check
- Wifi security: check
- Remote connexion security: check
- HA security: check
- Protection if an IoT goes rogue: check
- Connexion redundancy: check
- Privacy: check (DNS are filtering ads server and non tracking + VPN if need be and you can install adguard)