Skip to content

Add --skip-access-check flag and skipAccessCheck config option#3970

Open
minivolk wants to merge 1 commit intoderailed:masterfrom
minivolk:feat/skip-access-check-flag
Open

Add --skip-access-check flag and skipAccessCheck config option#3970
minivolk wants to merge 1 commit intoderailed:masterfrom
minivolk:feat/skip-access-check-flag

Conversation

@minivolk
Copy link
Copy Markdown

Summary

Adds an opt-in CLI flag --skip-access-check and a corresponding YAML config key k9s.skipAccessCheck that disables the pre-flight SelfSubjectAccessReview (SAR) probes k9s issues before every API request. When enabled, k9s relies solely on the API server's response to the actual call, eliminating the SAR round-trip entirely.
Default behavior is unchanged (flag/key default to false).

Motivation

k9s currently calls SelfSubjectAccessReview (via (*APIClient).CanI) before any resource list/get/watch/edit/delete. The result gates whether informers are created, whether menu items are shown, and whether DAO operations even attempt the call. While this is a useful UX optimization on a vanilla Kubernetes cluster, it breaks badly in environments where the SAR result does not reflect what kubectl would actually return.

Concrete failure case: Teleport (and similar auth proxies)

Teleport's Kubernetes Access proxy applies authorization policy at the proxy layer, not by translating it into RBAC the API server can fully introspect. In particular, Teleport supports per-resource deny rules and label-based filtering that the API server itself is unaware of. The behavior users observe:

Operation What Teleport does What k9s sees today
kubectl get secrets with a deny rule on one secret Returns the list with the denied secret filtered out
SelfSubjectAccessReview for secrets/list Returns allowed: false because any deny block exists for the resource type k9s shows no secrets at all and surfaces "access denied"
kubectl get secret allowed-one Returns the secret successfully n/a (k9s never tries)
So a user who has access to most secrets — minus a single explicitly-denied one — sees an empty/locked-out view in k9s, even though kubectl get secrets works fine. The same pattern applies to any auth provider that:
  • Filters list responses based on per-object policy (Teleport, OPA gatekeeper-style admission filtering, custom webhooks).
  • Uses deny rules that cause SAR to return false more conservatively than actual list/get permissions.
  • Translates policy at a proxy layer the API server cannot answer SAR for.

Other supporting cases

  • High-RTT clusters / metered API servers — eliminates one extra SAR per (resource, verb) pair.
  • Restricted clusters where SAR itself is denied — some hardened environments deny selfsubjectaccessreviews.create. Today k9s breaks; with this flag it works.
  • Audit noise — every k9s startup currently produces dozens of SAR audit-log entries; some compliance setups want to suppress them.

What changed

New CLI flag

--skip-access-check    Skip pre-flight SelfSubjectAccessReview checks
                       (relies on API server responses)

New YAML key (~/.config/k9s/config.yaml)

k9s:
  skipAccessCheck: true

Precedence

Matches the existing --readonly / readOnly pattern:

YAML CLI flag Effective
false not set false (default)
true not set true
false --skip-access-check true (CLI wins when explicitly true)
true --skip-access-check=false true (CLI false does not override YAML — same as --readonly)

Implementation notes

The change has a single chokepoint: (*APIClient).CanI in internal/client/client.go is the one place SAR is issued; all 30+ call sites (factory informers, browser UI gates, DAO operations, metrics, cluster info) flow through it. A short-circuit at the top of CanI therefore covers the entire app without touching any caller. When skipped, errors surface naturally on the actual API call — k9s already handles those.

Files changed

  • internal/config/flags.goSkipAccessCheck *bool on Flags
  • cmd/root.go — register flag; resolve final value (CLI > YAML) into client.Config.SkipAccessCheck after K9s.Override
  • internal/client/config.goSkipAccessCheck bool field on Config
  • internal/client/client.go — short-circuit CanI; preserve flag across SwitchContext
  • internal/config/k9s.go — persisted SkipAccessCheck field, manual override, IsSkipAccessCheck(), Merge propagation
  • internal/config/json/schemas/k9s.json — schema entry for YAML validation
  • internal/client/client_test.go, internal/config/flags_test.go, internal/config/k9s_test.go — unit tests
  • README.md — CLI usage and YAML examples

Tests

  • TestCanI_SkipAccessCheck — verifies CanI short-circuits without invoking the API server when the flag is set.
  • TestK9sIsSkipAccessCheck — table-driven precedence matrix (default / yaml-only / cli-only / both / cli-false-no-override).
  • Updated TestNewFlags to assert the new default.

How to verify

# Default (no behavior change)
k9s --logLevel debug
# → grep '\[CAN\] access' should still produce log lines
# CLI flag
k9s --skip-access-check --logLevel debug
# → no '[CAN] access' lines; secrets list works on Teleport-managed clusters
# YAML
echo 'k9s: { skipAccessCheck: true }' >> ~/.config/k9s/config.yaml
k9s --logLevel debug
# → no '[CAN] access' lines

Backwards compatibility

  • Default value is false. No existing user is affected.
  • YAML schema additions are additive (additionalProperties: false is preserved by adding the property explicitly).
  • No public API/interface changes.

Risks & tradeoffs

Risk Mitigation
Browser UI may show menu items the user can't actually invoke Acceptable — the API server still rejects, and k9s already surfaces those errors. This is also the desired behavior for Teleport users (whose RBAC is enforced by the proxy, not the apiserver).
Slightly more failed API calls (when user truly lacks access) Negligible; one failure per attempted action vs. one SAR per startup × resource × verb.
Cache no longer prevents repeated denied attempts Same as above; not a regression — cache only existed to skip work that the SAR previously precomputed.

@minivolk minivolk force-pushed the feat/skip-access-check-flag branch from 2708e50 to e375b97 Compare April 28, 2026 12:59
@minivolk
Copy link
Copy Markdown
Author

#3486

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant