Creating a secure BMS system for your customers: Part 7, Security

Physical security

The routers themselves and all LAN side network endpoints should be in a locked room or box. It doesn’t need to be a bank vault but a casual visitor should not be able to simply walk up and plug in. Should be trivial to add a lock to most BMS enclosures, just do it.

Firmware updates

First off subscribe to the Ubiquiti EdgeMAX blog to get notifications of new firmware updates. You don’t strictly need to update all devices when they post every month or two, just read and see if there is something important and make an informed decision. These updates can be applied remotely with little risk. In practice I’ve only ever had one Edgerouter have trouble after an update, something with DNS didn’t work right and it required the update to be applied again locally which required a site visit.

After a firmware update there is a problem with the private VPN keys and config being copied to a folder that’s not documented well, and worse this particular directory does NOT get wiped out on a factory reset. The risk is IF someone were to steal the router and do a factory reset they would be able to dig out the keys to your VPN with the factory default credentials and gain remote access to your networks, from anywhere in the world. Not good. Just check this folder after a firmware update and delete any private keys and OpenVPN config files you see there;

/root.dev/w.o/config/

Firewall

This is where the ERX really shines. The firewall in the Edgerouter line is very capable and accessible via the GUI or the config file and it will do things that no consumer firewall will do. In some of my earlier trials I locked the LAN side down to the extent where only BACnet/IP from certain MAC addresses could communicate over the LAN. You can do that but it really hurts troubleshooting and service changes. I eventually relaxed it a bit and settled on these rules;

  • Pass local LAN access to router configuration web pages and SSH
  • Pass remote VPN access to router and LAN devices
  • Pass local LAN UDP 47806-47810 to other sites
  • Block everything else from LAN, no internet access etc
  • Block WAN inbound everything unrelated to an existing outbound query, this is the default behavior as defined in the basic setup wizard.

While I am generally a GUI person there are things it just doesn’t do well. While I could document a dozen pictures and a hundred mouse clicks and values to type I’m just going to give you three sections to paste into the routers config to accomplish the above.

First step is optional, save a copy of the current router configuration. It’s nice to have a backup.

user@ubnt:~$ configure
[edit]
user@ubnt# save config.boot-bu5-5-2017
Warning: saving configuration to non-default location '/config/config.boot-bu5-5-2017'...
Done
[edit]
user@ubnt# exit

Now copy that file back to your workstation and edit it, on windows an editor like Notepad++ works great for this sort of thing. Add the following in the noted places

Section in top firewall after line: broadcast-ping disable

	group {
		port-group bms_ports {
			port 47806-47810
		}
	} 
	group {
		port-group mgmt_ports {
			port 22
			port 80
			port 443
		}
	}

Section in top firewall after line: log-martians enable

	name LAN_IN {
		default-action drop
		description "LAN passing through router"
		rule 1 {
			action accept
			description "accept est / rel"
			state {
				established enable
				related enable
			}
		}
		rule 2 {
			action accept
			description "accept traffic on ports bms_ports"
			destination {
				group {
					port-group bms_ports
				}
			}
			protocol udp
		}
	}
	name LAN_LOCAL {
		default-action drop
		description "LAN to router"
		rule 3 {
			action accept
			description "accept traffic on ports mgmt_ports"
			destination {
				group {
					port-group mgmt_ports
				}
			}
			protocol tcp
		}
		rule 10 {
			action accept
			description "Allow established/related"
			state {
				established enable
				related enable
			}
		}
		rule 20 {
			action drop
			description "Drop invalid state"
			state {
				invalid enable
			}
		}
	}

Section down in switch 0 after line: description Local

		firewall {
			in {
				name LAN_IN
			}
			local {
				name LAN_LOCAL
			}
		}

A complete config file should look something like this;

firewall {
    all-ping enable
    broadcast-ping disable
	group {
		port-group bms_ports {
			port 47806-47810
		}
	} 
	group {
		port-group mgmt_ports {
			port 22
			port 80
			port 443
		}
	} 	
    ipv6-receive-redirects disable
    ipv6-src-route disable
    ip-src-route disable
    log-martians enable
	name LAN_IN {
		default-action drop
		description "LAN passing through router"
		rule 1 {
			action accept
			description "accept est / rel"
			state {
				established enable
				related enable
			}
		}
		rule 2 {
			action accept
			description "accept traffic on ports bms_ports"
			destination {
				group {
					port-group bms_ports
				}
			}
			protocol udp
		}
	}
	name LAN_LOCAL {
		default-action drop
		description "LAN to router"
		rule 3 {
			action accept
			description "accept traffic on ports mgmt_ports"
			destination {
				group {
					port-group mgmt_ports
				}
			}
			protocol tcp
		}
		rule 10 {
			action accept
			description "Allow established/related"
			state {
				established enable
				related enable
			}
		}
		rule 20 {
			action drop
			description "Drop invalid state"
			state {
				invalid enable
			}
		}
	}
    name WAN_IN {
        default-action drop
        description "WAN to internal"
        rule 10 {
            action accept
            description "Allow established/related"
            state {
                established enable
                related enable
            }
        }
        rule 20 {
            action drop
            description "Drop invalid state"
            state {
                invalid enable
            }
        }
    }
    name WAN_LOCAL {
        default-action drop
        description "WAN to router"
        rule 10 {
            action accept
            description "Allow established/related"
            state {
                established enable
                related enable
            }
        }
        rule 20 {
            action drop
            description "Drop invalid state"
            state {
                invalid enable
            }
        }
    }
    receive-redirects disable
    send-redirects enable
    source-validation disable
    syn-cookies enable
}
interfaces {
    ethernet eth0 {
        address dhcp
        description Internet
        duplex auto
        firewall {
            in {
                name WAN_IN
            }
            local {
                name WAN_LOCAL
            }
        }
        speed auto
    }
    ethernet eth1 {
        description Local
        duplex auto
        speed auto
    }
    ethernet eth2 {
        description Local
        duplex auto
        speed auto
    }
    ethernet eth3 {
        description Local
        duplex auto
        speed auto
    }
    ethernet eth4 {
        description Local
        duplex auto
        speed auto
    }
    loopback lo {
    }
    openvpn vtun0 {
        config-file /config/auth/client2.ovpn
    }
    switch switch0 {
        address 10.105.102.1/24
        description Local
		firewall {
			in {
				name LAN_IN
			}
			local {
				name LAN_LOCAL
			}
		} 
        mtu 1500
        switch-port {
            interface eth1 {
            }
            interface eth2 {
            }
            interface eth3 {
            }
            interface eth4 {
            }
            vlan-aware disable
        }
    }
}
service {
    dhcp-server {
        disabled false
        hostfile-update disable
        shared-network-name LAN {
            authoritative enable
            subnet 10.105.102.0/24 {
                default-router 10.105.102.1
                dns-server 10.105.102.1
                lease 86400
                start 10.105.102.38 {
                    stop 10.105.102.99
                }
            }
        }
        static-arp disable
        use-dnsmasq disable
    }
    dns {
        forwarding {
            cache-size 150
            listen-on switch0
        }
    }
    gui {
        http-port 80
        https-port 443
        older-ciphers enable
    }
    nat {
        rule 5010 {
            description "masquerade for WAN"
            outbound-interface eth0
            type masquerade
        }
    }
    ssh {
        port 22
        protocol-version v2
    }
}
system {
    host-name ubnt
    login {
        user someuser {
            authentication {
                encrypted-password $6$Ff-LOLsecureAF-znkp1.
            }
            level admin
        }
    }
    ntp {
        server 0.ubnt.pool.ntp.org {
        }
        server 1.ubnt.pool.ntp.org {
        }
        server 2.ubnt.pool.ntp.org {
        }
        server 3.ubnt.pool.ntp.org {
        }
    }
    offload {
        hwnat enable
    }
    syslog {
        global {
            facility all {
                level notice
            }
            facility protocols {
                level debug
            }
        }
    }
    time-zone UTC
}


/* Warning: Do not remove the following line. */
/* === vyatta-config-version: "config-management@1:conntrack@1:cron@1:dhcp-relay@1:dhcp-server@4:firewall@5:ipsec@5:nat@3:qos@1:quagga@2:system@4:ubnt-pptp@1:ubnt-udapi-server@1:ubnt-unms@1:ubnt-util@1:vrrp@1:webgui@1:webproxy@1:zone-policy@1" === */
/* Release version: v1.10.5.5098943.180622.1555 */

Save this config with a different name and upload it to the routers /config/ directory. In SSH loading this configuration would look like this;

user@ubnt:~$ configure
[edit]
user@ubnt# load config.boot-buedt
Loading configuration from '/config/config.boot-buedt'...
[edit]
user@ubnt# commit
[edit]
user@ubnt# save
[edit]
user@ubnt# exit
exit
user@ubnt:~$

Once loaded with no errors the new firewall is in place and your BMS local LAN is as secure as practically possible IMO.