{"id":13587390,"url":"https://github.com/benalexau/PiZWG","last_synced_at":"2025-04-07T21:33:54.272Z","repository":{"id":143129089,"uuid":"322760701","full_name":"benalexau/PiZWG","owner":"benalexau","description":"Easily deploy a highly available, secure Z-Wave Gateway (ZWG) network service using Raspberry Pi 4 units","archived":true,"fork":false,"pushed_at":"2021-03-07T01:17:31.000Z","size":43,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-02-13T21:48:49.820Z","etag":null,"topics":["archlinuxarm","keepalived","openhab","openzwave","ser2net","socat","z-wave","zwave"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benalexau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-12-19T03:48:56.000Z","updated_at":"2024-02-13T21:48:50.806Z","dependencies_parsed_at":"2024-02-13T21:58:51.707Z","dependency_job_id":null,"html_url":"https://github.com/benalexau/PiZWG","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benalexau%2FPiZWG","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benalexau%2FPiZWG/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benalexau%2FPiZWG/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benalexau%2FPiZWG/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benalexau","download_url":"https://codeload.github.com/benalexau/PiZWG/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223295413,"owners_count":17121791,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["archlinuxarm","keepalived","openhab","openzwave","ser2net","socat","z-wave","zwave"],"created_at":"2024-08-01T15:06:11.320Z","updated_at":"2024-11-06T06:30:57.128Z","avatar_url":"https://github.com/benalexau.png","language":"Shell","funding_links":[],"categories":["Shell"],"sub_categories":[],"readme":"# PiZWG\n\n*PiZWG* allows you to easily and securely expose Z-Wave USB gateways (ZWG) to\nremote clients using Raspberry Pi 4 units.\n\n## Features\n\n* Ten minutes from download to a production-quality Z-Wave deployment\n* [Keepalived](https://github.com/acassen/keepalived) configured to deliver a\n  highly available network service\n* Optimised for widely available and low cost Raspberry Pi 4 hosts\n* Easy backup and restore approaches for peace of mind\n* File systems operate in read-only mode (eliminating SD card wear failure)\n* [Prometheus](https://prometheus.io/) enabled for easy HTTP-based monitoring\n* Easy, optional upgrades (minimal packages, rolling\n  [Arch Linux ARM](https://archlinuxarm.org/) distribution)\n* [uhubctl](https://github.com/mvp/uhubctl) disables USB ports until master\n* Hosts reboot for a known-clean state after any client disconnect or timeout\n* [PoE HAT](https://www.raspberrypi.org/products/poe-hat/) configured for near\n  silent operation\n\n## Motivation\n\nHome automation systems frequently incorporate specialised low-power radio mesh\nprotocols like Z-Wave and ZigBee. At the centre of these meshes is a \"gateway\"\n(or \"controller\"). At a hardware level these gateways are usually USB sticks\n(eg the very popular [Aeotech Z-Stick](https://aeotec.com/z-wave-usb-stick/)).\nOperating systems expose the USB sticks as devices (eg `/dev/ttyACM0`) and then\nsoftware like [openHAB](https://www.openhab.org/) or\n[OpenZWave](http://www.openzwave.com/) uses the gateway to monitor and control\nindividual devices.\n\nWhile the \"simplest thing that could possibly work\" is to install the Z-Wave\ngateway on the same host as running the home automation software, there are\nfrequently situations where this is infeasible. For example:\n\n* Multiple meshes (eg due to significant traffic volumes or deployed devices)\n* Physical constraints (eg large home, can't centrally locate gateway, racks)\n* 99.99%+ uptime SLA (eg limited traditional electrical controls like switches)\n* Cluster architectures (eg using Kubernetes to transparently relocate pods)\n* Engineering best practice (eg hardware hot spares, backup verification)\n\nThe usual recommendation in such situations is to deploy a single board computer\n(eg a Raspberry Pi) so its attached Z-Wave gateway can be placed in a desirable\nphysical location. `ser2net` is then run on the single board computer and a\nremote client uses `socat` to connect to it over TCP. While this approach is\nmature and widely understood, it does not provide:\n\n* High availability (what if the remote computer or its attached gateway fails?)\n* Security (are unauthorised hosts prevented from using the `ser2net` port?)\n* Backup verification (when was the \"backup\" computer and gateway last tested?)\n* Fast provisioning (how long does it take to recover if the computer fails?)\n\n## How It Works\n\n*PiZWG* is a ready to use Raspberry Pi 4 image that uses:\n\n* `ser2net` to expose the Z-Wave gateway as a `socat` accessible TCP service\n* `keepalived` to implement VRRP for automatic virtual IP address (VIP) failover\n* `uhubctl` to power on the USB hub (and Z-Wave gateway) only when \"master\"\n* Client and server certificates to authenticate and encrypt `ser2net` traffic\n* Scripts that provision the certificates and simplify their backup and restore\n* Services that automatically supervise, control and reboot hosts as appropriate\n\nThis combination facilitates fast and simple deployment of a secure, highly\navailable Z-Wave service.\n\n## Scope and Limitations\n\n*PiZWG* has been tested with Aeotec Z-Stick 5 and Aeotec Z-Stick Gen 5+ USB\ngateways. It will likely work with other gateways. The main requirement is that\nthe gateway mounts on a Linux machine as `/dev/ttyACM0`.\n\n*PiZWG* does not use the Z-Wave serial protocol in any way. It simply powers on\nand off a USB hub connected to the Raspberry Pi. This means you must:\n\n* Provide a USB hub that can be controlled by `uhubctl`\n* Properly backup and restore the Z-Wave gateway NVRAM following mesh changes\n\n*PiZWG* does not encrypt the file system or certificates. This is because most\npeople prefer a Z-Wave service to be very highly available with minimal delays\nor dependencies. While those interested in network bound disk encryption may\nfind [PiTang](https://github.com/benalexau/PiTang) helpful, there are no plans\nfor PiZWG to support Tang given doing so would substantially increase deployment\ncomplexity and failure modes with very few compensating benefits.\n\n## Getting Started\n\nStart by preparing your hardware:\n\n1. You will need a Raspberry Pi 4 with wired network access (PoE is optional)\n2. Use a quality SD card (at least 2 GB in size) and reliable power source\n3. Plug a USB hub (that can be controlled by `uhubctl`) into the Pi's upper\n   black USB port\n4. Plug the Z-Wave gateway into the aforementioned USB hub (not the Pi)\n\nNext prepare the subnet that *PiZWG* host(s) will be deployed on as follows:\n\n1. Ensure a DHCP IPv4 server is operating (address reservations not required)\n2. Ensure `xxx.xxx.xxx.254` is available for use as the *PiZWG* virtual IP (VIP)\n3. Ensure any existing VRRP deployment is not using router ID 254\n\nYou will need the *PiZWG* Raspberry Pi image. You can build it yourself using\nthe instructions at the bottom of this file, or you can [download the latest\nimage](https://github.com/benalexau/PiZWG/releases/tag/latest) created by\n[GitHub Actions](https://github.com/benalexau/PiZWG/actions).\n\nWrite the image file to an SD card using a command such as:\n\n```\nsudo dd if=pizwg-rp4.img of=/dev/sdd bs=4M \u0026\u0026 sync\n```\n\nPut the SD card in a Raspberry Pi and boot it. The system will obtain a DHCP\naddress, after which you can SSH in as root (password is also \"root\"). Next:\n\n1. Append your SSH key to `/root/.ssh/authorized_keys` (or use `ssh-copy-id`)\n2. Logout and login again over SSH to verify the certificate was used\n3. Edit `/etc/ssh/sshd_config`, changing `PermitRootLogin yes` to\n   `PermitRootLogin without-password` (`mg`, `vi` and `nano` are installed)\n4. Disable the root password using `passwd --lock root`\n\nIf this is your **first** *PiZWG* host, you need to:\n\n1. Run `pizwg-setup` to generate certificates and `/root/backup.tar`\n2. Copy `/root/backup.tar` (~20 KB) to a suitable remote location for backup\n3. Run `ro` to make the system read only (this survives reboots)\n4. Run `reboot` to verify everything works\n\nIf this is **NOT your first** *PiZWG* host, you need to:\n\n1. Copy `/root/backup.tar` from your first host (or backup) to the same filename\n   on your new host\n2. Run `pizwg-restore` to install the certificates in their correct locations\n3. Run `ro` to make the system read only (this survives reboots)\n4. Run `reboot` to verify everything works\n\nOnce you've booted up you should be able to `ping` the virtual IP address (VIP)\nmentioned earlier. If you have more than one PiZWG host you will notice that\nonly the current master will have its Z-Wave gateway remain energised.\n\n## Client Configuration\n\n`socat` allows a client to access the `ser2net` service active on the current\n*PiZWG* master. The client references the virtual IP address (VIP) rather than a\nspecific *PiZWG* host. A complete example command is shown below:\n\n```\nsocat -d -d -s -T30 pty,link=/dev/ttyUSB254,raw,user=root,group=root,mode=777 \\\nopenssl-connect:192.168.1.254:3333,cafile=etc/ser2net/ser2net.crt,commonname=pizwg-server,cert=root/pizwg-client.pem\n```\n\nThe above creates a `/dev/ttyUSB254` device connected to a *PiZWG* master VIP at\n`192.168.1.254`. The `crt` and `pem` certificate files should be extracted from\n`/root/backup.tar`. These provide encryption and mutual certificate\nauthentication.\n\nThe example command also used `-T30`. This ensures the connection is dropped if\nno traffic is sent or received in 30 seconds. Most Z-Wave networks have enough\nbackground traffic to allow such a timeout (or a power plug, light or similar\ncan probably be configured to regularly report energy consumption). You can use\na longer timeout if this is not the case. Once the timeout is reached the client\nwill disconnect its TCP connection, in turn causing the master *PiZWG* to\nrestart. In a high availability deployment a backup *PiZWG* host will\nimmediately acquire the VIP and be ready to receive a client connection.\n\nIn addition to client-initiated timeouts, the active master *PiZWG* host also\nimplements a 300 second inactivity timeout on the client TCP connection. This\nensures that the *PiZWG* host will reboot even if the remote client failed to\ndetect inactivity and drop the TCP connection.\n\nA [Kubernetes example](https://github.com/benalexau/PiZWG/tree/main/k8s) is also\navailable.\n\n## Monitoring\n\nIn general you don't need to track which *PiZWG* host is master. However you\nmay monitor individual hosts by HTTP requests to `http://host:9100/metrics`.\nThis is a standard Prometheus Node Exporter endpoint.\n\nYou may also SSH into the *PiZWG* host as root to view the status of\nservices. The important ones are:\n\n* `keepalived.service`: Configures VRRP settings (using MAC and DHCP-assigned\n   subnet) and allocates VIP to active master\n* `monitor.service`: Controls USB power, waits until active master, waits for\n   `ser2net` TCP loss (timeout, disconnect), then reboots host\n\nYou should not need to monitor `ser2net.service` as this is only started by the\n`monitor.service` when appropriate (ie once active master, USB powered up etc).\n\n## Security\n\n*PiZWG* is secure by design:\n\n* Image builds always include the latest available software from a popular\n  rolling Linux distribution (Arch Linux)\n* Downloads are built by GitHub Actions (with a public commit and build log)\n* Arch Linux's normal user account (`alarm`) is deleted\n* Unnecessary packages are not installed\n* Included packages are all mature and commonly used in production environments\n* SSH keys are generated on first boot (ie they're not in the image)\n* The setup instructions guide users to enhance security of the SSH daemon\n* `ser2net` keys are generated via `pizwg-setup` (they're not in the image)\n* `ser2net` monitoring is not network accessible (ie `localhost:4444` only)\n* `ser2net` performs mutual certificate authentication with clients\n\nThere are three open ports bound to network-accessible IPv4 addresses:\n\n* `ser2net` opens port 3333 so a client can connect to the USB serial port\n  (the client must authenticate using the expected client certificate)\n* Prometheus Node Exporter opens port 9100 to allow remote HTTP monitoring\n* SSH opens port 22 for administrative control (root password is disabled if\n  above setup instructions followed)\n\nYou should consider your threat model and apply appropriate mitigations.\nSome of the areas which may require consideration include:\n\n* Ensuring setup instructions were fully completed (eg SSH, root password)\n* Ensuring the `/root/backup.tar` is securely backed up (NB file is unencrypted)\n* Network level attacks such as ARP spoofing, denial of service etc\n* Physical access to the Raspberry Pi and/or the Z-Wave gateway\n* Exploiting unknown or unpatched vulnerabilities or misconfigurations\n\n## Software Updates\n\nThe PiZWG image uses Arch Linux, which is a popular rolling distribution. While\nthe author has operated Arch Linux for over 15 years and updates very rarely\nfail, the reality is that any upgrade brings with it some risk of failure and\nany potential benefits must be considered against that risk.\n\nIf later software is required, the recommended upgrade path is to download and\ndeploy the latest version of PiZWG, using the `backup.tar` backup and restore\napproach so that certificates can be transferred to the latest version. Deploy\nthe new version on a separate SD card from your existing deployment, therefore\nensuring there is a simple rollback plan for your existing service.\n\nIf you wish to attempt a rolling software update without installing a new\nversion of PiZWG:\n\n1. Consider copying the production SD card to a file on another machine (eg\n   `sudo dd if=/dev/sdd of=~/pizwg-prod.img bs=4M`)\n2. SSH into the PiZWG server as root\n3. Ensure that you have an up-to-date, remote backup of `backup.tar`\n4. Run `rw` to obtain a read-write file system\n5. Run `pacman -Syu` to perform the actual Arch Linux upgrade\n6. Run `ro` to return to a read-only file system\n7. Run `reboot` to test the upgrade was successful\n\nIf the above fails, you can either restore your PiZWG environment using the\nSD card backup image, or you can download a new PiZWG version and restore the\n`backup.tar` as per the usual backup and restore procedure.\n\n## Building\n\n[packer-builder-arm](https://github.com/mkaczanowski/packer-builder-arm) is used\nto build a standard image file that can be written to SD cards.\n\nTo build your own image, run:\n\n```\nPACKER_LOG=1 sudo packer build pizwg-rp4.json\n```\n## Contributing\n\nPull requests that reflect the project's priorities (reliability, security) are\nwelcome. If you would like to make more substantial changes or add new features,\nplease firstly open a GitHub ticket so that we can discuss it.\n\n## Support\n\nPlease open a GitHub ticket if you have any questions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenalexau%2FPiZWG","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenalexau%2FPiZWG","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenalexau%2FPiZWG/lists"}