#1922·kubeshark

CLI: replace License-Key bypass with OIDC device-code login (RBAC parity)

Author: corestCreated May 4, 2026Updated May 14, 2026

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 --license flag 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:

  1. kubeshark login discovers the IdP via the configured tap.auth.oidc.issuer (/.well-known/openid-configuration).
  2. CLI POSTs /device/code, prints Visit https://… and enter ABCD-1234.
  3. CLI polls /token while the user signs in via their normal browser SSO (groups + MFA included).
  4. On success, persist id_token + refresh_token to ~/.kubeshark/tokens.json (file mode 0600).
  5. All subsequent CLI → hub calls (internal/connect/hub.go, cmd/console.go, etc.) attach X-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 logout commands.
  • 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 with X-Authorization: Bearer … when a token is cached:
    • cmd/console.go:69
    • internal/connect/hub.go:77,108,131
    • utils/http.go:37 (the Post helper)
  • Fall back to License-Key only when no token is cached (back-compat for unattended scripts during transition).
  • Docs: kubeshark login in 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 login against 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 carry X-Authorization: Bearer … instead of (or in addition to) License-Key.
  • In a deployment with AUTH_ROLES configured, a CLI authenticated as a hub-sock-shop-viewer user 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 logout deletes 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/hub server/middlewares/auth.go:27-33 and auth/authutils/context.go:164 (MarkAuthBypassed)