CLI: replace License-Key bypass with OIDC device-code login (RBAC parity)
Follow-up to kubeshark/hub#734.
The hub-side OIDC RBAC refactor (kubeshark/hub#734, see kubeshark-tasks/hub-oidc-rbac.md) makes per-action and per-namespace authorization work for browser users on AUTH_TYPE=oidc. It deliberately leaves the CLI auth path as-is: the CLI continues to short-circuit hub auth via the License-Key bypass, which means CLI calls remain unconditionally admin-equivalent regardless of the deployment's AUTH_ROLES. This issue tracks closing that gap.
Problem
The CLI authenticates to the hub by sending the cluster's installed license string as License-Key: <license> on every request. The hub's Auth() middleware short-circuits on a matching header (hub/server/middlewares/auth.go:27-33), marks the request as IsAuthBypassed, and admits it with no identity, no roles, no namespace scope.
Concrete consequences in a deployment with auth + RBAC enabled (e.g. tap.auth.type=oidc, AUTH_ROLES populated):
- Browser users are gated correctly: OIDC session → identity + role →
Authz()enforces actions,injectRoleFilter(hub/pkg/connectgo/authz_filter.go) ANDs the namespace scope into queries. - The CLI is unconditionally admin. It can
kubeshark console, download any PCAP, manipulate scripts, change targeted pods, etc. — across every namespace — regardless of who's running it. - Anyone holding the license string (cluster-secret read access, operator's
~/.kubeshark/config.yaml, a leaked--licenseflag on a shared shell) gets the same admin equivalence.
The bypass exists because the CLI today has no way to obtain a real session. The hub side is ready (the OIDC middleware already validates X-Authorization: Bearer … for browsers); the missing piece is a CLI-side credential.
Proposed solution
Implement the OIDC device authorization grant (RFC 8628) in the CLI as the canonical interactive auth path. This is the same pattern kubectl --auth-provider=oidc, gcloud, gh, az use.
Flow:
kubeshark logindiscovers the IdP via the configuredtap.auth.oidc.issuer(/.well-known/openid-configuration).- CLI POSTs
/device/code, printsVisit https://… and enter ABCD-1234. - CLI polls
/tokenwhile the user signs in via their normal browser SSO (groups + MFA included). - On success, persist
id_token+refresh_tokento~/.kubeshark/tokens.json(file mode 0600). - All subsequent CLI → hub calls (
internal/connect/hub.go,cmd/console.go, etc.) attachX-Authorization: Bearer <id_token>. Refresh on 401.
The hub validates the bearer token through the same oidc.OidcAuthMiddleware used by browser sessions, so RBAC, action gating, and the role-derived KFL filter apply unchanged.
Scope
In scope (this issue):
kubeshark login/kubeshark logoutcommands.- IdP discovery, device-code request, polling with respect to
interval/slow_down. - Token cache file (read/write, mode 0600, refresh on expiry).
- Replace
headers.Set("License-Key", config.Config.License)callsites withX-Authorization: Bearer …when a token is cached:cmd/console.go:69internal/connect/hub.go:77,108,131utils/http.go:37(thePosthelper)
- Fall back to
License-Keyonly when no token is cached (back-compat for unattended scripts during transition). - Docs:
kubeshark loginin CLI README, mention in helm-chart README under the OIDC section.
Out of scope (separate follow-ups):
- SAML CLI flow — SAML is browser-redirect-based and has no standard CLI grant. SAML-only deployments either run a parallel OIDC client at the same IdP (Okta/Azure AD/Keycloak all support both) or keep the License-Key bypass for the CLI. Document the trade-off.
- Descope CLI flow — Use the Descope SDK's access-key / machine-token primitives. Different code path, file separately.
- Removing the License-Key bypass entirely — keep it as a supported fallback for now (CI scripts, headless ops). Deprecate in a later release once OIDC adoption is broad.
- k8s service-account TokenReview path — alternative auth method; valid future option but orthogonal to this issue.
Acceptance criteria
- Operator can run
kubeshark loginagainst an OIDC-configured deployment, complete the device-code dance in their browser, and the CLI persists tokens locally. - After
kubeshark login, all CLI → hub requests carryX-Authorization: Bearer …instead of (or in addition to)License-Key. - In a deployment with
AUTH_ROLESconfigured, a CLI authenticated as ahub-sock-shop-vieweruser is denied PCAP downloads from other namespaces and is denied scripting writes — same enforcement browser users get. - Without
kubeshark login, behavior is unchanged from today (License-Key bypass still works) so existing scripts don't break. - On 401 with
token_expired, CLI silently refreshes once before failing. - Token file is mode 0600;
kubeshark logoutdeletes it. - Docs updated: CLI README, helm-chart README OIDC section, and a migration note pointing at this issue.
Related
- Parent: kubeshark/hub#734
- Hub authz refactoring task doc:
kubeshark-tasks/hub-oidc-rbac.md - Bypass code path being replaced:
kubeshark/hubserver/middlewares/auth.go:27-33andauth/authutils/context.go:164(MarkAuthBypassed)
Source: kubeshark/kubeshark