You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Network trace: `/api/trace/{kind}/{ns}/{name}` (path-shaped diagnosis for Service/Ingress/HTTPRoute/GRPCRoute/Gateway; `?probe=true` runs DNS/TCP/TLS/HTTP probes against the declared path — direct TCP in-cluster, K8s API server proxy from a laptop, gated by the user's `services/proxy` + `pods/proxy` RBAC).
- RBAC reverse-lookup: `/api/rbac/subject/{kind}/{namespace}/{name}` (ServiceAccount) and `/api/rbac/subject/{kind}/{name}` (User/Group) return direct + group-inherited bindings + flattened effective rules. SA subjects also get a `usedByPods` list (Pods whose `spec.serviceAccountName` matches — closes the loop on the SA detail page). `/api/rbac/role/{kind}/{namespace}/{name}` (use `_` for ClusterRole's empty namespace) returns the inverse — bindings that reference the role + their subjects. `/api/rbac/namespace/{namespace}` returns RoleBindings in the namespace + ClusterRoleBindings with at least one SA subject in it + a ServiceAccount count (backs the NamespaceRenderer's RBAC section; group-only ClusterRoleBindings like `system:authenticated` grants are deliberately excluded — they'd appear in every namespace and would be noise). `/api/rbac/whoami?namespace=...` is a pass-through of `SelfSubjectRulesReview` for the current user. Backed by `pkg/rbac/` (pure index + 5s TTL memo); endpoints gate on `list rolebindings` AND `list clusterrolebindings` (403 when either is denied — silent partial views would mislead operators).
Copy file name to clipboardExpand all lines: README.md
+12Lines changed: 12 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -362,6 +362,18 @@ Proactive best-practices scanner with 31 checks across security, reliability, an
362
362
- Framework labels: NSA/CISA, CIS benchmarks
363
363
- MCP tool (`get_cluster_audit`) for AI-assisted cluster analysis
364
364
365
+
### Network Path Diagnose
366
+
367
+
Hop-ordered diagnosis for Service, Ingress, HTTPRoute, GRPCRoute, and Gateway — answering "if traffic is sent toward this resource, does it reach a healthy process, and if not which hop breaks first?"
368
+
369
+
- Composes the detections Radar already runs (missing backend Service, port mismatches, no-ready-endpoints, route not Accepted by parent Gateway, readiness probe targeting the wrong port) into a path shape ordered along the traffic flow
370
+
- Upstreams (Ingresses / Routes pointing at a Service) are judged independently — one broken Ingress doesn't condemn the other delivery paths
371
+
- First critical hop is named explicitly so the operator can localize the break without reading the whole list; each finding ships a kubectl reproducer
372
+
-**Optional one-shot reachability test** runs DNS / TCP / TLS / HTTP probes against the declared path — direct TCP when Radar is in-cluster, K8s API server proxy when running from a laptop — so the same button works regardless of where Radar runs. Probes never override the static verdict; they add evidence.
373
+
- NetworkPolicies that select the subject's pods are statically evaluated for their caller-independent ingress rules: a "would block" **WARNING prediction** when no rule admits the path's port, a source-restricted advisory, or an outbound egress note. It's a prediction, never a verdict — the CNI is the only enforcement authority, so the live in-cluster probe confirms or downgrades it
374
+
- Static trace is pure functions over the in-memory informer cache. Active probing from a laptop uses the cluster's normal RBAC (`get services/proxy`, `get pods/proxy`); in-cluster mode goes directly to the data path.
375
+
- Exposed via the **Diagnose** tab in the resource detail view and via the MCP `diagnose` tool for AI consumers — see [docs/diagnose.md](docs/diagnose.md)
376
+
365
377
### Access Control (RBAC visibility)
366
378
367
379
Inspect what any ServiceAccount can actually do — without three `kubectl describe` calls.
// Load persistent config (~/.radar/config.json) for flag defaults.
45
60
// CLI flags override config file values.
46
61
fileCfg:=config.Load()
@@ -62,6 +77,7 @@ func main() {
62
77
disableLocalTerminal:=flag.Bool("disable-local-terminal", false, "Disable local terminal feature")
63
78
podShellDefault:=flag.String("pod-shell-default", "", "Override the default pod exec shell command (runs as 'sh -c <value>'; empty = built-in bash -il → ash → sh cascade)")
64
79
debugImage:=flag.String("debug-image", fileCfg.DebugImage, "Image for ephemeral debug containers and node debug pods (empty = busybox:latest; point at a mirror for air-gapped/private-registry clusters)")
80
+
reachabilityImage:=flag.String("reachability-image", fileCfg.ReachabilityImage, "Image for the in-cluster reachability probe Job (empty = RADAR_IMAGE env, then the version-matched published Radar image; point at a mirror for air-gapped clusters)")
65
81
listPageSize:=flag.Int64("list-page-size", 0, "Paginate the initial LIST of high-cardinality kinds (Pods, ReplicaSets) at this page size on clusters without WatchList streaming. 0 = off (single LIST). Try 2000 if a very large cluster fails to sync.")
66
82
// Timeline storage options
67
83
timelineStorage:=flag.String("timeline-storage", fileCfg.TimelineStorageOr("memory"), "Timeline storage backend: memory or sqlite")
@@ -118,6 +134,16 @@ func main() {
118
134
maxScopeCandidates:=flag.Int("max-scope-candidates", k8s.EnvIntOr("RADAR_MAX_SCOPE_CANDIDATES", 20), "Cap on the namespace-fallback probe fanout for users who can list namespaces cluster-wide but not list a specific kind cluster-wide (default: 20). Raise for clusters with more than 20 namespaces to avoid silently marking kinds as denied in dropped namespaces. Env: RADAR_MAX_SCOPE_CANDIDATES")
119
135
flag.Parse()
120
136
137
+
// An explicit --reachability-image override applies to BOTH probe paths. It must
138
+
// win over the in-cluster self-read, so record it as the configured override
139
+
// (checked ABOVE self-read), not as DefaultImageRef (the last-resort fallback,
140
+
// below self-read — which would let radar's own pod image beat the operator's
141
+
// explicit choice on the MCP path). REST passes the config as the override arg;
142
+
// MCP has no server config, so it relies on this. Air-gapped mirrors work on both.
0 commit comments