Setting up IoTs ("S" for security) at home, wanted to add Zigbee too. Especially so as the port has been broken for quite a while.

Overview
The goal is
- for
zigbee2mqttto run in thezigbeejail - under
zigbeeuser - with the mosquitto network address the only allowed destination
- the jail will use an address on
lo0loopback.
Tasks
- Set up devfs to use the USB dongle in the
zigbeejail - Create a new jail for Zigbee2mqtt
- Create
zigbeeuser on host and in jail
Set up devfs ruleset for the USB dongle
I've bought a cheap USB dongle on Aliexpress to play with.
FreeBSD detects the dongle as C340 and creates the necessary device nodes.
The serial interface shows up as ttyU0.
I've chosen the first free number (6) for the ruleset. This is required in the jail config.
[devfsrules_jail_zigbee=6]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add path 'ttyU0*' unhide group 1883 mode 660
Jail setup
The minimal-jail pkgset wasn't enough to install, but it was enough to run. I use "thin" jails that share the base jail. This ties nicely in to the breakage of the port: it needs to compile things so you need clang.
I've ended up with the folloing jail.conf file `/etc/jail.conf.d/zigbee.conf
zigbee {
host.hostname = "zigbee";
path = "/jails/zigbee";
ip4.addr += "lo0|127.0.0.20/24";
allow.raw_sockets = 0;
exec.clean;
exec.system_user = "root";
exec.jail_user = "root";
exec.start += "/bin/sh /etc/rc";
exec.stop = "";
exec.consolelog = "/var/log/jail_zigbee_console.log";
mount.fstab = "/etc/fstab.zigbee";
mount.devfs;
devfs_ruleset = "6";
mount.fdescfs;
allow.set_hostname = 0;
allow.sysvipc = 0;
enforce_statfs = "2";
}
and in /etc/fstab.zigbee
/jails/basefull150 /jails/zigbee/basejail nullfs ro 0 0
And start the jail. Verify it is running.
Create a zigbee user
On the host system we create a zigbee user without login rights or homedir. This way the process shows up under user "zigbee" on the host.
pw useradd zigbee -u 1883 -d /var/empty -s /usr/bin/nologin
In the jail, we'll give it a homedir /home/zigbee which we'll use for npm's storage.
jexec zigbee useradd zigbee -u 1883 -d /home/zigbee -m -s /bin/sh
Config for (temporary) internet access
pkg, git and npm need access to internet resources.
# Add to /usr/local/etc/pkg.conf
PKG_ENV {
http_proxy = "http://squid.example.org:3128";
}
git config --global http.proxy = http://squid.example.org:3128
npm config set proxy http://squid.example.org:3128
NOTE: this works once you've installed npm using pkg
To use pkg, you must allow the following in squid
- pkg.freebsd.org
- pkgmir.geo.freebsd.org
- (pkg0.fra.freebsd.org?)
- (cloudfront.aws.pkgbase.freebsd.org?)
Your squid config will have to allow the zigbee jail to use
- github.com
- registry.npmjs.org
- nodejs.org
Your squid access logs will show what went wrong.
Install
Install required packages in the jail
Adapted from this PR.
pkg -r /jails/zigbee install npm-node24 python git-lite bash
(bash for later updating of zigbee2mqtt using the update.sh script)
Now we switch to the zigbee user.
The rest of the install is as zigbee.
su -l zigbee
mkdir $HOME/bin $HOME/lib
export PATH=$PATH:$HOME/bin
Get the source (check for latest tag)
cd /usr/local
git clone --depth=1 https://github.com/Koenkk/zigbee2mqtt.git z2m
git remote set-branches --add origin 2.7.2
git fetch origin 2.7.2:2.7.2
git checkout 2.7.2
Your zigbee2mqtt now lives in /usr/local/z2m.
Configure npm, install pnpm, and install zigbee2mqtt:
npm set prefix="$HOME"
npm config set proxy http://squid.example.org:3128
npm install -g pnpm@latest-9
which pnpm
/home/zigbee/bin/pnpm
pnpm --version
9.15.9
pnpm i --frozen-lockfile
pnpm run build
This should finish without errors.
Check and run
Inside your zigbee jail, you should see the USB dongle
/dev/ttyU0
/dev/ttyU0.init
/dev/ttyU0.lock
You can now start z2m by creating an rc-script /usr/local/etc/rc.d/z2m.
This comes from the comms/zigbee2mqtt port.
#!/bin/sh
# PROVIDE: z2m
# REQUIRE: DAEMON
# KEYWORD: shutdown
# FreeBSD rc.d script for zigbee2mqtt
#
# The z2m service has the following rc.conf options:
#
# z2m_enable (bool): Set to YES to enable z2m
# Default: NO
# z2m_user (str): The user to run z2m as
# Default: z2m
# z2m_group (str): The group to run z2m as
# Default: z2m
# z2m_chdir (str): The directory where z2m is installed
# Default: /usr/local/z2m
# z2m_datadir (str): The directory where z2m's data is stored
# Default: /var/db/z2m
# z2m_restart (bool): Set to YES if z2m should be automatically
# restarted after it crashes.
# Default: NO
. /etc/rc.subr
name=z2m
desc="zigbee2mqtt service"
rcvar=z2m_enable
load_rc_config $name
: ${z2m_enable:=NO}
: ${z2m_group:=zigbee}
: ${z2m_datadir:=/var/db/z2m}
: ${z2m_pidfile=/var/run/z2m/z2m.pid}
: ${z2m_restart=NO}
: ${z2m_user:=zigbee}
: ${z2m_chdir=/usr/local/z2m}
: ${z2m_env:="ZIGBEE2MQTT_DATA=${z2m_datadir}"}
# If z2m_restart is YES, then restart z2m when it crashes, otherwise
# daemon(8) will exit.
if checkyesno z2m_restart; then
_restartargs="-r"
else
_restartargs=""
fi
pidfile=${z2m_pidfile}
command=/usr/sbin/daemon
command_args="-f -H \
-P ${pidfile} -t ${name} -T ${name} \
${_restartargs} \
/usr/local/bin/node index.js"
required_files="${z2m_datadir}/configuration.yaml"
start_precmd="[ -d ${pidfile%/*} ] || install -d -o ${z2m_user} -g ${z2m_group} ${pidfile%/*}"
run_rc_command "$1"
You can now enable the service
service z2m enable
and start!
service z2m start
