feat(radius): WLAN setup page wlan.renner.uno for "Renner UniFi" #1564

Merged
k.renner merged 1 commits from feat/wlan-setup-page into main 2026-11-05 09:02:05 +00:00
Member

Setup page for the 802.1X WLAN "Renner UniFi" (cluster FreeRADIUS, EAP-TTLS/PAP, authentik credentials).

  • kubernetes/apps/authentik/wlan-setup.yaml: nginx (house image nginx-unprivileged:1.31-alpine, pinned digest), Service, Ingress wlan.renner.uno — public (Cloudflare → NetBird → Traefik) so a device can reach it before it is in the WLAN; Authentik gate, no group (the page holds no secrets).
  • kubernetes/apps/authentik/wlan-setup-config.yaml: nginx config, page, Windows file.
    • Apple: /renner-unifi.mobileconfig, com.apple.wifi.managed, TTLS (21) + inner PAP, outer identity anonymous, trusted server name radius.renner.uno, UserName from X-authentik-username. Fixed payload identifiers/UUIDs, so a reinstall replaces it.
    • Windows: /renner-unifi-wlan.cmd writes the WLAN profile after line 8 of itself to %TEMP%, imports it with netsh wlan add profile … user=current, then connects. EAP-TTLS with PAPAuthentication, anonymous, ServerNames radius.renner.uno, TrustedRootCAHash = ISRG Root X1 (the radius-tls chain is radius.renner.uno ← YR2 ← Root YR ← ISRG Root X1). CRLF line endings.
    • Linux: nmcli command with the username, 802-1x.system-ca-certs yes, domain-suffix-match radius.renner.uno, password agent-owned.
    • Android: not on the page — no native import path without Passpoint or an app.
  • Usernames outside [A-Za-z0-9._@+-]{1,64} are left blank rather than written into a generated file.
  • No file carries a password.
  • docs/radius/dot1x.md: client setup now for "Renner UniFi" with the page; obsolete renner-test.mobileconfig removed (SSID gone).

Checked: local run of the pinned image with the extracted ConfigMap files — /, /whoami (header, and a markup username → empty), /renner-unifi.mobileconfig (200, application/x-apple-aspen-config, well-formed, username filled), /renner-unifi-wlan.cmd (CRLF, embedded profile well-formed); nmcli --offline accepts the command; server-side dry-run of all objects.

🤖 Generated with Claude Code

Setup page for the 802.1X WLAN "Renner UniFi" (cluster FreeRADIUS, EAP-TTLS/PAP, authentik credentials). - `kubernetes/apps/authentik/wlan-setup.yaml`: nginx (house image `nginx-unprivileged:1.31-alpine`, pinned digest), Service, Ingress `wlan.renner.uno` — public (Cloudflare → NetBird → Traefik) so a device can reach it before it is in the WLAN; Authentik gate, no group (the page holds no secrets). - `kubernetes/apps/authentik/wlan-setup-config.yaml`: nginx config, page, Windows file. - **Apple**: `/renner-unifi.mobileconfig`, `com.apple.wifi.managed`, TTLS (21) + inner PAP, outer identity `anonymous`, trusted server name `radius.renner.uno`, `UserName` from `X-authentik-username`. Fixed payload identifiers/UUIDs, so a reinstall replaces it. - **Windows**: `/renner-unifi-wlan.cmd` writes the WLAN profile after line 8 of itself to `%TEMP%`, imports it with `netsh wlan add profile … user=current`, then connects. EAP-TTLS with `PAPAuthentication`, `anonymous`, `ServerNames radius.renner.uno`, `TrustedRootCAHash` = ISRG Root X1 (the `radius-tls` chain is radius.renner.uno ← YR2 ← Root YR ← ISRG Root X1). CRLF line endings. - **Linux**: `nmcli` command with the username, `802-1x.system-ca-certs yes`, `domain-suffix-match radius.renner.uno`, password agent-owned. - **Android**: not on the page — no native import path without Passpoint or an app. - Usernames outside `[A-Za-z0-9._@+-]{1,64}` are left blank rather than written into a generated file. - No file carries a password. - `docs/radius/dot1x.md`: client setup now for "Renner UniFi" with the page; obsolete `renner-test.mobileconfig` removed (SSID gone). Checked: local run of the pinned image with the extracted ConfigMap files — `/`, `/whoami` (header, and a markup username → empty), `/renner-unifi.mobileconfig` (200, `application/x-apple-aspen-config`, well-formed, username filled), `/renner-unifi-wlan.cmd` (CRLF, embedded profile well-formed); `nmcli --offline` accepts the command; server-side dry-run of all objects. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 1 commit 2026-11-05 08:56:24 +00:00
feat(radius): WLAN setup page wlan.renner.uno for "Renner UniFi"
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
1e66d793d5
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>
k.renner requested review from review-bot 2026-11-05 08:57:58 +00:00
review-bot approved these changes 2026-11-05 09:00:35 +00:00
review-bot left a comment
Member

🤖 Automated review — model claude-opus-5-5, commit 1e66d793d5945e547d39be144b0adb4a12334e60.

Automated review by the review-bot: it approves only when it found nothing red or yellow, and a human still merges. Request a review from review-bot again for a fresh one.


Summary

This PR adds the wlan-setup nginx Deployment, Service and gated public Ingress at wlan.renner.uno. The page serves an Apple profile with the caller's username filled in, a Windows netsh import file and an nmcli command for "Renner UniFi". It also points dot1x.md at the page and removes the old renner-test.mobileconfig. I found no blocking issues:

  • The gate controller (ci/images/authentik-gate-sync/controller.py) builds an any-user provider and the Traefik→backend CNP from this Ingress as written.
  • The middleware strips X-authentik-* headers sent by the client, so $wifi_user cannot be spoofed.
  • The username regex keeps XML and shell metacharacters out of the generated files.

mergeable is true, no issue is linked, and no other open PR touches these files. I could not list base-branch commits per path because I have no commit-listing tool.

⚪ Unverified

  • Windows trust anchor: wlan-setup-config.yaml renner-unifi-wlan.cmd pins TrustedRootCAHash to ISRG Root X1. That only works if Windows builds the radius-tls chain up to X1. The PR body describes the chain as ←YR2←Root YR←X1. If a Windows client already trusts Root YR as its own root, the chain stops there, the hash doesn't match, and the user gets a prompt or a failure (DisablePrompt is false). The PR's checks cover the XML being well-formed, not a real Windows join. I did not check how current Windows root stores handle the Gen-Y roots.
  • EapTtls element order: I could not confirm that netsh accepts the element order inside ServerValidation (ServerNames, TrustedRootCAHash, DisablePrompt). I had no schema or Windows test to check against.

🟡 Suggestions

  • Reloader annotation is in the wrong place: kubernetes/apps/authentik/wlan-setup.yaml:22-23 puts configmap.reloader.stakater.com/reload: wlan-setup on the pod template. The repo convention (freeradius.yaml:9-11) puts it on the Deployment's metadata.annotations, which is where Reloader looks.
    • index.html and the .cmd file are mounted without subPath, so they still update in place.
    • default.conf holds the whole mobileconfig and the map, and nginx only reads it at startup. Edits to the Apple profile or the username regex won't take effect until someone restarts the pod by hand.
    • Fix: move the annotation up to the Deployment metadata.
  • Image updates: wlan-setup.yaml:34 pins a digest but has no $imagepolicy marker. Other images in this directory (e.g. freeradius.yaml) use one. Unless something else (e.g. Renovate) updates digests here, this image won't get updated. Worth adding a policy or noting who bumps it.

🟢 Nits

  • docs/radius/dot1x.md:150-152 and :200-202: the reflowed paragraphs have lines much longer than the surrounding ~78-column wrap.
  • docs/radius/dot1x.md Apple section: it says "Install it via Settings (Profile Downloaded)", which is the iOS flow. The page itself gives the macOS path as well.

Tests

There are no automated tests, which is normal for manifests in this repo. The PR describes a local run of the pinned image, an nmcli --offline check and a server-side dry-run. What's still missing is an actual Windows and Apple join against radius.renner.uno, which would also settle the trust-anchor question above.

Recommendation

Looks reasonable — no blocking issues; comments above are optional.

🤖 **Automated review** — model `claude-opus-5-5`, commit `1e66d793d5945e547d39be144b0adb4a12334e60`. > Automated review by the `review-bot`: it approves only when it found nothing red or yellow, and a human still merges. Request a review from `review-bot` again for a fresh one. --- ## Summary This PR adds the `wlan-setup` nginx Deployment, Service and gated public Ingress at `wlan.renner.uno`. The page serves an Apple profile with the caller's username filled in, a Windows `netsh` import file and an `nmcli` command for "Renner UniFi". It also points `dot1x.md` at the page and removes the old `renner-test.mobileconfig`. I found no blocking issues: - The gate controller (`ci/images/authentik-gate-sync/controller.py`) builds an any-user provider and the Traefik→backend CNP from this Ingress as written. - The middleware strips `X-authentik-*` headers sent by the client, so `$wifi_user` cannot be spoofed. - The username regex keeps XML and shell metacharacters out of the generated files. `mergeable` is true, no issue is linked, and no other open PR touches these files. I could not list base-branch commits per path because I have no commit-listing tool. ## ⚪ Unverified - **Windows trust anchor:** `wlan-setup-config.yaml` `renner-unifi-wlan.cmd` pins `TrustedRootCAHash` to ISRG Root X1. That only works if Windows builds the `radius-tls` chain up to X1. The PR body describes the chain as ←YR2←Root YR←X1. If a Windows client already trusts Root YR as its own root, the chain stops there, the hash doesn't match, and the user gets a prompt or a failure (`DisablePrompt` is false). The PR's checks cover the XML being well-formed, not a real Windows join. I did not check how current Windows root stores handle the Gen-Y roots. - **EapTtls element order:** I could not confirm that `netsh` accepts the element order inside `ServerValidation` (`ServerNames`, `TrustedRootCAHash`, `DisablePrompt`). I had no schema or Windows test to check against. ## 🟡 Suggestions - **Reloader annotation is in the wrong place:** `kubernetes/apps/authentik/wlan-setup.yaml:22-23` puts `configmap.reloader.stakater.com/reload: wlan-setup` on the pod template. The repo convention (`freeradius.yaml:9-11`) puts it on the Deployment's `metadata.annotations`, which is where Reloader looks. - `index.html` and the `.cmd` file are mounted without `subPath`, so they still update in place. - `default.conf` holds the whole mobileconfig and the `map`, and nginx only reads it at startup. Edits to the Apple profile or the username regex won't take effect until someone restarts the pod by hand. - Fix: move the annotation up to the Deployment metadata. - **Image updates:** `wlan-setup.yaml:34` pins a digest but has no `$imagepolicy` marker. Other images in this directory (e.g. `freeradius.yaml`) use one. Unless something else (e.g. Renovate) updates digests here, this image won't get updated. Worth adding a policy or noting who bumps it. ## 🟢 Nits - `docs/radius/dot1x.md:150-152` and `:200-202`: the reflowed paragraphs have lines much longer than the surrounding ~78-column wrap. - `docs/radius/dot1x.md` Apple section: it says "Install it via Settings (Profile Downloaded)", which is the iOS flow. The page itself gives the macOS path as well. ## Tests There are no automated tests, which is normal for manifests in this repo. The PR describes a local run of the pinned image, an `nmcli --offline` check and a server-side dry-run. What's still missing is an actual Windows and Apple join against `radius.renner.uno`, which would also settle the trust-anchor question above. ## Recommendation Looks reasonable — no blocking issues; comments above are optional. <!-- pr-review:sha=1e66d793d5945e547d39be144b0adb4a12334e60 -->
k.renner merged commit ee1a421dc0 into main 2026-11-05 09:02:05 +00:00
Sign in to join this conversation.