Securing & segregating your home networks

Hi dear community.

Since 1999, I’ve dedicated most of my career to cyber security. Red test pentester, then blue teamer, I now lead an open-source editor named CrowdSec (which offers crowd-sourced protection against aggressive IP addresses). This doesn’t make me an authority, even less so because now I’m a CEO and no longer on the tech playground, but let’s say I’m sensitive to the topic and have experience.

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 case it it’s a naked Raspberry Pi)

This post requires some Linux / network knowledge. Nothing expert level, but it’s a reasonably advanced network & security technics I’ll be explaining here, which could confuse regular users. OpenWRT won’t get you as far as this setup, but it’s easier for a beginner.

PS: I adapted my own configuration here, with different ranges / eth, so I may have typoed it, don’t hesitate to let me know, and I’ll correct them. There will be a lot of edits I’m sure and inclusions of comments that will pop. Please don’t take offense, these kinds of howtos are tricky to write.

Table of content

1/ Goals of the project
2/ Bill of Materials (BoM)
3/ Setting up your firewall box
4/ Configuring your switch, Wifi and separating your networks
5/ Securing your HA

1. Goals of the project

We all have different goals and sensibilities, but I believe most HA users like privacy and cloud independence.

Why no cloud dependency? When a hardware provider imposes its (average) cloud services and forces you to use their app, you’re at risk that:

  • They are down for maintenance due to a cyber attack or bug with consequences on the usability of your system
  • They will lose your data (not if, when)
  • That some attacks can be carried to your home through their hardware, API or services
    (On top of that, we don’t need their app, we don’t want them at all, and we want to control everything through HA)

So 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 fulfilling 3 letter agencies’ wet dreams of having people install connected microphones in their homes (at their expense) isn’t my thing.

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 connexion based on protocol
  • Resilience to a potential Home Assistant 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

Last but not least, I want all of this to be, above all, usable.
Non-user-friendly security usually is a brilliant way to either 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, sorry about that. I’ll stick to the very minimum and very affordable component. (for the security level they’ll contribute to achieve). You can swap any of those by another brand or model, but my explanations and screenshot will be made on this exact hardware.

  • Around 6 hours of your time
  • A “smart” switch, here a Netgear GS324TP ~ $300
  • Serious Wifi Access Point, here Unifi UAP-AC-pro or LR ~ $150 per AP
  • A firewall box, here a Quotom Qotom Mini PC (I5 Core, 8 Gb and 128 Gb SSD i5, 4 Gigabit Ethernet) $233
  • Some cables

You’re looking at something between $500 and $1K to get a real, secure, resilient network. Now this investment will last ten years, and you can optimize buying times and models, but it’s not gonna be cheap.

3. Setting up your firewall

Ok, let’ start with the most central brick, 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 ethernet interfaces (eths) in /etc/network/interfaces and add this:

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

Basically:

  • on eth0, we’ll plug our DMZ
  • on eth1, our Home LAN
  • on eth2.104, our Guests
  • 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.

We’ll define some routes in /etc/iproute2/rt_tables (numbering doesn’t matter here):

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 things to be given proper IP addresses in the various ranges using dhcp.

apt install isc-dhcp-server

Then add the file /etc/default/isc-dhcp-server INTERFACESv4=“eth1 eth2.104 eth3.105”.

I tend to attribute a fixed IP to all my LAN and IoT VLAN devices and leave guests on dynamic attribution.
But if you’re lazy, you can stick to just dynamic pools (and guests are on dynamic):

deny declines;
log-facility local7;
authoritative;

class "unifi" {
  match hardware;
}

subclass "unifi" 1:90:2C:48:D0:FC:47; # Put your AP MAC address to avoid giving them IOT or guest IPs 
subclass "unifi" 1:50:13:db:b6:4a:88;
subclass "unifi" 1:77:A3:c6:33:c8:60;
subclass "unifi" 1:84:C3:c6:20:c1:01;

subnet 192.168.1.0 netmask 255.255.255.0 {
  interface eth1;
  range 192.168.1.205 192.168.1.253;
  option routers 192.168.1.1;
  option broadcast-address 192.168.0.255;
  option domain-name-servers 176.103.130.130, 94.140.14.14;
  default-lease-time 259200;
  max-lease-time 604800;
}

#-----------------------Fix hosts ------------------------------------------#
host Imac                { hardware ethernet 64:83:aa:98:7e:a5; fixed-address 192.168.1.3; }
host Printer             { hardware ethernet 50:4c:41:65:ac:de; fixed-address 192.168.1.4; }
[.........]
#-----------------------Fix hosts ------------------------------------------#

#-----------------------Guest VLAN--------------------------------#
subnet 192.168.2.0 netmask 255.255.255.0 {
  interface "eth2.104";
  pool {
    range 192.168.2.2 192.168.2.255;
    deny members of "unifi";        # Avoid unifi AP pulling some IP in the Guest or IoT dhcp ranges
  }
  option routers 192.168.2.1;
  option broadcast-address 192.168.2.255;
  option domain-name-servers 176.103.130.130, 94.140.14.14;
  default-lease-time 259200;
  max-lease-time 604800;
}
#------------------------IoT VLAN---------------------------------#
subnet 192.168.3.0 netmask 255.255.255.0 {
  interface "eth3.105";
  pool {
    range 192.168.3.2 192.168.3.255;
    deny members of "unifi"; # Avoid unifi AP pulling some IP in the guest dhcp range
  }
  option routers 192.168.3.1;
  option broadcast-address 192.168.3.255;
  option domain-name-servers 176.103.130.130, 94.140.14.14;
  default-lease-time 259200;
  max-lease-time 604800;
}

Ok now head to your ISP-provided FDDI or DSL 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 have to entirely disable the router WIFI to avoid perturbations entirely and because embedded components are crap.

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 '{$
}

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) $E$

  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 192.168.1.0/24  dev $LAN         src 192.168.1.1
  [[ $VPNCLIENT ]] && ip route add table vpnclient 192.168.2.0/24  dev $GUESTVLAN   src 192.168.2.1
  [[ $VPNCLIENT ]] && ip route add table vpnclient 192.168.3.0/24  dev $IOTVLAN     src 192.168.3.1
  [[ $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 # No reverse path filter. 1 stri$
  for i in /proc/sys/net/ipv4/conf/*/arp_filter; do echo 0 | tee "$i" >/dev/null; done # The kernel can respond to arp $
  echo 1 | tee /proc/sys/net/ipv4/ip_forward > /dev/null                               # Allow to forward packets betwe$

  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 that should fit most needs:

#!/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/12,192.0.0.0/24,192.0.2.0/24,1$
define icmp_v6        = { destination-unreachable, echo-reply, echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-a$

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 netdev filter {
  chain ingress {
    type filter hook ingress device $wan priority -120;
      tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg log prefix "Drop Xmas scan:"    drop
      tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|ack|urg     log prefix "Drop Xmas scan:"    drop
      tcp flags & (fin|syn|rst|psh|ack|urg) == fin|psh|urg             log prefix "Drop Xmas scan:"    drop
      tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0                     log prefix "Drop TCP NULL:"     drop
      tcp flags & (syn|rst) == syn|rst                                 log prefix "Drop syb/rst scan:" drop
      iif != lo ip saddr 127.0.0.0/8                                   log prefix "Fake localhost:"    drop
      tcp dport 0                                                      log prefix "Drop tcp dport 0:"  drop
      udp dport 0                                                      log prefix "Drop udp dport 0:"  drop
      tcp sport 0                                                      log prefix "Drop tcp sport 0:"  drop
  }
}

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 81    dnat to 192.168.1.40        # NVR
      iif $wan tcp dport 443   dnat to 192.168.1.49        # Hass.io
      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          # Masquerade rather than snat if ip address of the interface could change
  }
}

table inet filter {
  set prewhitelist {
    type ipv4_addr . inet_service
    flags timeout
  }

  set whitelist {
     type ipv4_addr
     flags interval, timeout
  }

  set whitelist2 {
     type ipv4_addr
     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 
      ip saddr @whitelist                                                 accept comment "whitelisted traffic" # from knockd
      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
      ip saddr @whitelist                                                      accept comment "Whitelisted forward"
      iif $local_eths udp dport {53,123}                                       accept # dns & ntp
      #---------- Cameras ---------
      iif $lan ip saddr $cameras tcp dport {465,587}                           accept # cam stmps
      iif $lan ip saddr $cameras                                               drop
      #----------- LANS ----------
      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;
  }
}

ok next on this firewall: 1/ CrowdSec 2/ Port knocking:

In this firewall you saw the line:

      ip saddr @whitelist                                                      accept comment "Whitelisted forward"

It is made to accept an IP set named “whitelist”. To be added to this whitelist, you’ll need to know the magic combination. It’s simply a port sequence. You ‘knock’ (send a packet to) at port 80, 67, 18 and 33 and we know it’s a friendly. So let’s install port knockd:

apt install knockd

and configure it (in /etc/default/knockd, set START_KNOCKD=1), on /etc/knockd.conf:

[options]
        logfile = /var/log/kern.log
        interface = eth0

[knockd]
	sequence    = 80:tcp,143:tcp,22:tcp,110:tcp,143:tcp
	seq_timeout = 10
	command     = nft add element inet filter whitelist "{ `echo %IP% | cut -f1,2,3 -d "."`".0/24" timeout 3600s }"
     	tcpflags    = syn

put obviously your own port sequence. (and obviously, this is not mine, neither the IP and all the rest)
Now the “command” line will just extend the IP with which you connect to a larger range because usually 4G providers are using CGNAT and all requests may not come from just one IP address.

Port knocking is helpful because you cannot hack what you cannot reach. Exposing a VPN, an HTTP or whatever else post can lead to compromission because the site or binary behind can be flawed without you knowing it. Now PK might look like an outdated strategy, but anyone but you will be filtering by the firewall, hence not even getting a chance at hacking anything.

Last but not least, we install and configure 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

Configuration will be vastly automated so you don’t have to do much on that front. What CrowdSec will do is to prevent bruteforce attacks on your ssh, port scans and also LAN initiated scans… Remember this line 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 the attempt 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.

I also use a simple script to switch to my 4G connexion automatically if my primary connexion is down (add it to your crontab):

#!/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
}

Check_connexion()
{
  TEST_FDDI=`ping -c5 1.1.1.1 | grep "time=" | cut -f4 -d "="|cut -f1 -d " "| cut -f1 -d "." | awk '{s+=$1} END {print s/5}' | cut -f 1 -d "."`

  if [ $TEST_FDDI -eq 0 ]; then
     touch /tmp/FDDI_FLAP
     route del default gw 192.168.0.2 eth0
     route add default gw 192.168.0.3 eth0
     Push "FDDI connexion down, switching to 4G"

  elif [ "$TEST_FDDI" -gt "22" ]; then
       echo $TEST_FDDI
       touch /tmp/FDDI_SLOW
       Push "FDDI connexion lagging"

  elif [ -f "/tmp/FDDI_FLAP" ]; then
    cd /tmp
    rm /tmp/FDDI_FLAP
    route del default gw 192.168.0.2 eth0
    route add default gw 192.168.0.3 eth0
    Push "FDDI connexion is back, switching back to it"

  elif [ -f "/tmp/FDDI_SLOW" ]; then
    cd /tmp
    rm /tmp/FDDI_SLOW
    Push "FDDI recovered from laggy state"
  fi
}

Check_connexion
touch /tmp/ping2mqtt

Ok, take a deep breath, congratulate yourself, you did the hard part, the rest is a cakewalk.

4. Configuring your switch & Wifi to separate your networks

Ok, we’ll define VLANs in our switches. Trunks actually. Let’s go.
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.

So 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. Also, you can leave multicast enhancement and the multicast and broadcast control to OFF.

Also, 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

5. 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 most 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)

Remember to configure your mobile phone app to connect on your https://[your domain name or public IP] and when you’re in mobility, install the knockonD app for iOS (or similar port knocking app on android) and configure your port knocking sequence before connecting to your home assistant.

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)
10 Likes

I see you wrote a lot and thank you for contributing but I have one question.
From security perspective is using ssl on local network a bit overkill or good security practice in your opinion?
I saw some guides on securing your network and ha but I can’t remember that anyone is speaking about securing you local services with ssl protocol.
I done this. I installed ha in container and created domain for it and subdomains for other containers. Using nginx and lets encrypt I got certs for domain and all subdomain I’m hosting.
My domain and subdomains are not accessible over net because I reroute dns traffic for domain back to my ha ip address.

It’s always good to cipher connexions. SSL everywhere you can :slight_smile:
In a (Home) LAN environment, though, if you’re worried about someone eavesdropping on the HTTP exchanges, you already have a more significant problem. You consider your local users as dangerous. In this case, I’d recommend creating an isolated WIFI and letting them live in a different environment.

1 Like

The thing is that you basically never know. I don’t have router that support vlans but from my perspective, and maybe I’m wrong as I’m not professional in this field, there is considerably more threats from the inside your network than from outside. Because you know, you have a family they all have wifi password, they come and go, they are all using different operating systems and you just never know.

I agree this is a risk, hence segmentation & segregation of your network in three zones is crucial.
1 trusted, 2 guests, 3 IoT, that way, if anything splashes, they are in a blast container.

Let me ask you another thing. I have a cisco rv320 router. It’s a dual wan router I bought during covid pandemic as then I had two lte routers. I used that because I worked from home and when ever my nephews turn on youtube my remote connection died. So I used two lte routers because I couldn’t argue with them any more.
Now, this router does support vlans but this is not wifi router.
I have xiaomi mesh wifi for my local network and the main mesh node is connected to my provider huawei router. Can I use this cisco router for a wifi vlan and if I can how to do it?

Your threats consist of two factors.

  1. the potential damage being done
  2. the potential risk of it being exploited

Outside threats often have a potential damage that is somewhat low, because you would have made risk management, but there are many that seek to use any potential risk, so that is high.

Inside threats often have a potential damage that is really high, because you often need to open up for users and services that needs access to data on a whole other level that for outside users and services.
Users and services located inside your network is often though known and controlled by you, which limits the potential risk.

Risk management can come in 4 types.

  1. Mitigated (you have done something to lower the risk and the left over risk is then a new risk to be risk managed)
  2. Insuranced (often not a possibility for normal persons, al though some risks like ID theft might be insurancable (is that a word?) today)
  3. Accepted (You know the risk and potential damage, but have to accepted it. Some left over risk will always have to be accepted, because the price of mitigating or insuring is too high)
  4. Ignored (this should really never be accepted as an option, but it is often the case, because people think the risk or potential damage is low, but they have never really looked into it)

Some try to put numeric values on the risk potential and the potential damage to rate the threats. Some just try to put them into a low/medium/high category.

Thanks for the writeup.

Here is a perspective that might be useful when designing for computer security (from Marcus Ranum):

https://ranum.com/security/computer_security/editorials/dumb/index.html

I’m sorry I wouldn’t know because I never owned any of these hardware myself.

@kameo4242 Thanks for the writing.
Question: You go the route of building the firewall on linux instead of using something like Opnsense. Any particular reason for that? Do you see these firewall less reliable than the linux approach?
Thanks

Opnsense is based on Freebase, which is a Unix variant, like Linux.
Opnsense just gives a more polished and complete setup, but if you are confident with Linux, then it can be just as good.

Sorry I just saw the update. I lagged.

So, several points here. First thing first, OpnSense & PFSense are absolutely great products, brilliantly assembled and carefully curated. In a word, nothing against them.

I’m an old-timer. So I’ve been using IP chain, IP tables, nf tables, PF and all that jazz for a while already. So they aren’t frightening for me. And having direct control over them allows me to do things I couldn’t with OPN or PFSense.

So more flexibility, more advanced usage and it’s has stable as it can be, perfectly optimized for my use case, etc.

The point of sharing this here is to allow everyone interested to dive in advanced security.
I’m also integrating more & more tailscale to replace port knocking. Not exactly the same (like knocking would protect you against unknown zero-day overflow in ssh/tailscale, etc.) but should be good enough.

Wow such an amazing write up. I am using Synology as my backbone to my network.

I have the Synology RT6600 as my router and I use the WRX560 as mesh access points.

I’ve just finished reading your other article about starting off right in HA. Such a wealth of knowledge and I’m grateful for the time you spent. I am heading your advice and starting slow and targeted.

I have had some minor issues so far with the VLAN segregation and what I think may be due to the mesh routing. Do you have any suggestions or tips for me with my Synology backbone.

I can write up a more detailed breakdown of my overall network later.

Thanks for your writeup! I can adopt important things to my system. I have few questions for your UniFi setup:

  1. You mentioned that “avoid setting some minimum data rate control” in case of the IoT network. I agree with that, but the settings on the WiFi tab is not clear for me. If I would disable the “Auto” in the checkbox, there will be an exact value settings where I have to define how much Mbps should be the rate. Did you mean keep this settings on “Auto” or what is the value of the rate here?
  2. What do you think of the “Client Device Isolation” in the IoT network? also in the UniFi…

If I’ll have more time to do a writeup of my system, I would bind it to yours:) I have the same security needs as you. My hw tools are a bit different so it would be added value if I could post my config as well. I work with Mikrotik routers/managed switch, HP mini server with Proxmox, where HA, Adguard and UniFi Controller have separated environment (VM/LXC depends on). I also use VLAN network separation, OpenVPN for VPN and hate any cleartext traffic:)

1/ Yeah, I’m not trusting much auto-setting in unify context because they are often 0 or 1 but rarely dynamic. ie, the power output, set to auto == max.
Here, having a good control over the rates allow to avoid outdated 802.11b devices to clutter the network. Having a subnet with only IoT is also what I ended up having so if they want to go slow, they can, without crippling the rest of the network. Mine is set to 2 mbps as we speak on the IoT segment of the wifi network.

2/ It’s useful in an IoT environment usually. They are not supposed to talk to each other, except for the Chromecast, airplay Sonos and the likes. You’re anyway likely to have those on your primary network or use the function that allow you to fine tune who is able to broadcast (terrible for wifi perf) using the “Multicast and Broadcast control” in your setting → wifi configuration of the unifi controller.

I’m eagerly waiting for your writeup because this is typically unusual posts that offer great value for the few people interested in security :wink:

Even though the NSA got hacked, their Best Practices for Securing Your Home Network guide is useful.

Anyone have recommendations on investing in hardware for this strategy?

I love this guide and @kameo4242’s approach! I intend to follow the guide but am interested in first exploring the options that spending more money could introduce.

For example, buying all of the hardware from someone like Ubiquiti or Aruba etc to potentially reduce the number of components while also gaining a single management interface?

Typically you would actually increase the number of components, because you would need a server for running the management software, but other than that it should be no problem with trying to limiting the number of manufacturers.
Some might say that it will be putting all your eggs in one basket and it might even be a single point of failure should malware get into the manufactures software, but on the other hand equipment from same manufacture usually work easier together and the use of same terminology makes it easier to set up and thereby improve the security in general.
Just avoid using the cloud services some of those manufacturers provide, because that can be a loop hole into your network.

You should make sure that the APs, router and firewall can handle VLANs and not just pass-through.