The wlan-setup Deployment runs gitea.renner.uno/gitea_admin/wlan-setup
(Flux image policy wlan-setup) instead of nginx + njs; the ConfigMap
wlan-setup-config.yaml with nginx.conf, njs and the Windows .cmd is removed.
- Certificate wlan-setup-signing for wlan.renner.uno (PKCS#8), mounted at
/etc/wlan-signing (fsGroup 65534, mode 0440); the service reads it per
request, so renewals need no restart.
- Ingress wlan-setup-provisioning: /windows/profile/ without the authentik
gate, for Windows Settings fetching the signed provisioning XML.
- OAuth provider wlan-setup: redirect URI
https://wlan.renner.uno/windows/callback.
- docs/radius/dot1x.md: Go service and the Windows Passpoint flow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Android and Apple get Passpoint profiles with their own app password from
wlan.renner.uno; the manual Android steps with the account password are
removed. Windows and Linux keep their setup files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Renner UniFi" now runs WPA3 Enterprise without transition mode and
advertises Passpoint (realm and domain renner.uno, EAP-TTLS/PAP), so the
Android profiles from wlan.renner.uno join it directly. The table row for
the deleted "Renner Test" WLAN is replaced; the Windows manual setup uses
WPA3-Enterprise.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New app passwords start with 29 days (below the 30-day API cap, so no
group attribute is needed). The expression policy dot1x-app-password-renew,
re-evaluated on the dot1x-login binding, renews the app password that
TokenBackend matched to 60 days on each 802.1X login (at most one write
per day; always passes, failure_result true). authentik rejects expired
app passwords and deletes them in clean_expired_models (every 5 min).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wlan.renner.uno signs the user in through the new authentik OAuth2
application wlan-setup (confidential, scope goauthentik.io/api). The
callback exchanges the code (client_secret_basic), creates an app password
with the user's own access token (10 years) and returns the Passpoint
profile carrying it in the response to the user's GET. The page asks for
no password and stores nothing; deleting the app password in authentik
removes the device.
- dot1x-password stage also accepts app passwords (TokenBackend).
- Client secret: ESO Password generator wlan-setup-oauth, read by the
authentik worker (blueprint) and by wlan-setup.
- authentik calls go through an internal nginx location; headers come
from $arg_* because subrequests share the parent's variable cache.
- Reloader annotations on the wlan-setup Deployment metadata.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Android starts its Passpoint installer only for a profile fetched with a
user-triggered GET and Content-Type application/x-wifi-config; a
browser-generated blob does not qualify. The page now POSTs the password to
/android/prepare, where njs builds the profile and stores it in a shared
dict (120 s, bound to the caller's username) under a random token; the
link /android/profile/<token> delivers it once.
AAAServerTrustedNames moves under Extension/Android, the only place
Android's PpsMoParser accepts it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Android section takes the password, builds a Passpoint R1 profile
(application/x-wifi-config: PPS MO with HomeSP FQDN and realm renner.uno,
EAP-TTLS with inner PAP, AAA server name radius.renner.uno, ISRG Root X1 as
trust anchor) in the browser and hands it to Android as a blob. The password
is not sent to any server.
Matches WLANs that advertise Hotspot 2.0 with domain and NAI realm
renner.uno.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nginx behind the Authentik gate (any user, public) serving:
- Apple profile (TTLS/PAP, outer identity anonymous, trusted server name
radius.renner.uno) with the caller's username from X-authentik-username,
- a Windows .cmd that imports the WLAN profile via netsh (TTLS/PAP, server
name radius.renner.uno, trust anchor ISRG Root X1),
- the Linux nmcli command.
No file carries a password. Usernames outside [A-Za-z0-9._@+-] are left
blank instead of being put into a generated file.
The Renner Test profile is removed; that SSID no longer exists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UDM RADIUS rejects the pod CIDR as an unknown client; it accepts
10.2.11.0/24. FreeRADIUS pods get net1 on VLAN 11 "(11) RADIUS" and the
udm home server moves to 10.2.11.1.
- Talos k8s-{1,2,3}: VLANConfig radius0 (VLAN 11 on uplink), no
addresses, IPv6 disabled on the host link.
- Cilium bpf.vlanBypass gains 11: radius0 is address-less like ens19, so
its tagged frames meet the VLAN filter on uplink (#592).
- NAD freeradius-udm: macvlan on radius0, DHCP IPAM requesting only
subnet-mask (skipDefault), so the lease has no router and net1 holds
only the connected /24. No sbr: it moves that route into a source
table, and the unbound proxy socket would then reach the UDM via eth0
from the pod IP. tuning disables IPv6 on net1 (VLAN 11 sends RAs).
- NAS listeners bound to eth0, so nothing listens on net1.
- CNP freeradius-udm-egress removed: net1 traffic bypasses Cilium.
- Harness: FreeRADIUS interfaces named eth0/net1 explicitly; Docker
attaches networks in no fixed order, and the eth0 bind exposed it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Outer identity anonymous[@realm] (any case) keeps the local EAP-TTLS ->
authentik path. Any other outer identity is a UniFi Endpoint account: the
outer server proxies every EAP round to new home server udm (10.2.47.1:1812,
UDM_RADIUS_SECRET) without local EAP processing; post-proxy/post-auth leave
UDM replies untouched. Endpoint account names look like ordinary usernames,
so routing keys on the anonymous identity, not a name pattern.
home_server udm: response_window 5 < proxy_dedup_window 10,
require_message_authenticator yes (RFC 3579, EAP replies), status_check
request with a user name that cannot exist.
CNP freeradius-udm-egress allows only 10.2.47.1/32 UDP 1812.
Harness: mock_udm.py on a second internal bridge at the udm address from
proxy.conf. Asserts both EAP rounds (State included) reach it, its Accept
reaches the NAS with Tunnel-* VLAN 200 and MS-MPPE keys intact, its Reject
passes, and anonymous logins and accounting never reach it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Home server: response_window 5, status_check = request with a user that
does not exist in authentik (always rejected), check_interval 6,
check_timeout 4, num_answers_to_alive 3; revive_interval dropped. With
status_check = none the blind revive gave 7-8 s in some runs and no
recovery within 120 s in others.
tests/radius/run.sh: recovery case stalls the mock outpost until the
home server is marked dead, resumes it and asserts a login succeeds
within 20 s. It fails intermittently: radiusd stops answering
(liveness probe fails) after late replies arrive for requests it already
failed. Diagnosis pending.
Docs: secret is the only guard on the FreeRADIUS -> outpost hop; outpost
Service name resolved at startup. Spec: VLAN 10 is dual-stack
(fd02:10::/64). helmrelease comment: missing !Env resolves to the
default, no KeyError.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeRADIUS listens on :: for 1812/1813 and accepts aps6 fd02:10::/64 and
test6 fd02:50::/64 with the same secrets as their IPv4 entries; the NAS
CNP admits both prefixes. The harness adds IPv6 cases on an isolated
dual-stack bridge.
Windows 11 setup requires server validation for radius.renner.uno and
never Windows sign-in credentials. The Apple profile uses EncryptionType
Any and OuterIdentity anonymous. dot1x.md marks the test/test6 clients
and their CNP CIDRs as test-phase only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Server pods (SSO) no longer depend on radius-secrets. Worker reloads on
radius-secrets changes. Docs: worker-only secret, unquoted integer
dot1x_vlan, FreeRADIUS VLAN contract.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blueprint dot1x-radius.yaml: non-interactive flow dot1x-authentication-flow,
policy dot1x-has-vlan, RADIUS mapping dot1x-vlan (lowest dot1x_vlan group
attribute; RFC 3580 Tunnel-* by raw code plus Cisco-AV-Pair fallback),
provider and outpost "802.1X Radius" (2 replicas, ClusterIP), client
networks = cluster pod CIDRs. Shared secret from radius-secrets via env
DOT1X_OUTPOST_SECRET.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New in-house controller (kuma-sync house pattern) that reconciles a
CloudflareLoadBalancer resource into a DNS-only Cloudflare load balancer,
its pool and its health monitor, using cloudflare-go v7.
proxy.renner.uno had round-robin A/AAAA records without health checks, so
every proxy reboot blackholed part of the public traffic. The owner set up
a DNS-only load balancer by hand (hostname origins proxy-1/proxy-2, HTTPS
monitor with Host: status.renner.uno); this moves it into git.
- Identity is the load balancer hostname; pool and monitor are found via
its references. Hand-made objects are adopted (the account allows only
2 origins, so a parallel pool would be rejected) and tagged in their
description; objects with another owner's tag are never written.
- Status shows per-origin health; resync every 2 minutes.
- Token from OpenBao homelab/infrastructure/k8s-cloudflare-lb via
ExternalSecret; egress limited to api.cloudflare.com:443.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cold-rebuild and HA-rollback runbooks, README and the image-automation README
name main and konairius/homelab. The plan records the Flux ref patch and the
cron API calls Cut-over 2 needed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
source-controller v1.9.5 uses go-git v5.19.1, whose documented RedirectPolicy
default is "initial", mirroring git's http.followRedirects: follow the
redirect on the discovery request and use the redirected URL as the base for
follow-up requests. The break-glass stays as a fallback only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Snapshot collaborators, konairius teams and branch-protection whitelists
before the transfer and compare after (Gitea drops collaborators who are
members of the new org).
- Break-glass suspends the flux-system Kustomization so it cannot re-apply the
old URL.
- Task 1 notes the off-cycle mint of both review-bot PATs and checks the
k.renner org holds only the two org secrets.
- Prompts move out of the branch PR into a post-rename PR to main, which
rebuilds the review-bot image (build-ci-images would not fire otherwise).
- Expected side effects of the transfer PR listed; greps widened to the whole
repo; wiki-maintenance cron deleted (no pipeline uses it); GitHub mirror gets
its default branch switched before master is deleted.
- Verified facts recorded: transfer collaborator rule, repair copies the
default branch, repo/cron ids, org secret inventory, repoRenameBranch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two cut-overs with their own windows, prep PRs and rollbacks, built on the
2026-10-18 inventory (repo references, live Flux/Woodpecker/rotator state,
workstation consumers) and the pattern proven on the five repos moved the same
day.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Removes the Paperclip deployment (namespace paperclip, CNPG cluster
pclp-pg, ingresses, image automation, server image build) and the
infrastructure that existed only for its agents:
- agent sandbox: paperclip-renner namespace and CNPs, the
kubernetes-sigs/agent-sandbox controller with its namespace, CNP and
Renovate rules
- agent-runtime-{go,ops} images and the build-agent-runtime pipeline
- matrix bridge (Element <-> Paperclip agents) and synapse's appservice
registration mount
- monitoring loki-read-gateway (only called by the paperclip Role)
- MQTT account agents-read and the controller's RBAC in ns paperclip
- the OpenBao secret-exists-check policy file
- agent admits in the wiki, lidarr, shelly-manager, mosquitto and
hubble-relay CNPs; the paperclip-api Gitea webhook host; the wiki API
key; the ClusterSecretStore namespace entry; the pclp-pg RECOVERY row
- the Paperclip design spec and plan
Authentik group "Paperclip Users" is switched to state: absent so the
blueprint deletes it. package-retention's EXCLUDE_PACKAGES becomes
optional, since its only value was the agent-runtime prefix.
The shelly-manager api-proxy sidecar stays: mqtt-account-controller
calls shelly-manager-api too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gitea-mcp advertises its -H URL as the authorization server in the OAuth protected-resource metadata, so clients doing discovery were sent to http://gitea.gitea.svc.cluster.local:3000. Switch -H to https://gitea.renner.uno; egress now goes to Traefik (as woodpecker-server and wiki already do) and the gitea:3000 grant on both sides is removed. Tool results' clone_url/html_url become public URLs too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Implementation plan for docs/superpowers/specs/2026-10-10-backup-verify-cloud-vm-design.md:
PR A image, PR B jq filters + scripts + manifests (CronJob suspended)
with offline fixture/stub tests, live runs, PR C cut-over.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design for replacing the monthly Velero restore-test: an in-cluster
CronJob creates a throwaway VM in fsn1, which runs kopia snapshot verify
(100% of files, read-only) against every latest DataUpload snapshot and
checks sizes against DataUpload totalBytes. No restored data is written
to the Ceph OSDs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The proxy-grant paragraph named `prometheus-read-rbac.yaml` and
`helmrelease-prometheus.yaml` by bare filename. Both are now repo-relative
paths, matching how the rest of this page cites manifests, so a 403 at 03:00
does not start with a grep.
Docs-only. No manifest changed. `docs/` is outside every Flux Kustomization
`spec.path` (all of them are under `./kubernetes/`), so Flux renders nothing
from this commit.
Follow-up to Argus's non-blocking note 2 on REN-113/REN-47.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>