shelly-manager has no authentication of its own. Anything that reaches :8080 can rewrite Shelly device config, push scripts, and read the stored per-device AdminPassword values. The only control on it was reachability of shelly-manager.home.renner.uno.
Proxy provider + application + dedicated outpost (ak-outpost-shelly-manager-gate), mode: proxy, authorization_flow: passkey-stepup-authorization — the same flow vscode-gate uses. Every authorize needs a passkey.
New git-owned Shelly Managers group, membership in the blueprint (k.renner), bound to the application. Blueprint apply is authoritative and reverts UI edits — add people in git.
Ingress repointed from shelly-manager:80 to ak-outpost-shelly-manager-gate:9000 via an ExternalName mirror.
Scoped CNPs: ingress rule in home-assistant allowing only outpost-name=shelly-manager-gate → :8080; matching egress rule in authentik allowing that outpost → app.kubernetes.io/name=shelly-manager:8080 only.
No skip_path_regex, deliberately
The music-assistant gate was retired because Chrome fetches manifest.webmanifest / sw.js with credentials: 'omit', producing a /authorize redirect loop; paperless and vscode carve those paths out.
shelly-manager ships neither — wwwroot/ is app.css, favicon.png, lib/. Every asset is a same-origin credentialed fetch. It's a Blazor Web app whose SignalR circuit at /_blazor upgrades to a websocket carrying the session cookie, which the proxy outpost forwards. Starting with no skip set is the safer default here; adding one blindly would punch an unauthenticated hole.
This also un-breaks the app
shelly-manager has been returning 504 through Traefik since 01:20 today, when #132 merged. Cause:
default-deny-ingress has endpointSelector: {} — it applies to every pod in home-assistant. Its traefik → :8080 grant was written for the code-server sidecar, but namespace-wide it was in fact the only rule letting Traefik reach shelly-manager, which also listens on 8080. Removing that grant to gate vscode took shelly-manager's ingress down as collateral.
The kubelet probe matches reserved:host / ANY, so the pod stayed Ready while Traefik was dropped — which is why nothing alerted.
Gating the app is the right fix rather than re-widening the rule to Traefik. It should not have been reachable unauthenticated to begin with.
I've added a comment above the two gate rules noting the endpointSelector: {} footgun explicitly, so the next person who edits a toPorts there knows it lands on every pod in the namespace.
Unrelated note
This PR does not touch the DHCP/sbr macvlan work from #135/#137. That change was a genuine, separate regression (a DHCP-supplied default route on net1 shadowing eth0's, taking the pod NotReady), fixed by #137, and verified: one default route via eth0, sbr rule from 10.2.30.122 lookup 100, pod serves 200 in 3 ms to a Traefik-shaped request from inside the cluster.
Verified
kubectl kustomize builds clean for apps/authentik, apps/home-assistant, infrastructure/network-policies.
Rendered ingress CNP: vscode-gate → :8080 and shelly-manager-gate → :8080 coexist; neither outpost can reach the other's target (enforced by the egress CNPs).
Traefik is already permitted to reach every pod in the authentik namespace, so the outpost needs no additional ingress allow.
Requires a browser test after apply
Not verifiable from the cluster side, and I'm not going to claim otherwise:
The Blazor SignalR circuit establishes through the outpost (scan progress updates live). If it doesn't, that's the websocket upgrade, not static assets.
A user outside Shelly Managers is refused.
Unlike paperless-gate, neither this nor vscode-gate adds an /outpost.goauthentik.io Ingress on auth.renner.uno — proxy-mode outposts serve that path on their own external_host. If the callback 401-loops, that's the first thing to add.
## Why
`shelly-manager` has **no authentication of its own**. Anything that reaches `:8080` can rewrite Shelly device config, push scripts, and read the stored per-device `AdminPassword` values. The only control on it was reachability of `shelly-manager.home.renner.uno`.
Same treatment as code-server got in #132.
## What
Mirrors the `vscode-gate` pattern exactly:
- **Proxy provider + application + dedicated outpost** (`ak-outpost-shelly-manager-gate`), `mode: proxy`, `authorization_flow: passkey-stepup-authorization` — the same flow `vscode-gate` uses. Every authorize needs a passkey.
- **New git-owned `Shelly Managers` group**, membership in the blueprint (`k.renner`), bound to the application. Blueprint apply is authoritative and reverts UI edits — add people in git.
- **Ingress repointed** from `shelly-manager:80` to `ak-outpost-shelly-manager-gate:9000` via an ExternalName mirror.
- **Scoped CNPs**: ingress rule in `home-assistant` allowing only `outpost-name=shelly-manager-gate` → `:8080`; matching egress rule in `authentik` allowing that outpost → `app.kubernetes.io/name=shelly-manager:8080` only.
## No `skip_path_regex`, deliberately
The music-assistant gate was retired because Chrome fetches `manifest.webmanifest` / `sw.js` with `credentials: 'omit'`, producing a `/authorize` redirect loop; paperless and vscode carve those paths out.
`shelly-manager` ships **neither** — `wwwroot/` is `app.css`, `favicon.png`, `lib/`. Every asset is a same-origin credentialed fetch. It's a Blazor Web app whose SignalR circuit at `/_blazor` upgrades to a websocket carrying the session cookie, which the proxy outpost forwards. Starting with no skip set is the safer default here; adding one blindly would punch an unauthenticated hole.
## This also un-breaks the app
`shelly-manager` has been returning **504 through Traefik since 01:20 today**, when #132 merged. Cause:
`default-deny-ingress` has `endpointSelector: {}` — it applies to **every** pod in `home-assistant`. Its `traefik → :8080` grant was written for the code-server sidecar, but namespace-wide it was in fact the only rule letting Traefik reach `shelly-manager`, which also listens on `8080`. Removing that grant to gate vscode took `shelly-manager`'s ingress down as collateral.
Hubble, on the `shelly-manager` endpoint:
```
ingress/traefik-…:47524 -> home-assistant/shelly-manager-…:8080 Policy denied DROPPED (SYN)
10.244.2.60 (host) -> home-assistant/shelly-manager-…:8080 to-endpoint FORWARDED (SYN,ACK)
```
The kubelet probe matches `reserved:host / ANY`, so the pod stayed `Ready` while Traefik was dropped — which is why nothing alerted.
Gating the app is the right fix rather than re-widening the rule to Traefik. It should not have been reachable unauthenticated to begin with.
I've added a comment above the two gate rules noting the `endpointSelector: {}` footgun explicitly, so the next person who edits a `toPorts` there knows it lands on every pod in the namespace.
## Unrelated note
This PR does **not** touch the DHCP/`sbr` macvlan work from #135/#137. That change was a genuine, separate regression (a DHCP-supplied default route on `net1` shadowing `eth0`'s, taking the pod `NotReady`), fixed by #137, and verified: one default route via `eth0`, `sbr` rule `from 10.2.30.122 lookup 100`, pod serves 200 in 3 ms to a Traefik-shaped request from inside the cluster.
## Verified
- `kubectl kustomize` builds clean for `apps/authentik`, `apps/home-assistant`, `infrastructure/network-policies`.
- Rendered Ingress backends `ak-outpost-shelly-manager-gate:9000`.
- Rendered egress CNP: `outpost-name=shelly-manager-gate` → `shelly-manager:8080`.
- Rendered ingress CNP: `vscode-gate → :8080` and `shelly-manager-gate → :8080` coexist; neither outpost can reach the other's target (enforced by the egress CNPs).
- Traefik is already permitted to reach every pod in the `authentik` namespace, so the outpost needs no additional ingress allow.
## Requires a browser test after apply
Not verifiable from the cluster side, and I'm not going to claim otherwise:
1. `https://shelly-manager.home.renner.uno` → Authentik → passkey step-up → app loads.
2. The Blazor SignalR circuit establishes through the outpost (scan progress updates live). If it doesn't, that's the websocket upgrade, not static assets.
3. A user outside `Shelly Managers` is refused.
Unlike `paperless-gate`, neither this nor `vscode-gate` adds an `/outpost.goauthentik.io` Ingress on `auth.renner.uno` — proxy-mode outposts serve that path on their own `external_host`. If the callback 401-loops, that's the first thing to add.
shelly-manager has no authentication of its own. Anything that can reach
:8080 can rewrite Shelly device config, push scripts, and read the stored
per-device AdminPassword values. Until now the only control on it was
network reachability of shelly-manager.home.renner.uno.
Put it behind Authentik using the same proxy-outpost pattern as the
vscode gate (760e187) and paperless before it:
- New proxy provider + application + dedicated outpost
(ak-outpost-shelly-manager-gate) in mode: proxy. authorization_flow is
passkey-stepup-authorization, the same flow vscode-gate uses, so every
authorize requires a passkey.
- New git-owned "Shelly Managers" group (membership in the blueprint,
k.renner only) bound to the application as the explicit allowlist.
- shelly-manager Ingress now backends the gate outpost via an ExternalName
mirror instead of the app Service directly.
- home-assistant CNP gains a scoped ingress rule: only the
shelly-manager-gate outpost pod may reach :8080. Matching scoped egress
CNP added in the authentik namespace.
No skip_path_regex, deliberately. The music-assistant gate died on PWA
assets fetched with credentials:'omit' (manifest, service worker), and
paperless/vscode carve those out. shelly-manager ships neither -- wwwroot
is app.css, favicon.png and lib/ only -- so every asset is a same-origin
credentialed fetch. It is a Blazor Web app whose SignalR circuit at
/_blazor upgrades to a websocket carrying the session cookie, which the
proxy outpost forwards.
This also restores the app. It has been returning 504 through Traefik
since 760e187 merged at 01:20 today: `default-deny-ingress` has
endpointSelector {}, so its traefik -> :8080 grant, written for the
code-server sidecar, was namespace-wide and was in fact the only rule
letting Traefik reach shelly-manager, which also listens on 8080. Removing
that grant to gate vscode took shelly-manager's ingress down as collateral.
Gating it properly is the correct fix rather than re-widening the rule --
the app should not have been reachable unauthenticated in the first place.
Verified: kubectl kustomize builds clean for authentik, home-assistant and
network-policies; the rendered Ingress backends ak-outpost-shelly-manager-
gate:9000, the egress CNP is scoped to shelly-manager:8080, and the two
gate ingress rules cannot reach each other's target.
The Authentik proxy flow, the passkey login, and the SignalR circuit
through the outpost all require a browser test after apply.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Why
shelly-managerhas no authentication of its own. Anything that reaches:8080can rewrite Shelly device config, push scripts, and read the stored per-deviceAdminPasswordvalues. The only control on it was reachability ofshelly-manager.home.renner.uno.Same treatment as code-server got in #132.
What
Mirrors the
vscode-gatepattern exactly:ak-outpost-shelly-manager-gate),mode: proxy,authorization_flow: passkey-stepup-authorization— the same flowvscode-gateuses. Every authorize needs a passkey.Shelly Managersgroup, membership in the blueprint (k.renner), bound to the application. Blueprint apply is authoritative and reverts UI edits — add people in git.shelly-manager:80toak-outpost-shelly-manager-gate:9000via an ExternalName mirror.home-assistantallowing onlyoutpost-name=shelly-manager-gate→:8080; matching egress rule inauthentikallowing that outpost →app.kubernetes.io/name=shelly-manager:8080only.No
skip_path_regex, deliberatelyThe music-assistant gate was retired because Chrome fetches
manifest.webmanifest/sw.jswithcredentials: 'omit', producing a/authorizeredirect loop; paperless and vscode carve those paths out.shelly-managerships neither —wwwroot/isapp.css,favicon.png,lib/. Every asset is a same-origin credentialed fetch. It's a Blazor Web app whose SignalR circuit at/_blazorupgrades to a websocket carrying the session cookie, which the proxy outpost forwards. Starting with no skip set is the safer default here; adding one blindly would punch an unauthenticated hole.This also un-breaks the app
shelly-managerhas been returning 504 through Traefik since 01:20 today, when #132 merged. Cause:default-deny-ingresshasendpointSelector: {}— it applies to every pod inhome-assistant. Itstraefik → :8080grant was written for the code-server sidecar, but namespace-wide it was in fact the only rule letting Traefik reachshelly-manager, which also listens on8080. Removing that grant to gate vscode tookshelly-manager's ingress down as collateral.Hubble, on the
shelly-managerendpoint:The kubelet probe matches
reserved:host / ANY, so the pod stayedReadywhile Traefik was dropped — which is why nothing alerted.Gating the app is the right fix rather than re-widening the rule to Traefik. It should not have been reachable unauthenticated to begin with.
I've added a comment above the two gate rules noting the
endpointSelector: {}footgun explicitly, so the next person who edits atoPortsthere knows it lands on every pod in the namespace.Unrelated note
This PR does not touch the DHCP/
sbrmacvlan work from #135/#137. That change was a genuine, separate regression (a DHCP-supplied default route onnet1shadowingeth0's, taking the podNotReady), fixed by #137, and verified: one default route viaeth0,sbrrulefrom 10.2.30.122 lookup 100, pod serves 200 in 3 ms to a Traefik-shaped request from inside the cluster.Verified
kubectl kustomizebuilds clean forapps/authentik,apps/home-assistant,infrastructure/network-policies.ak-outpost-shelly-manager-gate:9000.outpost-name=shelly-manager-gate→shelly-manager:8080.vscode-gate → :8080andshelly-manager-gate → :8080coexist; neither outpost can reach the other's target (enforced by the egress CNPs).authentiknamespace, so the outpost needs no additional ingress allow.Requires a browser test after apply
Not verifiable from the cluster side, and I'm not going to claim otherwise:
https://shelly-manager.home.renner.uno→ Authentik → passkey step-up → app loads.Shelly Managersis refused.Unlike
paperless-gate, neither this norvscode-gateadds an/outpost.goauthentik.ioIngress onauth.renner.uno— proxy-mode outposts serve that path on their ownexternal_host. If the callback 401-loops, that's the first thing to add.