-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathdaemonset.yaml
More file actions
197 lines (197 loc) · 9.21 KB
/
Copy pathdaemonset.yaml
File metadata and controls
197 lines (197 loc) · 9.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# 5-Spot Reclaim Agent DaemonSet
#
# Opt-in node-side emergency reclaim trigger. Lands only on nodes that carry
# the label `5spot.finos.org/reclaim-agent=enabled` (mirrors the kata-deploy
# opt-in pattern). The 5-Spot controller stamps this label on worker nodes
# whose parent ScheduledMachine has a non-empty `spec.killIfCommands`.
#
# The agent:
# - Watches `/proc` on its own host (hostPID + privileged reads).
# - On first match against the command list projected in its per-node
# ConfigMap, PATCHes the Node object with reclaim annotations so the
# controller can enter Phase::EmergencyRemove.
# - Exits 0 after a successful annotation write; kubelet restarts the pod,
# which re-runs and idempotently exits 0 again (annotation already set).
#
# Deployment shape is intentionally an in-pod agent rather than kata's
# "install to systemd via nsenter" pattern: the agent is lighter than kata,
# doesn't need to survive kubelet crashes, and pod restarts are an
# acceptable failure domain for an emergency trigger.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: 5spot-reclaim-agent
namespace: 5spot-system
labels:
app: 5spot-reclaim-agent
app.kubernetes.io/name: 5spot
app.kubernetes.io/component: reclaim-agent
spec:
selector:
matchLabels:
app: 5spot-reclaim-agent
# Allow slow rollouts on large fleets; reclaim is edge-case so there is
# no value in a fast burst.
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 10%
template:
metadata:
labels:
app: 5spot-reclaim-agent
app.kubernetes.io/name: 5spot
app.kubernetes.io/component: reclaim-agent
spec:
serviceAccountName: 5spot-reclaim-agent
# Opt-in: only land on nodes explicitly labeled by the controller.
# Unlabeled nodes never run the agent — zero footprint on fleets that
# do not use killIfCommands.
nodeSelector:
5spot.finos.org/reclaim-agent: enabled
# Tolerate every taint so the agent reaches even cordoned / NotReady
# nodes (it's an emergency component).
tolerations:
- operator: Exists
# Required: the agent reads every pid in `/proc`, which on a pod's
# private pid namespace would only see itself + pause. hostPID exposes
# the node's pid namespace.
hostPID: true
# Emergency component — scheduler must never evict it under pressure.
priorityClassName: system-node-critical
# Terminate fast. The agent holds no state; on delete we just want it
# gone so the next rollout can replace it.
terminationGracePeriodSeconds: 5
# Pod-level security context. An explicit block is required to satisfy
# Trivy KSV-0118 ("default security context configured"). Per-container
# hardening (readOnlyRootFilesystem, drop ALL caps, no privilege
# escalation) is repeated below on the single container; the pod-level
# block enumerates the scope we *cannot* change because of the agent's
# architectural job:
# - runAsNonRoot: false — reading /proc/<pid>/{comm,cmdline} for every
# process on the host requires UID 0 under hardened hidepid=2 /proc
# mounts. A non-root variant would need CAP_SYS_PTRACE added back,
# which is a broader privilege than running as root with every
# other capability dropped.
# - seccompProfile.type: RuntimeDefault — adopt the cluster's default
# syscall filter rather than running unconfined.
# Suppression rationale is in the multi-line block above and mirrors
# the .trivyignore entries for KSV-0012 / KSV-0116 / KSV-0118 / KSV-0105.
securityContext:
# nosemgrep: yaml.kubernetes.security.run-as-non-root-unsafe-value.run-as-non-root-unsafe-value
runAsNonRoot: false
runAsUser: 0
runAsGroup: 0
seccompProfile:
type: RuntimeDefault
containers:
- name: agent
# Pin to a released version tag — NEVER use :latest in production
# manifests (Trivy KSV-0013). Update the tag via the CI/CD release
# workflow. The agent binary is the `5spot-reclaim-agent` bin
# target in this crate; the image is built from the same release
# as the controller.
image: ghcr.io/finos/5-spot-reclaim-agent:v0.1.0
imagePullPolicy: IfNotPresent
# The agent no longer reads its config from a mounted file.
# Instead it watches the per-node ConfigMap
# `reclaim-agent-<NODE_NAME>` in the 5spot-system namespace and
# reloads live on every change. The only runtime knob it needs
# is the proc root; NODE_NAME (below) is the ConfigMap-name key.
args:
- --proc-root=/host/proc
env:
- name: RUST_LOG
value: "info"
- name: RUST_LOG_FORMAT
value: "json"
# NODE_NAME is consumed by the CLI (`--node-name` default). The
# agent PATCHes this Node object on match.
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
# MACHINE_ID_PATH points at the host's /etc/machine-id, mounted
# read-only via the host-machine-id volume below. The agent
# cross-checks its value against the target Node's
# status.nodeInfo.machineID before each PATCH; a mismatch
# refuses the write. Closes the "modified-DaemonSet
# → impersonate-victim-Node" attack documented in Phase 4 of
# the 2026-04-25 security audit roadmap.
- name: MACHINE_ID_PATH
value: "/host/etc/machine-id"
# SKIP_HOST_ID_CHECK opts out of the cross-check above. Default
# is "false" (strict). Enable ONLY for environments where
# /etc/machine-id is genuinely unavailable (containers without
# the host file, dev sandboxes, kubelet variants that do not
# populate status.nodeInfo.machineID). Production must stay
# strict — leaving this commented out keeps the binary default.
# - name: SKIP_HOST_ID_CHECK
# value: "true"
# Reading every `/proc/<pid>/comm` + `cmdline` requires root;
# CAP_SYS_PTRACE is not strictly required and is kept dropped.
# CAP_NET_ADMIN is added for rung 2: subscribing to the
# netlink proc connector (`NETLINK_CONNECTOR` socket family)
# requires it. Drop ALL first, then add only the one cap we
# need. See `.trivyignore` (KSV-0012 / KSV-0116 / KSV-0118 /
# KSV-0105 for runAsNonRoot=false; KSV-0102 for the
# CAP_NET_ADMIN add) for the architectural-necessity
# rationale. Operators who pin `--detector=poll` do not
# exercise the cap; it is granted at the pod level so the
# binary never has to fail open at runtime.
securityContext:
runAsUser: 0
runAsGroup: 0
# nosemgrep: yaml.kubernetes.security.run-as-non-root-unsafe-value.run-as-non-root-unsafe-value
runAsNonRoot: false
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_ADMIN
seccompProfile:
type: RuntimeDefault
resources:
limits:
cpu: 50m
memory: 64Mi
requests:
cpu: 10m
memory: 16Mi
volumeMounts:
- name: host-proc
mountPath: /host/proc
readOnly: true
# Single-file mount of the host's /etc/machine-id into
# /host/etc/machine-id (matches MACHINE_ID_PATH above).
# Mounted read-only — the agent only ever reads it. Single
# file rather than the whole /etc to minimise host filesystem
# exposure: the only path the agent needs is machine-id, so
# everything else stays out of the container's view.
- name: host-machine-id
mountPath: /host/etc/machine-id
readOnly: true
volumes:
# Host /proc — the detection surface. Mounted read-only; the agent
# never writes to /proc, only reads comm + cmdline.
- name: host-proc
hostPath:
path: /proc
type: Directory
# Host machine-id (Phase 4 of the 2026-04-25 security audit
# roadmap). hostPath type=File ensures kubelet refuses to
# schedule the pod if the file is missing on the host — fail
# fast rather than silently degrading to skip-check mode. The
# file is written at host boot by systemd-machine-id-setup
# (or kairos / k0s-installer) and is stable across reboots.
- name: host-machine-id
hostPath:
path: /etc/machine-id
type: File
# No ConfigMap volume. The agent uses the kube API directly to watch
# its per-node ConfigMap (reclaim-agent-<NODE_NAME>) and reloads
# live on every change — see src/bin/reclaim_agent.rs. This sidesteps
# the Kubernetes limitation that `configMap.name` in a volume spec
# cannot be templated by the downward API per replica.