Skip to content

Commit dfb525e

Browse files
committed
feat: address feedback
1 parent 73612cd commit dfb525e

4 files changed

Lines changed: 27 additions & 7 deletions

File tree

distros/kubernetes/nvsentinel/templates/_helpers.tpl

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,16 @@ platform-connector's own ConfigMap; publishers do not need it.
371371
*/}}
372372
{{- define "nvsentinel.pcAuth.mode" -}}
373373
{{- $auth := ((.Values.global).platformConnectorAuth) | default dict -}}
374-
{{- $mode := $auth.mode | default "enforce" -}}
374+
{{- /*
375+
`default` treats false, 0, "" and nil as empty, so `$auth.mode | default
376+
"enforce"` would silently accept a typo'd non-string value by defaulting it
377+
away instead of rejecting it. Check presence explicitly, then validate
378+
whatever was actually supplied.
379+
*/ -}}
380+
{{- $mode := "enforce" -}}
381+
{{- if hasKey $auth "mode" -}}
382+
{{- $mode = index $auth "mode" -}}
383+
{{- end -}}
375384
{{- if not (kindIs "string" $mode) -}}
376385
{{- fail (printf "global.platformConnectorAuth.mode must be a string (\"enforce\" or \"audit\"), got %s %#v." (kindOf $mode) $mode) -}}
377386
{{- end -}}
@@ -390,7 +399,15 @@ ConfigMap.
390399
*/}}
391400
{{- define "nvsentinel.pcAuth.failOpenOnUnavailable" -}}
392401
{{- $auth := ((.Values.global).platformConnectorAuth) | default dict -}}
393-
{{- $failOpen := $auth.failOpenOnUnavailable | default false -}}
402+
{{- /*
403+
Same reasoning as nvsentinel.pcAuth.mode: `default` would treat an explicit
404+
0 as absent and silently coerce it to false rather than rejecting the wrong
405+
type, so presence is checked explicitly first.
406+
*/ -}}
407+
{{- $failOpen := false -}}
408+
{{- if hasKey $auth "failOpenOnUnavailable" -}}
409+
{{- $failOpen = index $auth "failOpenOnUnavailable" -}}
410+
{{- end -}}
394411
{{- if not (kindIs "bool" $failOpen) -}}
395412
{{- fail (printf "global.platformConnectorAuth.failOpenOnUnavailable must be a boolean (true or false), got %s %#v." (kindOf $failOpen) $failOpen) -}}
396413
{{- end -}}

docs/METRICS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ submitting health events naming another node. See
168168
|------------|------|--------|-------------|
169169
| `platform_connector_auth_decisions_total` | Counter | `decision` | Health event batches by the node scope granted to the caller. Values: `node_local`, `cross_node` |
170170
| `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"}[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 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]))` |
172172

173173
### Kubernetes Connector Metrics
174174

docs/configuration/authentication.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ request through instead of rejecting it.
4444
Use `audit` to roll node-binding out against real traffic: run it for a
4545
period, confirm the violation counters stay at zero, then switch to `enforce`
4646
with evidence rather than finding out what it breaks in production. `audit`
47-
still requires `enabled: true` and a working validator — it changes what
48-
happens after a violation is detected, not whether requests are checked.
47+
still requires `enabled: true`, and every request is still validated: a
48+
validator failure (including `validator_unavailable` and
49+
`validator_timeout`) is still recorded, it just no longer rejects the
50+
request. `audit` changes what happens after a violation is detected, not
51+
whether requests are checked.
4952

5053
### `failOpenOnUnavailable`
5154

platform-connectors/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,12 +468,12 @@ func initializeAuthInterceptor(
468468

469469
mode, err := authMode(config)
470470
if err != nil {
471-
return nil, err
471+
return nil, fmt.Errorf("parse AuthMode: %w", err)
472472
}
473473

474474
failOpenOnUnavailable, err := boolFromConfig(config, "AuthFailOpenOnUnavailable", false)
475475
if err != nil {
476-
return nil, err
476+
return nil, fmt.Errorf("parse AuthFailOpenOnUnavailable: %w", err)
477477
}
478478

479479
interceptor, err := auth.NewNodeBindingInterceptor(auth.Config{

0 commit comments

Comments
 (0)