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
Copy file name to clipboardExpand all lines: docs/METRICS.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -168,7 +168,7 @@ submitting health events naming another node. See
168
168
|------------|------|--------|-------------|
169
169
|`platform_connector_auth_decisions_total`| Counter |`decision`| Health event batches by the node scope granted to the caller. Values: `node_local`, `cross_node`|
170
170
|`platform_connector_auth_node_claim_total`| Counter |`result`| Whether an authenticated caller's token carried a node claim. `verified`: it carried one and it named this node. `absent`: it carried none, so the caller was pinned to this node — the same scope a tokenless caller gets. |
171
-
| `platform_connector_auth_violations_total` | Counter | `reason` | Health event batches that violated the node-binding rule. Under `global.platformConnectorAuth.mode: enforce` (default) these are rejected; under `mode: audit` they are recorded here but let through. Reasons: `node_mismatch` (a node-local caller named a different node), `node_claim_mismatch` (the token was issued on another node — applies to every caller, allowlisted or not), `unbound_cross_node_token` (an allowlisted caller presented a token bound to no pod, e.g. from `kubectl create token`), `cross_node_claim_absent` (pod-bound, but the pod was never scheduled), `missing_node_name` (no node name on the event and none could be stamped), `token_invalid` (TokenReview rejected the token), `malformed_credentials` (the authorization header was duplicated or did not use the Bearer scheme). Three further reasons mean **no verdict could be reached**, not that the caller was rejected: `validator_unavailable` (the API server was unreachable), `validator_timeout` (the caller gave up or its deadline passed) and `validator_error` (an unexpected failure, typically this component's own RBAC to create TokenReviews). With `failOpenOnUnavailable: true`, `validator_unavailable` and `validator_timeout` still increment this counter but fall back to node-local scope instead of rejecting the request, in enforce mode too — an outage says nothing about the caller's credential. Exclude those three when alerting on suspected credential abuse — a control-plane outage increments them for every in-flight request and would otherwise look identical to an attack: `sum(rate(platform_connector_auth_violations_total{reason=~"node_mismatch\|token_invalid\|node_claim_mismatch\|unbound_cross_node_token\|malformed_credentials\|cross_node_claim_absent\|missing_node_name"}[5m]))` |
171
+
| `platform_connector_auth_violations_total` | Counter | `reason` | Health event batches that violated the node-binding rule. Under `global.platformConnectorAuth.mode: enforce` (default) these are rejected; under `mode: audit` they are recorded here but let through. Reasons: `node_mismatch` (a node-local caller named a different node), `node_claim_mismatch` (the token was issued on another node — applies to every caller, allowlisted or not), `unbound_cross_node_token` (an allowlisted caller presented a token bound to no pod, e.g. from `kubectl create token`), `cross_node_claim_absent` (pod-bound, but the pod was never scheduled), `missing_node_name` (no node name on the event and none could be stamped), `token_invalid` (TokenReview rejected the token), `malformed_credentials` (the authorization header was duplicated or did not use the Bearer scheme). Three further reasons mean **no verdict could be reached**, not that the caller was rejected: `validator_unavailable` (the API server was unreachable), `validator_timeout` (the caller gave up or its deadline passed) and `validator_error` (an unexpected failure, typically this component's own RBAC to create TokenReviews). With `failOpenOnUnavailable: true`, `validator_unavailable` and `validator_timeout` still increment this counter but fall back to a degraded node-local scope instead of rejecting the request, in enforce mode too — an outage says nothing about the caller's credential. That scope is a guess, not a verified identity: an event with a blank or matching node name is accepted and stamped as usual, but one naming a different node is refused as retryable `Unavailable` rather than counted as `node_mismatch`, since the caller might really be an allowlisted cross-node publisher the outage prevented from being verified. `missing_node_name` is the one violation `mode: audit` does not let through — a blank node name from a cross-node caller is always rejected, in both modes, because nothing downstream can handle it. Exclude those three when alerting on suspected credential abuse — a control-plane outage increments them for every in-flight request and would otherwise look identical to an attack: `sum(rate(platform_connector_auth_violations_total{reason=~"node_mismatch\|token_invalid\|node_claim_mismatch\|unbound_cross_node_token\|malformed_credentials\|cross_node_claim_absent\|missing_node_name"}[5m]))` |
Copy file name to clipboardExpand all lines: docs/configuration/authentication.md
+23-3Lines changed: 23 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -50,6 +50,13 @@ validator failure (including `validator_unavailable` and
50
50
request. `audit` changes what happens after a violation is detected, not
51
51
whether requests are checked.
52
52
53
+
One rejection is not affected by `mode`: a cross-node caller's event with no
54
+
node name (`missing_node_name`) is always rejected, in both `audit` and
55
+
`enforce`. Nothing downstream can handle an empty node name, so forwarding
56
+
that event would not be a useful preview of what `enforce` would do — it
57
+
would just be an event `enforce` could never have produced, sitting in the
58
+
datastore.
59
+
53
60
### `failOpenOnUnavailable`
54
61
55
62
Distinguishes a validator that never reached a verdict from one that reached
@@ -62,9 +69,22 @@ credential) does say something about the caller and is always rejected under
62
69
63
70
Defaults to `false`: an unreachable validator rejects the request, matching
64
71
behaviour prior to this setting's introduction. Set `true` to fall back to
65
-
node-local scope instead — the same treatment an anonymous caller gets —
72
+
a **degraded** node-local scope instead — a guess, not a verified identity —
66
73
so that a control-plane blip degrades publishers to their own node rather
67
-
than blocking their health events outright.
74
+
than blocking their health events outright:
75
+
76
+
- An event with a blank or matching node name is accepted and stamped exactly
77
+
as a verified node-local caller's would be. This is the common case the
78
+
setting exists for: most publishers present a token but leave the node name
79
+
blank for platform-connector to fill in.
80
+
- An event naming a *different* node is refused as retryable `Unavailable` —
81
+
the same code the validator itself returned — rather than as
82
+
`node_mismatch`/ `PermissionDenied`. The caller might really be an
83
+
allowlisted cross-node publisher the outage prevented from being verified,
84
+
not an actual mismatch, so it is not counted as `node_mismatch` (one of the
85
+
reasons an operator would alert on as suspected credential abuse) and not
86
+
rejected in a way publishers treat as non-retryable; it is retried once the
87
+
validator recovers instead of being dropped for good.
68
88
69
89
### `audience`
70
90
@@ -156,7 +176,7 @@ cluster already uses.
156
176
| `missing_node_name` | An event carried no node name and none could be stamped. |
157
177
| `token_invalid` | TokenReview rejected the token. |
158
178
| `malformed_credentials` | The authorization header was duplicated, or did not use the Bearer scheme. A *completely absent* header is not a violation — that caller is accepted and pinned to the connector's node. |
159
-
| `validator_unavailable` / `validator_timeout` / `validator_error` | The API server could not be reached, or returned no identity. With `failOpenOnUnavailable: true`, `validator_unavailable` and `validator_timeout` still increment this counter but fall back to node-local scope instead of rejecting the request. |
179
+
| `validator_unavailable` / `validator_timeout` / `validator_error` | The API server could not be reached, or returned no identity. With `failOpenOnUnavailable: true`, `validator_unavailable` and `validator_timeout` still increment this counter but fall back to a degraded node-local scope instead of rejecting the request — see [`failOpenOnUnavailable`](#failopenonunavailable) for how that scope treats a blank vs. a differently-named node. |
160
180
161
181
A healthy cluster reports zero violations. A sustained non-zero
162
182
`validator_unavailable`is an API-server problem, not a caller problem.
Copy file name to clipboardExpand all lines: docs/platform-connectors.md
+18-7Lines changed: 18 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -280,9 +280,15 @@ authenticated caller stays accepted for up to two minutes after its token
280
280
expires or its pod is deleted. An unreachable API server is
281
281
**retried inside the call** with exponential backoff for a few seconds before
282
282
anything is surfaced. Past that the connector **fails closed** with
283
-
`Unavailable`, which every publisher treats as retryable; tokenless callers
284
-
never trigger a TokenReview at all, so custom monitors keep working with no
285
-
API-server dependency whatsoever.
283
+
`Unavailable`by default, which every publisher treats as retryable.
284
+
`global.platformConnectorAuth.failOpenOnUnavailable: true` changes this: the
285
+
caller falls back to a degraded node-local scope instead of being rejected —
286
+
an outage says nothing about the caller's credential — with a blank or
287
+
matching node name accepted and stamped as usual, and an event naming a
288
+
different node kept retryable rather than silently accepted or counted as a
289
+
mismatch. See [Authentication Configuration](configuration/authentication.md)
290
+
for the detail. Tokenless callers never trigger a TokenReview at all, so
291
+
custom monitors keep working with no API-server dependency whatsoever.
286
292
287
293
#### Configuration
288
294
@@ -296,10 +302,15 @@ allowlist and the publishers' projected tokens cannot drift apart:
296
302
| `tokenExpirationSeconds` | `3600` | Token lifetime. Must be a whole number in `600 <= x <= 2^32`; Kubernetes rejects anything else. |
297
303
| `tokenMountPath` | `/var/run/secrets/nvsentinel/platform-connector` | Where publishers mount the token. |
298
304
| `crossNodeServiceAccounts` | `[]` | **Additional** fully-qualified usernames allowed to name other nodes. The bundled monitors are derived from the release namespace for whichever are enabled — do not list them here. |
305
+
| `mode` | `enforce` | `enforce` rejects a violating request. `audit` validates every request and still increments `platform_connector_auth_violations_total` by reason, but lets the request through instead of rejecting it — use it to roll node-binding out against real traffic, confirm the violation counters stay at zero, then switch to `enforce` with evidence. One exception applies in both modes: a cross-node caller's event with no node name is always rejected, since nothing downstream can handle it. |
306
+
| `failOpenOnUnavailable` | `false` | When `true`, a validator that never reached a verdict (the API server was unreachable, or the call timed out) falls back to a degraded node-local scope instead of rejecting the request, since an outage says nothing about the caller's credential. A rejected credential (an invalid or malformed token) is always rejected regardless of this setting. |
299
307
300
-
There is no `mode` and no `requireNodeClaim`. A setting that is "on but
301
-
not enforcing" cannot be reasoned about from its configuration alone, so
302
-
enforcement is not separable from enablement.
308
+
There is no `requireNodeClaim`: whether a claim is checked is not
309
+
configurable, only what happens after a violation is found (`mode`) and how an
310
+
unavailable validator is treated (`failOpenOnUnavailable`). See
311
+
[Authentication Configuration](configuration/authentication.md) for the full
312
+
detail on both, including how a degraded scope treats a blank vs. a
313
+
differently-named node.
303
314
304
315
A **custom cluster-scoped monitor** needs three things: a projected token volume
305
316
with the configured audience, its token path passed to the client dial, and its
| `platform_connector_auth_violations_total` | `reason` | Batches rejected by the interceptor |
398
+
| `platform_connector_auth_violations_total` | `reason` | Batches that violated the node-binding rule. Rejected under `mode: enforce`; recorded but let through under `mode: audit`, except `missing_node_name`, which is always rejected. |
388
399
| `platform_connector_auth_node_claim_total` | `result` | Whether the token carried a node claim (`verified`, `absent`) |
389
400
390
401
Not every `reason` means a caller was rejected. `validator_unavailable`,
0 commit comments