22 Oct 2023

PF: Limit network access for Mill Heater

Mill is an interesting vendor with cloud connection with open API as well as a documented local API. This post is about Mill Heater 1200W CO1200WIFI3. However, as any other IOT vendor, there is an overly complicated internal setup process when installed on an segregated IOT network.

Setup

Initial setup is done over bluetooth, so no need for mDNS etc. From there, the mobile app connects over the internet to the mothership to controll the heater.

Communication overview

The heater diagnoses its outbound network connectivity with ICMP, sending pings to some common DNS server. If the API endpoint is reachable but ICMP is blocked, it could fail connecting because of a silly ping timeout.

At the time of writing and given that this might not be 100% accurate (didn’t spend too much time analyzing): Under normal operation, The heater spends its time doing https against Amazon ECS every minute, probably pulling tasks from the mothership and uploading the latest sensordata. After bootup it syncs time against cloudflare and also does some checkups against Hjelm Enterprises. I suspect this is where user information is stored. They have done some kind of platform migration going on late 2023, so this might change.

Things happening at bootup:

After a successful setup, it exposes port 80 with some basic status of the heater such as temperature.

Note

If you have Unifi AP, you need to disable wireless meshing in order to complete the setup. This seems to be an ESP32 “feature”, sadly. However, once everything is setup properly you can enable meshing again.

If you want to poll the local API, you need to set a password. After this password is set, the heater will reboot and enable port 443 and disable port 80.

Mill claims this on github:

That server uses self-signed certificate which can generate some warning massages but will provide better security. 

Which is quite an hmmhmphf…

The https certificate reveals that the webserver runs on a ESP32 SoC with integrated bluetooth and wifi.

For some reason, the https web interface looks really broken now, if you open the css file it will respond with “Failed to create response body” and API requests fails. Not exactly sure how this is supposed to work locally now, but at least it works from their public API portal. Might investigate further when bored.

There might be some additional rules need for the setup process, but when its up and running it works with this (configs below are from OpenBSD 7.4 with PF logging enabled):

/etc/pf.conf snippet

VLAN_MILL_IOT="vlan92"
VLAN_CLIENTS="vlan418"

block log on $VLAN_MILL_IOT

pass out log on $VLAN_MILL_IOT inet from $VLAN_CLIENTS:network to $VLAN_MILL_IOT:network
pass in log on $VLAN_MILL_IOT inet proto udp from $VLAN_MILL_IOT:network to 10.62.1.1 port 53
pass in log on $VLAN_MILL_IOT inet proto udp from $VLAN_MILL_IOT:network to any port 53
pass in log on $VLAN_MILL_IOT inet proto udp from $VLAN_MILL_IOT:network to any port 123

pass in log on $VLAN_MILL_IOT inet proto tcp from $VLAN_MILL_IOT:network to any port { https, http}
pass in log on $VLAN_MILL_IOT inet proto {tcp, udp} from $VLAN_MILL_IOT:network to any port 1883
# Allow it to use ping to test connectivity
pass in log on $VLAN_MILL_IOT inet proto icmp from $VLAN_MILL_IOT:network to any

pass in log on $VLAN_MILL_IOT inet from $VLAN_MILL_IOT:network to $VLAN_MILL_IOT:network
# Mothership connectivity
pass out on egress inet from $VLAN_MILL_IOT:network to any nat-to (egress)