ha: bridge the Home Assistant VM onto br0 as a first-class LAN device
The VM lived on libvirt's default NAT net (192.168.122.50) behind a /etc/libvirt/hooks/network hook that inserted FORWARD accepts above libvirt's REJECT. libvirtd restarts re-inserted its chains on top without firing the hook, silently cutting clients off from HA. Now the VM's NIC (virtio, was e1000) bridges into br0: pinned lease 10.42.0.50 / name 'homeassistant' via van-ap dnsmasq, mDNS discovery works, and no firewall hook can rot. Legacy http://10.42.0.1:8123 kept working via DNAT + same-subnet hairpin masquerade. Hook, libvirt DHCP reservation, and iptables rules removed; domain XML in ha/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
4117802f69
commit
a7819edbc2
@@ -55,6 +55,7 @@ This directory is the source of truth. The live system files live under `/etc`,
|
|||||||
- Cockpit: `https://192.168.10.251:9090` (or `.27`, or ZeroTier). Log in with a Unix account; enable *Administrative access* for action buttons.
|
- Cockpit: `https://192.168.10.251:9090` (or `.27`, or ZeroTier). Log in with a Unix account; enable *Administrative access* for action buttons.
|
||||||
- Regulatory domain **CA** (unlocks 5GHz ch149–161 @30dBm, no DFS).
|
- Regulatory domain **CA** (unlocks 5GHz ch149–161 @30dBm, no DFS).
|
||||||
- ZeroTier network `d3ecf5726d041b2a`, pushed DNS domain `wrede.pvt` via `192.168.196.115` + `192.168.10.5`.
|
- ZeroTier network `d3ecf5726d041b2a`, pushed DNS domain `wrede.pvt` via `192.168.196.115` + `192.168.10.5`.
|
||||||
|
- Home Assistant (HAOS in libvirt VM `ha_van`) is bridged into `br0` at **`10.42.0.50:8123`** (pinned DHCP lease, name `homeassistant`); legacy URL `http://10.42.0.1:8123` still works via DNAT.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -127,6 +128,14 @@ This directory is the source of truth. The live system files live under `/etc`,
|
|||||||
The `hbc` binary itself (venv at `~/venvs/hbd`, symlink `~/bin/hbc`) is installed once via the
|
The `hbc` binary itself (venv at `~/venvs/hbd`, symlink `~/bin/hbc`) is installed once via the
|
||||||
heartbeat project's own installer — see §4. `deploy.sh` only starts the service once it exists.
|
heartbeat project's own installer — see §4. `deploy.sh` only starts the service once it exists.
|
||||||
|
|
||||||
|
### `ha/` — Home Assistant VM
|
||||||
|
| file | → installs to | purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `ha_van.xml` | *(reference only — `virsh define ha/ha_van.xml` to restore)* | libvirt domain: HAOS VM, virtio NIC bridged onto `br0` (MAC `52:54:00:ad:0a:01`). Disk image lives outside the repo. |
|
||||||
|
|
||||||
|
Not touched by `deploy.sh` — the VM's LAN address/name come from `ap/van-ap-dnsmasq.conf`
|
||||||
|
(`dhcp-host` → `10.42.0.50`, `homeassistant`) and the legacy-URL DNAT from `ap/nftables.conf`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Components & how to operate
|
## 3. Components & how to operate
|
||||||
@@ -185,6 +194,21 @@ heartbeat project's own installer — see §4. `deploy.sh` only starts the servi
|
|||||||
- Read-only status works as any user; Prefer/Up/Down/Restart need *Administrative access* (polkit).
|
- Read-only status works as any user; Prefer/Up/Down/Restart need *Administrative access* (polkit).
|
||||||
- **Prefer** sets the manual WAN preference (see *Manual preference* above); **Up/Down** connect/disconnect the NM device; **Restart** restarts hostapd.
|
- **Prefer** sets the manual WAN preference (see *Manual preference* above); **Up/Down** connect/disconnect the NM device; **Restart** restarts hostapd.
|
||||||
|
|
||||||
|
### Home Assistant VM (`ha_van`)
|
||||||
|
- HAOS runs as a libvirt KVM VM whose NIC is **bridged into `br0`** — it is a first-class LAN
|
||||||
|
device, not NAT'd behind libvirt's `virbr0`. VanLink + wired clients reach it directly at
|
||||||
|
`http://10.42.0.50:8123` (or `http://homeassistant:8123` / `homeassistant.local` via mDNS);
|
||||||
|
ZT clients route in via the ZT-managed `10.42.0.0/24` route. mDNS/SSDP device discovery works
|
||||||
|
because the VM shares the clients' L2 segment.
|
||||||
|
- The old NAT-era URL `http://10.42.0.1:8123` keeps working: nftables DNATs it to `10.42.0.50`,
|
||||||
|
with a hairpin masquerade for same-subnet clients (see comments in `ap/nftables.conf`).
|
||||||
|
- History: the VM used to sit on libvirt's `default` NAT net (`192.168.122.50`) with a
|
||||||
|
`/etc/libvirt/hooks/network` hook inserting FORWARD accepts above libvirt's REJECT. That broke
|
||||||
|
whenever libvirtd re-inserted its chains on restart (hook doesn't fire then) — bridging removed
|
||||||
|
the whole failure mode. The hook and the libvirt DHCP reservation are gone.
|
||||||
|
- Operate: `virsh {start,shutdown,domstate} ha_van`; autostart is per libvirt config. Verify:
|
||||||
|
`curl -s -o /dev/null -w '%{http_code}' http://10.42.0.50:8123/` → `200`.
|
||||||
|
|
||||||
### Never sleep (lid-closed operation)
|
### Never sleep (lid-closed operation)
|
||||||
- wayback lives lid-closed in the van and must stay up. Stock logind `HandleLidSwitch=suspend` would sleep it on lid close (even on AC). The `power/10-vanlink-nolid.conf` drop-in sets all three lid actions to `ignore`; `deploy.sh` also masks every sleep target so idle / GUI / a stray `systemctl suspend` can't suspend it.
|
- wayback lives lid-closed in the van and must stay up. Stock logind `HandleLidSwitch=suspend` would sleep it on lid close (even on AC). The `power/10-vanlink-nolid.conf` drop-in sets all three lid actions to `ignore`; `deploy.sh` also masks every sleep target so idle / GUI / a stray `systemctl suspend` can't suspend it.
|
||||||
- Verify: `busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager HandleLidSwitch` → `"ignore"`, and `systemctl is-enabled suspend.target` → `masked`. `busctl call ... CanSuspend` should return `"no"`.
|
- Verify: `busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager HandleLidSwitch` → `"ignore"`, and `systemctl is-enabled suspend.target` → `masked`. `busctl call ... CanSuspend` should return `"no"`.
|
||||||
|
|||||||
+9
-12
@@ -6,21 +6,18 @@ delete table ip van_router_nat
|
|||||||
table ip van_router_nat {
|
table ip van_router_nat {
|
||||||
chain prerouting {
|
chain prerouting {
|
||||||
type nat hook prerouting priority dstnat; policy accept;
|
type nat hook prerouting priority dstnat; policy accept;
|
||||||
# Expose the Home Assistant VM (on libvirt's NAT net, 192.168.122.50) on the
|
# Home Assistant VM (ha_van) is bridged onto br0 at 10.42.0.50 — clients reach
|
||||||
# router's own LAN IP, so VanLink clients reach it at http://10.42.0.1:8123.
|
# it directly. Keep the legacy http://10.42.0.1:8123 URL working for anything
|
||||||
# The matching FORWARD accept lives in iptables (re-applied by the libvirt
|
# that bookmarked it (phones, ZT clients).
|
||||||
# /etc/libvirt/hooks/network hook), since libvirt's reject is in that chain.
|
ip daddr 10.42.0.1 tcp dport 8123 dnat to 10.42.0.50:8123
|
||||||
ip daddr 10.42.0.1 tcp dport 8123 dnat to 192.168.122.50:8123
|
|
||||||
}
|
}
|
||||||
chain postrouting {
|
chain postrouting {
|
||||||
type nat hook postrouting priority srcnat; policy accept;
|
type nat hook postrouting priority srcnat; policy accept;
|
||||||
ip saddr 10.42.0.0/24 oifname != "br0" masquerade
|
ip saddr 10.42.0.0/24 oifname != "br0" masquerade
|
||||||
# ZeroTier clients reaching the HA VM via the 10.42.0.1:8123 DNAT: masquerade
|
# Hairpin for the legacy 10.42.0.1:8123 DNAT when the client sits on the same
|
||||||
# them onto virbr0 so HA sees the router (192.168.122.1) and the reply path is
|
# subnet as the HA VM: without masquerade the VM would reply directly on br0
|
||||||
# symmetric, exactly like VanLink clients. Matched by ZT ingress interface so it
|
# from 10.42.0.50 and the client (expecting 10.42.0.1) would drop it. ZT-sourced
|
||||||
# covers direct members (192.168.196.x) AND the LAN subnets routed in behind other
|
# traffic doesn't match and doesn't need it — VM replies route back through us.
|
||||||
# ZT nodes (192.168.0/6/8/10/13.x). Scoped to virbr0 so ZT<->ZT and ZT->WAN are
|
ip saddr 10.42.0.0/24 ip daddr 10.42.0.50 tcp dport 8123 oifname "br0" masquerade
|
||||||
# untouched.
|
|
||||||
iifname "ztuga7c2kh" oifname "virbr0" masquerade
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,8 @@ domain-needed
|
|||||||
bogus-priv
|
bogus-priv
|
||||||
dhcp-authoritative
|
dhcp-authoritative
|
||||||
dhcp-range=10.42.0.10,10.42.0.254,255.255.255.0,12h
|
dhcp-range=10.42.0.10,10.42.0.254,255.255.255.0,12h
|
||||||
|
# Home Assistant VM (libvirt ha_van, bridged onto br0) — pinned address, resolves
|
||||||
|
# as "homeassistant" via this dnsmasq; HAOS also announces homeassistant.local (mDNS).
|
||||||
|
dhcp-host=52:54:00:ad:0a:01,10.42.0.50,homeassistant
|
||||||
dhcp-option=option:router,10.42.0.1
|
dhcp-option=option:router,10.42.0.1
|
||||||
dhcp-option=option:dns-server,10.42.0.1
|
dhcp-option=option:dns-server,10.42.0.1
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
<domain type='kvm'>
|
||||||
|
<name>ha_van</name>
|
||||||
|
<uuid>af014c94-de20-4f52-8d6b-438f16cd82e6</uuid>
|
||||||
|
<description>Home Assistant OS</description>
|
||||||
|
<memory unit='KiB'>4194304</memory>
|
||||||
|
<currentMemory unit='KiB'>4194304</currentMemory>
|
||||||
|
<vcpu placement='static'>2</vcpu>
|
||||||
|
<os firmware='efi'>
|
||||||
|
<type arch='x86_64' machine='pc-i440fx-noble-v2'>hvm</type>
|
||||||
|
<firmware>
|
||||||
|
<feature enabled='no' name='enrolled-keys'/>
|
||||||
|
<feature enabled='no' name='secure-boot'/>
|
||||||
|
</firmware>
|
||||||
|
<loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE_4M.fd</loader>
|
||||||
|
<nvram template='/usr/share/OVMF/OVMF_VARS_4M.fd'>/var/lib/libvirt/qemu/nvram/ha_van_VARS.fd</nvram>
|
||||||
|
<boot dev='hd'/>
|
||||||
|
</os>
|
||||||
|
<features>
|
||||||
|
<acpi/>
|
||||||
|
<apic/>
|
||||||
|
</features>
|
||||||
|
<cpu mode='host-passthrough' check='none' migratable='on'/>
|
||||||
|
<clock offset='utc'>
|
||||||
|
<timer name='rtc' tickpolicy='catchup'/>
|
||||||
|
<timer name='pit' tickpolicy='delay'/>
|
||||||
|
<timer name='hpet' present='no'/>
|
||||||
|
</clock>
|
||||||
|
<on_poweroff>destroy</on_poweroff>
|
||||||
|
<on_reboot>restart</on_reboot>
|
||||||
|
<on_crash>destroy</on_crash>
|
||||||
|
<pm>
|
||||||
|
<suspend-to-mem enabled='no'/>
|
||||||
|
<suspend-to-disk enabled='no'/>
|
||||||
|
</pm>
|
||||||
|
<devices>
|
||||||
|
<emulator>/usr/bin/qemu-system-x86_64</emulator>
|
||||||
|
<disk type='file' device='disk'>
|
||||||
|
<driver name='qemu' type='qcow2'/>
|
||||||
|
<source file='/var/lib/libvirt/images/haos_ova-17.3.qcow2'/>
|
||||||
|
<target dev='sda' bus='scsi'/>
|
||||||
|
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
|
||||||
|
</disk>
|
||||||
|
<controller type='scsi' index='0' model='virtio-scsi'>
|
||||||
|
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
|
||||||
|
</controller>
|
||||||
|
<controller type='usb' index='0' model='ich9-ehci1'>
|
||||||
|
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/>
|
||||||
|
</controller>
|
||||||
|
<controller type='usb' index='0' model='ich9-uhci1'>
|
||||||
|
<master startport='0'/>
|
||||||
|
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0' multifunction='on'/>
|
||||||
|
</controller>
|
||||||
|
<controller type='usb' index='0' model='ich9-uhci2'>
|
||||||
|
<master startport='2'/>
|
||||||
|
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/>
|
||||||
|
</controller>
|
||||||
|
<controller type='usb' index='0' model='ich9-uhci3'>
|
||||||
|
<master startport='4'/>
|
||||||
|
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/>
|
||||||
|
</controller>
|
||||||
|
<controller type='pci' index='0' model='pci-root'/>
|
||||||
|
<interface type='bridge'>
|
||||||
|
<mac address='52:54:00:ad:0a:01'/>
|
||||||
|
<source bridge='br0'/>
|
||||||
|
<model type='virtio'/>
|
||||||
|
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
|
||||||
|
</interface>
|
||||||
|
<serial type='pty'>
|
||||||
|
<target type='isa-serial' port='0'>
|
||||||
|
<model name='isa-serial'/>
|
||||||
|
</target>
|
||||||
|
</serial>
|
||||||
|
<console type='pty'>
|
||||||
|
<target type='serial' port='0'/>
|
||||||
|
</console>
|
||||||
|
<input type='mouse' bus='ps2'/>
|
||||||
|
<input type='keyboard' bus='ps2'/>
|
||||||
|
<tpm model='tpm-crb'>
|
||||||
|
<backend type='emulator' version='2.0'/>
|
||||||
|
</tpm>
|
||||||
|
<audio id='1' type='none'/>
|
||||||
|
<memballoon model='virtio'>
|
||||||
|
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
|
||||||
|
</memballoon>
|
||||||
|
</devices>
|
||||||
|
</domain>
|
||||||
|
|
||||||
Reference in New Issue
Block a user