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/src/concepts/emergency-reclaim.md
+74Lines changed: 74 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -374,6 +374,80 @@ These are out of scope — recorded here because operators often ask:
374
374
375
375
---
376
376
377
+
## Host-identity verification
378
+
379
+
Before the agent PATCHes any Node with reclaim annotations, it
380
+
cross-checks the host's `/etc/machine-id` against the target Node's
381
+
`status.nodeInfo.machineID`. Both are populated from the same source
382
+
(`systemd-machine-id-setup` on host boot; kubelet on Node registration),
383
+
so they agree on a healthy node — and a mismatch is a strong signal
384
+
that the agent is about to write to the wrong Node.
385
+
386
+
### Why
387
+
388
+
This closes the *modified-DaemonSet → impersonate-victim-Node* path:
389
+
390
+
1. Without the check, the agent trusts whatever value `NODE_NAME` carries
391
+
(sourced from the downward API: `spec.nodeName`).
392
+
2. An attacker with `update daemonsets` in `5spot-system` can override
393
+
`NODE_NAME`to a hard-coded value (e.g. `victim-node`).
394
+
3. The agent runs on host A, sees a process match, and PATCHes
395
+
`victim-node`instead of host A — triggering `Phase::EmergencyRemove`
396
+
on a node it has no business reclaiming.
397
+
398
+
The cross-check makes the impersonation visible: the spoofed Node's
399
+
`status.nodeInfo.machineID`belongs to a different host, so the
400
+
comparison fails and the agent refuses to write.
401
+
402
+
The exploit precondition (`update daemonsets`) is cluster-admin in most
403
+
clusters, so this is **defence-in-depth** — but the check is cheap and
404
+
removes a category of impersonation. Filed as Phase 4 of the
405
+
2026-04-25 security audit roadmap.
406
+
407
+
### Modes
408
+
409
+
| Mode | Flag / env | Behaviour |
410
+
|---|---|---|
411
+
| **Strict (default)** | `--skip-host-id-check=false` / `SKIP_HOST_ID_CHECK=false` | Read `/etc/machine-id` at startup; fail to start if missing or empty. Before each PATCH, fetch the target Node and refuse if `status.nodeInfo.machineID` does not match. |
412
+
| Bypass | `--skip-host-id-check=true` / `SKIP_HOST_ID_CHECK=true` | Trust `NODE_NAME` blindly (pre-Phase-4 behaviour). Use **only** when `/etc/machine-id` is genuinely unavailable: containers without the host file mounted, dev sandboxes, kubelet variants that do not populate `status.nodeInfo.machineID`. |
413
+
414
+
### How `/etc/machine-id` is exposed
415
+
416
+
The DaemonSet mounts the host file as a single read-only file (not the
417
+
whole `/etc`):
418
+
419
+
```yaml
420
+
volumeMounts:
421
+
- name: host-machine-id
422
+
mountPath: /host/etc/machine-id
423
+
readOnly: true
424
+
volumes:
425
+
- name: host-machine-id
426
+
hostPath:
427
+
path: /etc/machine-id
428
+
type: File
429
+
```
430
+
431
+
`type: File` makes kubelet refuse to schedule the pod if the host file
432
+
is missing — fail-fast is preferable to silently degrading to skip-check
433
+
mode. The agent reads the path from `MACHINE_ID_PATH` (default
434
+
`/host/etc/machine-id`when deployed via the manifest).
435
+
436
+
### What it does NOT defend against
437
+
438
+
- A compromise that lets an attacker modify *both* the DaemonSet **and**
439
+
the host's `/etc/machine-id` (would require root on the node).
440
+
- Kubelet bugs or out-of-band manipulation of
441
+
`Node.status.nodeInfo.machineID`. The kubelet writes this field; if
442
+
the cluster trusts a misbehaving kubelet, identity verification has
443
+
no anchor.
444
+
445
+
Both gaps are out of scope for a node-side agent; remediating them
446
+
requires a stronger node-attestation primitive (TPM-backed identity,
447
+
SPIFFE SVIDs, etc.) — not in this controller's scope.
448
+
449
+
---
450
+
377
451
## Related
378
452
379
453
- [Machine Lifecycle](./machine-lifecycle.md) — full phase state machine including `EmergencyRemove`
0 commit comments