Compare commits

...
Author SHA1 Message Date
claude-botandClaude Opus 5.5 7ecd10702e fix(authentik): turn off error reporting
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
error_reporting.enabled has been true since the first deploy. The server
hands the Sentry DSN (authentik.error-reporting.a7k.io) to every outpost
through /api/v3/root/config/, and the RADIUS and LDAP outposts then try
to send reports there; egress policy drops all of it. Reporting to a
third party was never intended to work here.

S28-43

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 22:38:11 +01:00
security-bot a6fdcf54f7 chore(image-automation): bump gitea.renner.uno/konairius/shelly-manager:2026-11-07-2052
Updates managed by image-automation-controller.
2026-11-07 21:21:33 +00:00
k.renner 1a3368e70d Merge pull request 'feat(home-assistant): let matter-server and zigbee2mqtt reach their update sources' (#1603) from feat/matter-z2m-egress into main
Reviewed-on: #1603
2026-11-07 21:21:19 +00:00
claude-botandClaude Opus 5.5 82165d6704 feat(home-assistant): let matter-server and zigbee2mqtt reach their update sources
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
matter-server fetches PAA root certificates, vendor names and OTA
metadata from the CSA DCL (on.dcl.csa-iot.org) and the development PAA
certificates from project-chip on GitHub (python-matter-server
helpers/paa_certificates.py, vendor_info.py, ota/dcl.py). zigbee2mqtt
fetches the Koenkk/zigbee-OTA index and images from
raw.githubusercontent.com (zigbee-herdsman ota.ts). The namespace
baseline denied all of it (~350 and ~200 drops a day), so certificate
refresh, vendor names and OTA silently failed. FQDN-scoped, 443 only.

S28-42

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 22:20:15 +01:00
k.renner 2ff8e216e1 Merge pull request 'fix(monitoring): turn off Alloy and InfluxDB usage telemetry' (#1602) from fix/disable-telemetry into main
Reviewed-on: #1602
2026-11-07 21:12:09 +00:00
claude-botandClaude Opus 5.5 4589ef3747 fix(monitoring): turn off Alloy and InfluxDB usage telemetry
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
Both phone home on a timer: every alloy pod to stats.grafana.org (~135
a day each) and influxd to telemetry.influxdata.com (56 a day). Egress
policy denies it, so this only removed log noise and wasted SYNs; switch
it off at the source like Loki's reporting_enabled: false.
--disable-reporting per the "alloy run" reference (chart alloy.extraArgs);
INFLUXD_REPORTING_DISABLED per the InfluxDB v2 config options.

S28-41

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 22:10:52 +01:00
k.renner dee425fd2f Merge pull request 'fix(image-automation): scan the Immich repositories every 6h' (#1601) from fix/immich-image-scan-interval into main
Reviewed-on: #1601
2026-11-07 21:09:12 +00:00
claude-botandClaude Opus 5.5 59cc813a87 fix(image-automation): scan the Immich repositories every 6h
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
immich-machine-learning has ~222k tags and immich-server ~33k, nearly
all PR builds. Each anonymous scan pages the full list (about 220
requests of 1000 tags for ML) and hourly scans hit ghcr.io's
TOOMANYREQUESTS. Immich releases a few times a week at most, so 6h
costs nothing.

S28-40

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 22:07:33 +01:00
k.renner 979e3910ca Merge pull request 'fix(authentik): admit the UDM's LDAPS sync to the LDAP outpost' (#1600) from fix/ldap-outpost-udm-ldaps into main
Reviewed-on: #1600
2026-11-07 21:03:33 +00:00
claude-botandClaude Opus 5.5 6a0dbf9e26 fix(authentik): admit only the UDM's own address, not all of k8s-ingress
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
New CIDR group udm-k8s-ingress (10.2.47.1/32, fd02:47::1/128); the LDAPS
rule references it instead of the whole net-k8s-ingress network.

S28-39

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 22:02:30 +01:00
claude-botandClaude Opus 5.5 e25f1a43f8 fix(authentik): admit the UDM's LDAPS sync to the LDAP outpost
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was canceled
The UDM authenticates admins against ldap.renner.uno:636 and syncs
hourly. It sources from its k8s-ingress address (10.2.47.1 /
fd02:47::1), which ldap-outpost-lan-ingress did not admit: 200 SYNs a
day dropped at :26 every hour. Same group the alloy-syslog and CoreDNS
policies use for the UDM; LDAPS only.

S28-39

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 22:00:21 +01:00
k.renner a4e5f99ca1 Merge pull request 'docs(trmnl): note the expected Google drops from the renderer's Chromium' (#1599) from docs/trmnl-renderer-google-drops into main
Reviewed-on: #1599
2026-11-07 20:02:17 +00:00
claude-botandClaude Opus 5.5 17839eeeab docs(trmnl): note the expected Google drops from the renderer's Chromium
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
About 43k POLICY_DENIED a day from trmnl-renderer to Google update and
account hosts. Source (OCI label): usetrmnl/trmnl-home-assistant dd556e6;
the Chromium flags are a fixed list in screenshot.ts:78 with no env or
option override, and --disable-background-networking is not among them.
Nothing on our side can switch it off; the denies are correct.

S28-4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 21:00:44 +01:00
k.renner f6fc79fce9 Merge pull request 'fix(mdns-relay): turn off RA on net1 so the IPv6 forward uses eth0' (#1598) from fix/mdns-relay-no-ra into main
Reviewed-on: #1598
2026-11-07 19:43:47 +00:00
claude-botandClaude Opus 5.5 583fc044cb fix(mdns-relay): turn off RA on net1 so the IPv6 forward uses eth0
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
sbr confines only the DHCP (IPv4) routes. net1 still takes a SLAAC address
and an RA default route, so socat-ipv6 sent every relayed datagram out the
IoT VLAN with source fd02:30::…; it came back through the UDM and HA's
CNP dropped it (37k POLICY_DENIED to home-assistant:5353 in 24 h). No IPv6
mDNS announcement reached HA. Same tuning as shelly-manager-iot. The
annotation bump re-attaches net1 with the new NAD.

S28-36

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:41:46 +01:00
k.renner f7deb40a2b Merge pull request 'feat(mqtt): register shelly-41 and seven Gen2 Shellies without an account' (#1597) from feat/mqtt-shelly-41 into main
Reviewed-on: #1597
2026-11-07 19:29:05 +00:00
claude-botandClaude Opus 5.5 b3bf09ebc3 feat(mqtt): register shelly-40, the Flood G4
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
Battery device, entered like the sleeping H&Ts.

S28-35

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:28:18 +01:00
claude-botandClaude Opus 5.5 5afb29dcb2 feat(mqtt): register the seven Gen2 Shellies refused as not authorised
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was canceled
Devices 7, 11, 17, 18, 20, 23 and 28 have MQTT on and reconnect about
once a minute, each refused for lack of an account. Each reboots once
when its password is pushed.

S28-35

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:25:25 +01:00
claude-botandClaude Opus 5.5 e4b0b13c3e feat(mqtt): register shelly-41, the Gen1 plug in the laundry room
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was canceled
shelly-manager has MQTT enabled on the device (gen1 mqtt.enable,
mqtt.server mqtt.renner.uno:1883) and the plug reaches the broker, but it
is refused as not authorised: it has no account. With MQTT on, the
controller's push lands and the device reports connected, so it does not
loop on ROTATE_GRACE.

S28-35

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:23:41 +01:00
security-bot 00e443c397 chore(image-automation): bump gitea.renner.uno/konairius/shelly-manager:2026-11-07-1918
Updates managed by image-automation-controller.
2026-11-07 19:21:33 +00:00
k.renner b2b4cd4ab9 Merge pull request 'chore(edge): retire the open3e MQTT bridge from all edge hosts' (#1596) from chore/edge1-retire-mqtt-bridge into main
Reviewed-on: #1596
Reviewed-by: Review Bot <review-bot@renner.uno>
2026-11-07 13:50:36 +00:00
claude-botandClaude Opus 5.5 6baf3139b9 chore(edge): retire the MQTT bridge from edge-2 too, drop the role
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
edge-2's bridge carried only open3e/# as well, with nothing publishing to it
(owner decision 2026-11-07). The mqtt-bridge role is gone entirely.

- delete the role mqtt-bridge and the play install-mqtt-bridge.yml with its
  site.yml import and the inventory group mqtt_bridge
- install-edge-host.yml pre_tasks remove the bridge from every edge host:
  stop and disable edge-mqtt-cred and mosquitto (if present), delete the unit,
  script, /etc/edge-mqtt-cred and /etc/mosquitto, uninstall mosquitto
- group_vars edge_host: drop the local MQTT contract (edge_mqtt_host/port,
  edge_open3e_mqtt_user/password) and the mosquitto/edge-mqtt-cred sudo units
- register: drop the account edge-2; controller egress: drop
  edge-2.iot.renner.uno:8443
- docs: drop the MQTT bridge section and the vault_edge_open3e_mqtt_password
  entry

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 14:44:08 +01:00
claude-botandClaude Opus 5.5 ff6ad64783 chore(edge): retire the open3e MQTT bridge from edge-1
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was canceled
Nothing on edge-1 publishes to its loopback mosquitto any more: the CAN stack is
gone (#1595) and vitocal-can publishes open3e/# to the cluster broker itself.
The bridge carried only open3e/# out and open3e/cmnd in.

- mqtt-bridge leaves the edge base play; the new play install-mqtt-bridge.yml
  applies it to the inventory group mqtt_bridge (edge-2) and removes it from
  every other edge host: stop and disable edge-mqtt-cred and mosquitto, delete
  the unit, script, /etc/edge-mqtt-cred and /etc/mosquitto/bridge.d,
  uninstall mosquitto
- register: drop the account edge-1 (readwrite open3e/#)
- mqtt-account-controller-egress: drop edge-1.iot.renner.uno:8443
- docs: the bridge runs only on [mqtt_bridge] hosts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 14:40:59 +01:00
k.renner 07e1684bd1 Merge pull request 'chore(edge): retire the heating-can CAN stack from edge-1' (#1595) from chore/retire-heating-can into main
Reviewed-on: #1595
2026-11-07 13:01:27 +00:00
27 changed files with 178 additions and 499 deletions
+2 -51
View File
@@ -61,7 +61,7 @@ inventory change.
| Nabu Casa ZWA-2 Z-Wave radio | `zwave_gateway` | `plays/install-zwave-gateway.yml` |
Add the host to the matching group, then run that playbook. It depends on
the base play having run: it needs the local MQTT bridge and docker.
the base play having run: it needs docker.
A host with nothing attached is simply absent from these groups and needs
none of these commands.
@@ -69,11 +69,7 @@ none of these commands.
## Secrets (before first play run)
Create `inventory/group_vars/edge_host/vault.yml` with ansible-vault. The
cluster broker has no `open3e` user: edge-1 logs in only as the register
account `edge-1` (see "MQTT bridge" below), so
`vault_edge_open3e_mqtt_password` reaches only the loopback broker, which
allows anonymous clients; any random value works (`openssl rand -hex 24`).
The zwave-js value comes out of the cluster:
zwave-js value comes out of the cluster:
```
# zwave-js session secret
@@ -82,7 +78,6 @@ kubectl -n home-assistant get secret zwave-js-session -o jsonpath='{.data.sessio
```yaml
# vault.yml
vault_edge_open3e_mqtt_password: "..."
vault_edge_zwavejs_session_secret: "..."
vault_edge_loki_password: "..." # dedicated edge-1 Loki push cred (see "Logging & eMMC")
```
@@ -194,47 +189,3 @@ journal commit).
- Verify the drop with the eMMC-write sampling described above; roll back by
removing the `LD_PRELOAD` env + `.so` mount from the compose file.
## MQTT bridge (TLS to the cluster broker)
open3e, e3oncan and sml2mqtt do **not** publish to the cluster broker directly.
They connect to a **local mosquitto on loopback** (`127.0.0.1:1883`,
`edge_mqtt_host`), which **bridges to the cluster broker over TLS**
(`mqtt.renner.uno:8883`). Two reasons:
- **Security:** open3e and e3oncan have no native MQTTS support (their MQTT
code does a plain `connect()`), so they can't use the broker's 8883 TLS
listener. Terminating TLS in the local broker keeps their plaintext MQTT on
loopback only — off the untrusted IoT VLAN — while the actual network hop is
encrypted. The public Let's Encrypt cert is validated against the system CA
bundle (`edge_mqtt_bridge_cafile`); no cert distribution needed.
- **Availability:** the local broker buffers telemetry (in RAM) if the cluster
broker is briefly unreachable, and flushes on reconnect — edge-1 no longer
hard-depends on cluster/network uptime for every publish.
The local broker is `allow_anonymous` on loopback — the trust boundary is the
host; nothing off-host can reach it.
**Bridge credential (REN-1307).** One bridge `connection` logs in as the
register account `edge-1` (`kubernetes/apps/home-assistant/mqtt-accounts.yaml`)
and carries `open3e/#` out and `open3e/cmnd` back. The cluster broker does not
accept the vault-password bridges in `bridge.d/legacy.conf`, which exists only
until the first push. Its password is pushed
by mqtt-account-controller to `edge-mqtt-cred` (`:8443`, the controller's
esp32 API over HTTPS). That endpoint accepts only a client certificate from
the MQTT CA with CN `mqtt-account-controller`; edge-1 holds the public CA and
its own server key, nothing else. On a push it writes
`/etc/mosquitto/bridge.d/edge-1.conf`, deletes `bridge.d/legacy.conf` and
restarts the local mosquitto. The controller sets a new password whenever the
bridge reports "not connected" (also after a reinstall), so no play run is
needed for a rotation. Diagnosis: `journalctl -u
edge-mqtt-cred`, and `mosquitto_sub -t '$SYS/broker/connection/edge-1/state'`
(retained `1` = connected).
**eMMC:** the local broker writes **nothing** to flash — `persistence false`
(retained/queued messages in RAM) and `log_dest stdout` (to the volatile
journal → Loki). Do not add persistence or log-file settings to
`edge-mqtt-bridge.conf.j2`.
Rollback: point `edge_mqtt_host` back to `mqtt.renner.uno` and re-run (clients
reconnect directly on 1883). Config: `edge-mqtt-bridge.conf.j2` →
`/etc/mosquitto/mosquitto.conf`.
@@ -2,7 +2,6 @@
# Secret indirection — real values live in vault.yml (ansible-vault) next
# to this file; see docs/edge-host.md for how to populate it from
# the cluster secrets.
edge_open3e_mqtt_password: "{{ vault_edge_open3e_mqtt_password }}"
edge_zwavejs_session_secret: "{{ vault_edge_zwavejs_session_secret }}"
# Loki push credential (dedicated 'edge-1' user, Traefik basic-auth on the
# loki.renner.uno ingress; the matching htpasswd line lives in the cluster
@@ -36,18 +35,8 @@ keepalived_ntp_sysctl:
# Realtek NIC on the Wyse 3040 has no hardware timestamping.
chrony_hwtimestamp: false
# --- Local MQTT contract -------------------------------------------------
# Shared between the broker and its clients, so it belongs to the inventory
# rather than to either side's role. mqtt-bridge listens here, and its legacy
# bridge authenticates to the cluster broker as the open3e user.
edge_mqtt_host: 127.0.0.1
edge_mqtt_port: 1883
edge_open3e_mqtt_user: open3e
# Units claude-bot may restart (ssh-ca role); the containers go through docker.
ssh_ca_claude_sudo_units:
- mosquitto
- edge-mqtt-cred
- edge-alloy
- keepalived
- chronyd
+3 -4
View File
@@ -18,8 +18,8 @@ proxy-1.renner.uno ansible_host=proxy-1.nb.renner.uno ansible_user=root
proxy-2.renner.uno ansible_host=proxy-2.nb.renner.uno ansible_user=root
# Every bare-metal edge thin client. Gets the host-generic roles only:
# flash-wear handling, the log shipper, the MQTT bridge, the VLAN
# sub-interface. Nothing here assumes any peripheral is attached.
# flash-wear handling, the log shipper, the VLAN sub-interface. Nothing here
# assumes any peripheral is attached.
[edge_host]
edge-1.iot.renner.uno ansible_user=root
edge-2.iot.renner.uno ansible_user=root
@@ -37,8 +37,7 @@ nas-2.renner.uno ansible_user=root
# These are children of [edge_host], declared below: a peripheral is added to an
# edge client, it does not replace the base. Making that a :children relation
# rather than a comment means a host added only to a capability group still
# resolves group_vars/edge_host -- where the local MQTT contract lives -- so it
# cannot silently come up without edge_mqtt_host and friends.
# resolves group_vars/edge_host.
[zwave_gateway]
edge-1.iot.renner.uno
+38 -3
View File
@@ -31,9 +31,45 @@
become: true
# The Viessmann E3 CAN bus is read by vitocal-can (open3e-esp32 at the
# Vitocal), not by an edge host. These remove the former heating-can stack
# and the can0 configuration; each is a no-op where they are absent.
# Vitocal), which publishes to the cluster broker itself, not by an edge
# host. These remove the former heating-can stack, the can0 configuration
# and the open3e MQTT bridge (loopback mosquitto + edge-mqtt-cred); each is
# a no-op where they are absent.
pre_tasks:
- name: Read the service list
ansible.builtin.service_facts:
- name: Stop and disable the retired MQTT bridge services
ansible.builtin.systemd:
name: "{{ item }}"
state: stopped
enabled: false
loop:
- edge-mqtt-cred
- mosquitto
when: (item ~ '.service') in ansible_facts.services
- name: Remove the retired MQTT bridge files
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /etc/systemd/system/edge-mqtt-cred.service
- /usr/local/sbin/edge-mqtt-cred
- /etc/edge-mqtt-cred
- /etc/mosquitto
register: edge_mqtt_bridge_files
- name: Reload systemd without the edge-mqtt-cred unit
ansible.builtin.systemd:
daemon_reload: true
when: edge_mqtt_bridge_files.changed
- name: Uninstall mosquitto
ansible.builtin.package:
name: mosquitto
state: absent
- name: Check for the retired heating-can compose project
ansible.builtin.stat:
path: /opt/edge-host/heating-can/docker-compose.yml
@@ -86,5 +122,4 @@
- edge-flash-wear
- docker-install
- edge-log-shipper
- mqtt-bridge
- edge-vlan
@@ -1,23 +0,0 @@
---
# --- MQTT: edge clients (open3e, e3oncan, sml2mqtt) publish to a LOCAL
# mosquitto on loopback, which bridges to the central cluster broker over TLS
# (open3e/e3oncan have no native MQTTS). This keeps plaintext on loopback only
# — off the untrusted IoT VLAN — and buffers telemetry in RAM during cluster
# blips. The local broker writes nothing to the eMMC (persistence off, logs to
# the volatile journal). See edge-mqtt-bridge.conf.j2 and README "MQTT bridge".
# Upstream cluster broker the local bridge connects to (TLS; the public
# Let's Encrypt cert is validated against the system CA bundle).
edge_mqtt_cluster_host: mqtt.renner.uno
edge_mqtt_cluster_tls_port: 8883
edge_mqtt_bridge_cafile: /etc/ssl/certs/ca-certificates.crt
# --- Bridge credential from mqtt-account-controller (REN-1307) ------------
# The cluster account the bridge logs in as (register entry in
# kubernetes/apps/home-assistant/mqtt-accounts.yaml). The controller pushes
# its password to edge-mqtt-cred and rotates it when the bridge reports
# "not connected"; there is no copy in ansible-vault.
edge_mqtt_bridge_account: "{{ inventory_hostname_short }}"
# HTTPS port of edge-mqtt-cred (esp32 device API, mTLS). Only a client
# certificate from the MQTT CA with this CN is accepted.
edge_mqtt_cred_port: 8443
edge_mqtt_cred_allowed_cn: mqtt-account-controller
@@ -1,141 +0,0 @@
#!/usr/bin/env python3
# Managed by ansible (roles/mqtt-bridge). Receives the cluster bridge password
# from mqtt-account-controller (REN-1307) so no plaintext copy lives in
# ansible-vault and no play run is needed to rotate it.
#
# Speaks the controller's esp32 device API over HTTPS:
# GET /api/status -> {"mqtt": {"connected": bool}}
# PUT /api/settings <- {"mqtt": {"user": "...", "pass": "..."}}
# A client certificate from the MQTT CA with CN mqtt-account-controller is
# required; nothing else may call it. The server key is generated on this
# host and never leaves it (the controller pins it on first contact).
#
# PUT writes the bridge file (template + credentials), removes the legacy
# vault-password bridges and restarts the local mosquitto. The password is
# kept only in that 0640 root:mosquitto file.
import grp
import json
import os
import socket
import ssl
import subprocess
import sys
import tempfile
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
CONF = json.load(open(sys.argv[1]))
WRITE = threading.Lock()
def connected():
try:
out = subprocess.run(
["mosquitto_sub", "-h", "127.0.0.1", "-p", str(CONF["local_port"]),
"-t", CONF["state_topic"], "-C", "1", "-W", "3"],
capture_output=True, text=True, timeout=10).stdout
except (OSError, subprocess.TimeoutExpired):
return False
return out.strip() == "1"
def write_bridge(user, password):
with open(CONF["template"]) as f:
body = f.read()
body += f"remote_username {user}\nremote_password {password}\n"
d = os.path.dirname(CONF["bridge_file"])
fd, tmp = tempfile.mkstemp(dir=d, prefix=".edge-mqtt-cred.")
with os.fdopen(fd, "w") as f:
f.write(body)
os.chown(tmp, 0, grp.getgrnam("mosquitto").gr_gid)
os.chmod(tmp, 0o640)
os.replace(tmp, CONF["bridge_file"])
for legacy in CONF["legacy_files"]:
if os.path.exists(legacy):
os.remove(legacy)
subprocess.run(["systemctl", "restart", "mosquitto"], check=True, timeout=60)
def valid(s):
# Controller passwords are random URL-safe tokens; anything with
# whitespace or a control character would break the config line.
return isinstance(s, str) and 0 < len(s) <= 256 and s.isprintable() and not any(c.isspace() for c in s)
class Handler(BaseHTTPRequestHandler):
def authorized(self):
cert = self.connection.getpeercert() or {}
cns = [v for rdn in cert.get("subject", ()) for k, v in rdn if k == "commonName"]
if cns != [CONF["allowed_cn"]]:
self.send_error(403)
return False
return True
def reply(self, code, obj=None):
data = b"" if obj is None else json.dumps(obj).encode()
self.send_response(code)
if data:
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_GET(self):
if not self.authorized():
return
if self.path != "/api/status":
return self.send_error(404)
self.reply(200, {"mqtt": {"connected": connected()}})
def do_PUT(self):
if not self.authorized():
return
if self.path != "/api/settings":
return self.send_error(404)
try:
n = int(self.headers.get("Content-Length", "0"))
if not 0 < n <= 4096:
raise ValueError("body size")
m = json.loads(self.rfile.read(n))["mqtt"]
user, password = m["user"], m["pass"]
except (ValueError, KeyError, TypeError):
return self.send_error(400)
if user != CONF["user"] or not valid(password):
return self.send_error(400)
with WRITE:
write_bridge(user, password)
self.log_message("bridge credentials for %s replaced, mosquitto restarted", user)
self.reply(204)
def log_message(self, fmt, *args):
sys.stderr.write("%s %s\n" % (self.address_string(), fmt % args))
class Server(ThreadingHTTPServer):
address_family = socket.AF_INET6
def server_bind(self):
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
super().server_bind()
def get_request(self):
# The TLS handshake runs in the request thread, with a timeout, so a
# stalled peer cannot block the accept loop.
sock, addr = self.socket.accept()
sock.settimeout(10)
return self.ctx.wrap_socket(sock, server_side=True, do_handshake_on_connect=False), addr
def main():
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
ctx.load_cert_chain(CONF["cert"], CONF["key"])
ctx.load_verify_locations(CONF["ca"])
ctx.verify_mode = ssl.CERT_REQUIRED
srv = Server(("::", CONF["port"]), Handler)
srv.ctx = ctx
srv.serve_forever()
if __name__ == "__main__":
main()
@@ -1,10 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIBdTCCARygAwIBAgIUN8ne4k4Wif9O83GDyKiBP0Ma7EIwCgYIKoZIzj0EAwIw
GTEXMBUGA1UEAxMOUmVubmVyIE1RVFQgQ0EwHhcNMjYxMDA2MTUzMDE1WhcNMzYx
MDAzMTUzMDE1WjAZMRcwFQYDVQQDEw5SZW5uZXIgTVFUVCBDQTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABOZhZF2JDoyvVg2oIXSbJAuAWDcoG22RxPA4oMKRMA/b
XzIY566D2k6cVnWqbil9s/q4O2mj+Epo7plBw8NwuOOjQjBAMA4GA1UdDwEB/wQE
AwICpDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQcqSDDiLZzhrvqq9f16Y08
CO7QfzAKBggqhkjOPQQDAgNHADBEAiBFkxd9pXnqIcjReEcOTvCBZ3HxKu2jNQGL
/le7gunTGgIgZapWAcu4awa+wmZcR/z2luOjD2GhIT15um14Z+sstJk=
-----END CERTIFICATE-----
@@ -1,13 +0,0 @@
---
# The MQTT clients (zwave-js, sml2mqtt, open3e, e3oncan) reconnect on their
# own once the local broker is back.
- name: Restart mosquitto
ansible.builtin.service:
name: mosquitto
state: restarted
- name: Restart edge-mqtt-cred
ansible.builtin.systemd:
name: edge-mqtt-cred
state: restarted
daemon_reload: true
-109
View File
@@ -1,109 +0,0 @@
---
# Local MQTT bridge broker.
#
# Edge clients (open3e, e3oncan, sml2mqtt) publish here over loopback; this
# broker bridges to the cluster broker over TLS, because those clients have no
# native MQTTS. Persistence is off and logging goes to stdout so it writes
# nothing to local flash.
- name: Install mosquitto
ansible.builtin.package:
name: mosquitto
state: present
- name: Create the bridge directory
ansible.builtin.file:
path: /etc/mosquitto/bridge.d
state: directory
owner: root
group: mosquitto
mode: "0750"
- name: Check for the controller-managed bridge file
ansible.builtin.stat:
path: "/etc/mosquitto/bridge.d/{{ edge_mqtt_bridge_account }}.conf"
register: edge_mqtt_bridge_managed
# Until mqtt-account-controller has pushed the first password, keep the
# vault-password bridges so the cluster never loses edge-1's data. Once the
# managed file exists, never render them again (edge-mqtt-cred removed them).
- name: Deploy legacy vault-password bridges
ansible.builtin.template:
src: edge-mqtt-bridge-legacy.conf.j2
dest: /etc/mosquitto/bridge.d/legacy.conf
owner: root
group: mosquitto
mode: "0640" # carries the legacy cluster bridge credentials
when: not edge_mqtt_bridge_managed.stat.exists
notify: Restart mosquitto
- name: Deploy local mosquitto config
ansible.builtin.template:
src: edge-mqtt-bridge.conf.j2
dest: /etc/mosquitto/mosquitto.conf
owner: root
group: mosquitto
mode: "0640"
notify: Restart mosquitto
- name: Enable local mosquitto bridge
ansible.builtin.systemd:
name: mosquitto
enabled: true
state: started
# --- edge-mqtt-cred (REN-1307): receives the bridge password from
# mqtt-account-controller. edge-1 holds only the public MQTT CA and its own
# server key, generated here and never copied off the host.
- name: Create edge-mqtt-cred config directory
ansible.builtin.file:
path: /etc/edge-mqtt-cred
state: directory
owner: root
group: root
mode: "0700"
- name: Generate edge-mqtt-cred server key and certificate
ansible.builtin.command:
cmd: >-
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1
-nodes -days 36500 -subj "/CN={{ inventory_hostname }}"
-keyout /etc/edge-mqtt-cred/tls.key -out /etc/edge-mqtt-cred/tls.crt
creates: /etc/edge-mqtt-cred/tls.key
notify: Restart edge-mqtt-cred
- name: Deploy edge-mqtt-cred files
ansible.builtin.copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
owner: root
group: root
mode: "{{ item.mode }}"
loop:
- {src: edge-mqtt-cred.py, dest: /usr/local/sbin/edge-mqtt-cred, mode: "0755"}
# Public CA of the cluster's MQTT client certificates
# (kubernetes/infrastructure/post-crds/mqtt-ca.yaml), valid until 2036-10-03.
- {src: mqtt-ca.crt, dest: /etc/edge-mqtt-cred/mqtt-ca.crt, mode: "0644"}
notify: Restart edge-mqtt-cred
- name: Deploy edge-mqtt-cred config and bridge template
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
owner: root
group: root
mode: "{{ item.mode }}"
loop:
- {src: edge-mqtt-cred.json.j2, dest: /etc/edge-mqtt-cred/config.json, mode: "0600"}
# Read only on the controller's next push: a change here reaches the
# running bridge when the password is next set.
- {src: edge-1-bridge.conf.in.j2, dest: /etc/edge-mqtt-cred/bridge.conf.in, mode: "0600"}
- {src: edge-mqtt-cred.service.j2, dest: /etc/systemd/system/edge-mqtt-cred.service, mode: "0644"}
notify: Restart edge-mqtt-cred
- name: Enable edge-mqtt-cred
ansible.builtin.systemd:
name: edge-mqtt-cred
enabled: true
state: started
daemon_reload: true
@@ -1,21 +0,0 @@
# Managed by roles/mqtt-bridge (REN-1307). edge-mqtt-cred appends
# remote_username/remote_password to this and writes the result to
# /etc/mosquitto/bridge.d/{{ edge_mqtt_bridge_account }}.conf. One bridge for
# open3e, as the register account `{{ edge_mqtt_bridge_account }}`
# (ACL readwrite open3e/#). sml2mqtt is retired (SML reader, 2026-09-19).
connection {{ edge_mqtt_bridge_account }}
address {{ edge_mqtt_cluster_host }}:{{ edge_mqtt_cluster_tls_port }}
bridge_cafile {{ edge_mqtt_bridge_cafile }}
bridge_insecure false
remote_clientid {{ edge_mqtt_bridge_account | replace('-', '') }}-bridge
cleansession false
try_private true
restart_timeout 5 30
queue_qos0_messages true
# The bridge state goes to the LOCAL broker only (retained 1/0); edge-mqtt-cred
# reads it to answer the controller's "connected?".
notifications true
notifications_local_only true
notification_topic $SYS/broker/connection/{{ edge_mqtt_bridge_account }}/state
topic open3e/# out 0
topic open3e/cmnd in 0
@@ -1,21 +0,0 @@
# Managed by roles/mqtt-bridge. Legacy vault-password bridges, rendered only
# while /etc/mosquitto/bridge.d/{{ edge_mqtt_bridge_account }}.conf does not
# exist; edge-mqtt-cred deletes this file on the controller's first push
# (REN-1307). Remove with the vault keys once edge-1 is on the register account.
# --- Bridge 1: open3e datapoints + command topic, as the cluster `open3e`
# user (its ACL is `readwrite open3e/#`, which covers publishing datapoints
# and subscribing to open3e/cmnd). e3oncan also publishes under open3e/#.
connection edge1-open3e
address {{ edge_mqtt_cluster_host }}:{{ edge_mqtt_cluster_tls_port }}
bridge_cafile {{ edge_mqtt_bridge_cafile }}
bridge_insecure false
remote_username {{ edge_open3e_mqtt_user }}
remote_password {{ edge_open3e_mqtt_password }}
remote_clientid edge1-bridge-open3e
cleansession false
try_private true
notifications false
restart_timeout 5 30
queue_qos0_messages true
topic open3e/# out 0
topic open3e/cmnd in 0
@@ -1,30 +0,0 @@
# Managed by roles/mqtt-bridge. Local mosquitto that the edge clients (open3e,
# e3oncan, sml2mqtt) publish to over loopback; it bridges to the central
# cluster broker over TLS. open3e/e3oncan have no native MQTTS, so this is how
# edge-1's MQTT gets off plaintext on the untrusted IoT VLAN, and it buffers
# telemetry in RAM if the cluster broker is briefly unreachable.
#
# eMMC: this host runs on a small internal eMMC, so the local broker writes
# NOTHING to flash — `persistence false` keeps retained/queued messages in RAM
# only, and `log_dest stdout` goes to the (volatile, RAM) journal that Alloy
# ships to Loki. Do not add persistence/log-file settings here.
persistence false
log_dest stdout
log_type error
log_type warning
log_type notice
# Loopback only — the trust boundary is the host itself. Any process on edge-1
# may publish here (same trust as the bridged services); nothing off-host can
# reach it. Real authentication to the cluster happens on the bridge below.
# Single v4-loopback listener: all edge clients dial 127.0.0.1 (edge_mqtt_host),
# and a second same-port listener can make some builds refuse to start.
listener 1883 127.0.0.1
allow_anonymous true
# --- Bridges to the cluster broker (REN-1307). bridge.d holds either
# {{ edge_mqtt_bridge_account }}.conf, written by edge-mqtt-cred with the password
# mqtt-account-controller pushed, or, until that first push, legacy.conf with
# the two vault-password bridges. edge-mqtt-cred deletes legacy.conf when it
# writes the new file.
include_dir /etc/mosquitto/bridge.d
@@ -1,13 +0,0 @@
{{ {
"port": edge_mqtt_cred_port,
"allowed_cn": edge_mqtt_cred_allowed_cn,
"user": edge_mqtt_bridge_account,
"cert": "/etc/edge-mqtt-cred/tls.crt",
"key": "/etc/edge-mqtt-cred/tls.key",
"ca": "/etc/edge-mqtt-cred/mqtt-ca.crt",
"template": "/etc/edge-mqtt-cred/bridge.conf.in",
"bridge_file": "/etc/mosquitto/bridge.d/" ~ edge_mqtt_bridge_account ~ ".conf",
"legacy_files": ["/etc/mosquitto/bridge.d/legacy.conf"],
"local_port": edge_mqtt_port,
"state_topic": "$SYS/broker/connection/" ~ edge_mqtt_bridge_account ~ "/state",
} | to_nice_json }}
@@ -1,19 +0,0 @@
# Managed by roles/mqtt-bridge (REN-1307). See /usr/local/sbin/edge-mqtt-cred.
[Unit]
Description=Receives the MQTT bridge credential from mqtt-account-controller
After=network-online.target mosquitto.service
Wants=network-online.target
[Service]
ExecStart=/usr/bin/python3 /usr/local/sbin/edge-mqtt-cred /etc/edge-mqtt-cred/config.json
Restart=always
RestartSec=5
# Root only to write the bridge file and restart mosquitto.
ProtectSystem=strict
ReadWritePaths=/etc/mosquitto/bridge.d
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
+3 -1
View File
@@ -113,8 +113,10 @@ spec:
value: "10.244.0.0/16,fd10:244::/48,127.0.0.0/8,::1/128"
authentik:
log_level: info
# Off: reports would go to authentik.error-reporting.a7k.io (Sentry),
# which egress policy denies. The outposts take this from the server.
error_reporting:
enabled: true
enabled: false
postgresql:
host: "ak-pg-rw.authentik.svc.cluster.local"
name: "authentik"
@@ -34,3 +34,10 @@ spec:
protocol: TCP
- port: "6636"
protocol: TCP
# The UDM's LDAPS admin-auth sync.
- fromCIDRSet:
- cidrGroupRef: udm-k8s-ingress
toPorts:
- ports:
- port: "6636"
protocol: TCP
@@ -44,7 +44,7 @@ spec:
annotations:
# Bump to restart the pod and re-attach net1 (DHCP via multus).
# 2026-10-06: first attach on containernetworking/plugins v1.9.1.
homelab.renner.uno/restarted-for: "2026-10-06 cni-plugins v1.9.1 attach check"
homelab.renner.uno/restarted-for: "2026-11-07 net1 without RA (S28-36)"
k8s.v1.cni.cncf.io/networks: mdns-relay-iot
spec:
# Runs where the node patch declares the IoT VLAN (ens19).
@@ -31,6 +31,11 @@ spec:
# dropped. The relay's forward path to Home Assistant runs over eth0, so
# that route has to stay intact.
#
# sbr covers IPv4 only. With Router Advertisements net1 gets a global IPv6
# address and default route in the main table, and the IPv6 forward to the
# Service leaves via net1 with an IoT source, which HA's CNP drops. tuning
# turns RA processing off; the link-local address ff02::fb needs stays.
#
# Requires the cni-dhcp-daemon DaemonSet in infrastructure/multus.
config: |
{
@@ -45,6 +50,13 @@ spec:
"type": "dhcp"
}
},
{
"type": "tuning",
"sysctl": {
"net.ipv6.conf.IFNAME.accept_ra": "0",
"net.ipv6.conf.IFNAME.autoconf": "0"
}
},
{
"type": "sbr"
}
@@ -64,7 +64,7 @@ spec:
type: RuntimeDefault
containers:
- name: shelly-manager
image: gitea.renner.uno/konairius/shelly-manager:2026-11-05-0732 # {"$imagepolicy": "flux-system:shelly-manager"}
image: gitea.renner.uno/konairius/shelly-manager:2026-11-07-2052 # {"$imagepolicy": "flux-system:shelly-manager"}
imagePullPolicy: IfNotPresent
ports:
- name: http
@@ -49,23 +49,6 @@
auth: cert
deliver: {namespace: home-assistant}
acl: ["readwrite #"]
# edge-1's local mosquitto bridge (REN-1307): one bridge for open3e/e3oncan.
# The controller pushes
# the password to edge-mqtt-cred on edge-1 (ansible role mqtt-bridge) over
# HTTPS with its own client certificate, and pins edge-1's self-generated
# server certificate on first contact (key edge-1.pin in mqtt-account-hashes;
# delete it after a reinstall of edge-1).
- name: edge-1
auth: password
deliver: {esp32: "https://edge-1.iot.renner.uno:8443"}
# No sml2mqtt/#: the SML meter reader is retired for good (owner,
# 2026-09-19; grid data comes from the E380 on the CAN bus).
acl: ["readwrite open3e/#"]
# edge-2's local mosquitto bridge, same role and pinning as edge-1.
- name: edge-2
auth: password
deliver: {esp32: "https://edge-2.iot.renner.uno:8443"}
acl: ["readwrite open3e/#"]
# vitocal-can: open3e-esp32 CAN gateway at the Vitocal 250-A (S28-28).
# The firmware registers its compiled-in DHCP name `open3e`, not its
# configured hostname.
@@ -118,6 +101,10 @@
auth: password
deliver: {shelly-manager: 25} # Plug S Gen1 1c1a8f
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-41
auth: password
deliver: {shelly-manager: 41} # Plug Gen1 f4cfa26d9cb2
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-15
auth: password
deliver: {shelly-manager: 15} # Plug S G3 8cbfea910578
@@ -200,6 +187,34 @@
auth: password
deliver: {shelly-manager: 30} # Plus 2PM b48a0a226c28 (reachable again, REN-1108)
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-7
auth: password
deliver: {shelly-manager: 7} # Plus 1PM Mini 348518df3500
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-23
auth: password
deliver: {shelly-manager: 23} # Plus 1PM Mini 348518e05b88
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-11
auth: password
deliver: {shelly-manager: 11} # Plus 2PM ccdba7d0b804
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-28
auth: password
deliver: {shelly-manager: 28} # Plus 2PM 0cdc7efa8dc0
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-17
auth: password
deliver: {shelly-manager: 17} # Plus Plug S d4d4daed2054
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-18
auth: password
deliver: {shelly-manager: 18} # Plus Plug S d4d4daecc47c
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-20
auth: password
deliver: {shelly-manager: 20} # Plus Plug S d4d4daecb690
acl: ["readwrite shellies/#", "write homeassistant/#"]
# Sleeping H&Ts (REN-1107): awake only 8-13 s every 1-2 h, so the
# controller pushes when the device publishes `online` on its wake topic
# (shelly-manager topic prefix `shellies/<hostname>`), not in the 2-min loop.
@@ -223,3 +238,8 @@
deliver: {shelly-manager: 38} # H&T G3 ecda3bc0b6a0
wake: shellies/shellyhtg3-ecda3bc0b6a0/online
acl: ["readwrite shellies/#", "write homeassistant/#"]
- name: shelly-40
auth: password
deliver: {shelly-manager: 40} # Flood G4 7c2c67732e08
wake: shellies/shellyfloodg4-7c2c67732e08/online
acl: ["readwrite shellies/#", "write homeassistant/#"]
@@ -100,6 +100,9 @@ spec:
# Keep tokens unhashed; enable hashing later as its own change.
- name: INFLUXD_USE_HASHED_TOKENS
value: "false"
# No usage telemetry to telemetry.influxdata.com (egress is denied).
- name: INFLUXD_REPORTING_DISABLED
value: "true"
# The chart defaults to liveness/readiness with initialDelaySeconds=0,
# so after a crash restart the kubelet kills the pod before shards
# finish opening. Give it 10 minutes to come up cleanly.
@@ -54,6 +54,9 @@ spec:
# non-RFC-framed payload). Scoped to this single-purpose receiver
# only — the pod-log Alloy DaemonSet stays at default stability.
stabilityLevel: experimental
# No usage reporting to stats.grafana.org (egress is denied anyway).
extraArgs:
- --disable-reporting
resources:
requests:
cpu: 20m
@@ -116,6 +116,9 @@ spec:
retries: 3
values:
alloy:
# No usage reporting to stats.grafana.org (egress is denied anyway).
extraArgs:
- --disable-reporting
configMap:
content: |
// Discover and collect K8s pod logs
@@ -1,11 +1,14 @@
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
# 6h, not 1h: both repositories carry tens to hundreds of thousands of PR
# build tags, and every scan pages the full list anonymously, which runs
# into ghcr.io's rate limit.
metadata:
name: immich-server
namespace: flux-system
spec:
image: ghcr.io/immich-app/immich-server
interval: 1h
interval: 6h
---
# v-prefix tags only, semver 3.x. The 4.x cutover would need manual
# review — Immich major bumps historically require migration.
@@ -31,7 +34,7 @@ metadata:
namespace: flux-system
spec:
image: ghcr.io/immich-app/immich-machine-learning
interval: 1h
interval: 6h
---
# Same policy as immich-server; machine-learning image must stay in
# version-sync with the server image (Immich docs require it).
@@ -74,6 +74,17 @@ spec:
- fd02:47::/64
- 10.2.47.0/24
---
# The UDM's own address on k8s-ingress, which it sources from towards the
# cluster's LoadBalancer IPs.
apiVersion: cilium.io/v2
kind: CiliumCIDRGroup
metadata:
name: udm-k8s-ingress
spec:
externalCIDRs:
- fd02:47::1/128
- 10.2.47.1/32
---
apiVersion: cilium.io/v2
kind: CiliumCIDRGroup
metadata:
@@ -467,7 +467,8 @@ spec:
- port: "443"
protocol: TCP
---
# zigbee2mqtt: Zigbee coordinator (SLZB) TCP socket.
# zigbee2mqtt: Zigbee coordinator (SLZB) TCP socket, and the OTA index and
# firmware images (Koenkk/zigbee-OTA) on raw.githubusercontent.com.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
@@ -498,6 +499,47 @@ spec:
- ports:
- port: "6638"
protocol: TCP
- toFQDNs:
- matchName: "raw.githubusercontent.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
---
# matter-server: the CSA DCL (PAA root certificates, vendor names, OTA
# metadata) and the development PAA certificates of project-chip on GitHub.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: matter-server-egress
namespace: home-assistant
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: matter-server
egress:
# rules.dns lets Cilium learn the toFQDNs mapping
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
- port: "53"
protocol: TCP
rules:
dns:
- matchPattern: "*"
- toFQDNs:
- matchName: "on.dcl.csa-iot.org"
- matchName: "api.github.com"
- matchName: "raw.githubusercontent.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
---
# mqtt-account-controller: credential delivery to deliver.esp32 targets in mqtt-accounts.yaml.
apiVersion: cilium.io/v2
@@ -524,13 +566,6 @@ spec:
rules:
dns:
- matchPattern: "*"
- toFQDNs:
- matchName: "edge-1.iot.renner.uno"
- matchName: "edge-2.iot.renner.uno"
toPorts:
- ports:
- port: "8443"
protocol: TCP
# vitocal-can (open3e-esp32), plain HTTP API.
- toFQDNs:
- matchName: "open3e.iot.renner.uno"
@@ -160,6 +160,12 @@ spec:
---
# In-cluster + DNS for every pod except terminus (terminus-egress is its
# allowlist; selecting it here would widen it to all of `cluster`).
#
# trmnl-renderer's Chromium calls clients2.google.com:80,
# update.googleapis.com:443 and accounts.google.com:443; these drops are
# expected and harmless. The launch flags are fixed in upstream
# trmnl-ha/ha-trmnl/screenshot.ts (PUPPETEER_ARGS, no override) and lack
# --disable-background-networking.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata: