-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsctphantom-mitigation-daemonset.yaml
More file actions
283 lines (268 loc) · 11.9 KB
/
Copy pathsctphantom-mitigation-daemonset.yaml
File metadata and controls
283 lines (268 loc) · 11.9 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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: cve-2026-64564-fix
namespace: kube-system
labels:
app: cve-2026-64564-fix
security: vulnerability-mitigation
spec:
selector:
matchLabels:
app: cve-2026-64564-fix
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
app: cve-2026-64564-fix
security: vulnerability-mitigation
spec:
hostPID: true
priorityClassName: system-node-critical
tolerations:
# Tolerate all taints to ensure deployment on all worker nodes
- operator: Exists
effect: NoExecute
- operator: Exists
effect: NoSchedule
initContainers:
- name: apply-fix
image: ubuntu:22.04
env:
# Set to "true" to unload sctp even if active SCTP sockets are found on the node.
# This will break those SCTP connections - see README.
- name: FORCE_APPLY
value: "false"
command:
- /bin/bash
- -c
- |
set -e
# sctp_diag first: it depends on sctp
MODULES="sctp_diag sctp"
CONF=/host/etc/modprobe.d/blacklist-sctp.conf
echo "========================================="
echo "SCTPhantom (CVE-2026-64564) mitigation for Yandex Managed K8s"
echo "Node: $(cat /host/etc/hostname 2>/dev/null || hostname)"
echo "Date: $(date)"
echo "FORCE_APPLY: ${FORCE_APPLY}"
echo "========================================="
echo ""
echo "Step 1: Checking modules state before fix..."
# Intentionally passive: we do NOT try to open an SCTP socket before the
# blacklist is in place, because that would autoload sctp on a node where
# it is not loaded yet and make the node more exposed, not less.
for mod in ${MODULES}; do
if chroot /host lsmod 2>/dev/null | grep -q "^${mod} "; then
refcnt=$(cat /host/sys/module/${mod}/refcnt 2>/dev/null || echo "?")
echo " [LOADED] ${mod} (refcnt=${refcnt}) - node is exposed"
else
echo " [UNLOADED] ${mod}"
fi
done
echo ""
echo "Step 2: Checking whether SCTP is in use on this node..."
# SCTP is not used by Kubernetes itself, but a workload may declare
# protocol: SCTP in a Service/Pod. Unloading the module under such a
# workload would break its connections, so we detect that first.
SCTP_IN_USE=false
if chroot /host lsmod 2>/dev/null | grep -q "^sctp "; then
# NOTE: refcnt is NOT a usage indicator. On Ubuntu 22.04 / kernel 5.15
# a freshly autoloaded, completely unused sctp module already reports
# refcnt=6 (protocol/proc registrations), so it is logged for
# information only and never used to decide anything.
SCTP_REFCNT=$(cat /host/sys/module/sctp/refcnt 2>/dev/null || echo "?")
echo " sctp is loaded, refcnt=${SCTP_REFCNT} (informational only)"
# Real usage is decided by live associations and endpoints
for f in assocs eps; do
if [ -r /host/proc/net/sctp/${f} ]; then
# first line is the header, so anything beyond it means live entries
if [ "$(tail -n +2 /host/proc/net/sctp/${f} 2>/dev/null | grep -c .)" != "0" ]; then
SCTP_IN_USE=true
echo " live entries found in /proc/net/sctp/${f}"
fi
fi
done
fi
if [ "${SCTP_IN_USE}" = "true" ]; then
echo " ⚠ SCTP appears to be IN USE on this node"
else
echo " ✓ No SCTP usage detected (expected: Kubernetes itself does not use SCTP)"
fi
echo ""
echo "Step 3: Applying the mitigation..."
mkdir -p /host/etc/modprobe.d
# 'install ... /bin/false' also blocks an explicit 'modprobe sctp',
# 'blacklist' additionally blocks alias-based autoload
printf 'install sctp /bin/false\ninstall sctp_diag /bin/false\nblacklist sctp\nblacklist sctp_diag\n' \
> ${CONF}
echo "Created /etc/modprobe.d/blacklist-sctp.conf"
echo ""
echo "Step 4: Unloading vulnerable modules..."
for mod in ${MODULES}; do
if ! chroot /host lsmod 2>/dev/null | grep -q "^${mod} "; then
echo " ${mod} was not loaded (OK)"
continue
fi
if [ "${SCTP_IN_USE}" = "true" ] && [ "${FORCE_APPLY}" != "true" ]; then
echo " Skipping ${mod} (SCTP is in use - set FORCE_APPLY=true to override)"
continue
fi
chroot /host rmmod ${mod} 2>/dev/null \
&& echo " ${mod} unloaded" \
|| echo " ⚠ could not unload ${mod} (see the note about refcnt below)"
done
echo ""
echo "Step 5: Validating the configuration..."
if [ -f ${CONF} ]; then
echo "Configuration file is in place:"
cat ${CONF}
else
echo "ERROR: mitigation verification failed - configuration file not found"
exit 1
fi
echo ""
echo "Step 6: Validating that the modules can no longer be loaded..."
for mod in ${MODULES}; do
# Skip the probe if the module is already resident - it proves nothing
# and the "was it loaded by us" check below would misfire
if chroot /host lsmod 2>/dev/null | grep -q "^${mod} "; then
echo " ${mod} is still resident, skipping the modprobe test"
continue
fi
chroot /host modprobe ${mod} 2>/dev/null || true
if chroot /host lsmod 2>/dev/null | grep -q "^${mod} "; then
# The blacklist did not work. Undo our own load so that this check
# does not leave the node more exposed than it was, then fail loudly.
echo " ERROR: ${mod} was loaded despite the blacklist!"
chroot /host rmmod ${mod} 2>/dev/null \
&& echo " ${mod} unloaded again after the failed test" \
|| echo " ⚠ could not unload ${mod} after the failed test"
exit 1
fi
echo " modprobe ${mod} is blocked ✓"
done
# Autoload path an attacker would use: socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)
if nsenter -t 1 -m -u -i -n -p -- test -x /usr/bin/python3 2>/dev/null; then
if nsenter -t 1 -m -u -i -n -p -- python3 -c 'import socket; socket.socket(socket.AF_INET, socket.SOCK_STREAM, 132).close()' 2>/dev/null; then
# An SCTP socket can only be opened while the module is resident. If we
# failed to unload it, this is the expected PARTIAL state (reported in
# step 7), not a failure of the blacklist itself.
if chroot /host lsmod 2>/dev/null | grep -q "^sctp "; then
echo " ⚠ SCTP socket still available: the sctp module is still resident and could not be unloaded"
else
echo " ERROR: SCTP socket is still available even though sctp is not resident - blacklist did not work!"
exit 1
fi
else
echo " SCTP socket creation is blocked ✓"
fi
else
echo " python3 not available on the host, skipping the SCTP socket test"
fi
echo ""
echo "Step 7: Validating modules state after fix..."
FAILED=false
for mod in ${MODULES}; do
if chroot /host lsmod 2>/dev/null | grep -q "^${mod} "; then
refcnt=$(cat /host/sys/module/${mod}/refcnt 2>/dev/null || echo "?")
echo " [STILL LOADED] ${mod} (refcnt=${refcnt}) - WARNING: module could not be unloaded"
FAILED=true
else
echo " [UNLOADED] ${mod} ✓"
fi
done
echo ""
echo "========================================="
if [ "${FAILED}" = "true" ]; then
echo "⚠ CVE-2026-64564 mitigation applied PARTIALLY"
echo " The blacklist is in place, so the module will not be loaded again after a"
echo " reboot, but it is still resident in memory and the node stays exposed."
echo " Note: on many kernels (incl. Ubuntu 22.04 / 5.15) sctp keeps a non-zero"
echo " refcnt once it has been used and can no longer be unloaded at all."
echo " In that case reboot the node or recreate the node group to close the vector."
else
echo "✓ CVE-2026-64564 mitigation applied successfully"
fi
echo "========================================="
securityContext:
privileged: true
volumeMounts:
- name: host-root
mountPath: /host
resources:
requests:
cpu: 10m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
containers:
- name: monitor
image: ubuntu:22.04
env:
# Keep in sync with the initContainer: controls whether the monitor may
# unload sctp while a workload is actively using it.
- name: FORCE_APPLY
value: "false"
command:
- /bin/bash
- -c
- |
MODULES="sctp_diag sctp"
CONF=/host/etc/modprobe.d/blacklist-sctp.conf
echo "CVE-2026-64564 mitigation monitoring started on node: $(cat /host/etc/hostname 2>/dev/null || hostname)"
echo "Monitoring interval: 1 hour"
# Keep the pod running and monitor the mitigation
while true; do
sleep 3600
# Check if the configuration file still exists
if [ ! -f ${CONF} ]; then
echo "[$(date)] WARNING: blacklist-sctp.conf missing, reapplying..."
mkdir -p /host/etc/modprobe.d
printf 'install sctp /bin/false\ninstall sctp_diag /bin/false\nblacklist sctp\nblacklist sctp_diag\n' \
> ${CONF}
echo "[$(date)] Configuration restored"
fi
# Check whether the vulnerable modules got loaded again
for mod in ${MODULES}; do
if chroot /host lsmod 2>/dev/null | grep -q "^${mod} "; then
refcnt=$(cat /host/sys/module/${mod}/refcnt 2>/dev/null || echo "?")
# Same guard as the init container: live associations, not refcnt
IN_USE=false
for f in assocs eps; do
if [ -r /host/proc/net/sctp/${f} ] && [ "$(tail -n +2 /host/proc/net/sctp/${f} 2>/dev/null | grep -c .)" != "0" ]; then
IN_USE=true
fi
done
if [ "${IN_USE}" = "true" ] && [ "${FORCE_APPLY}" != "true" ]; then
echo "[$(date)] WARNING: ${mod} is loaded and IN USE on $(cat /host/etc/hostname) (refcnt=${refcnt}), not unloading (FORCE_APPLY=false)"
continue
fi
echo "[$(date)] WARNING: ${mod} is loaded on $(cat /host/etc/hostname) (refcnt=${refcnt}), attempting to unload..."
chroot /host rmmod ${mod} 2>/dev/null \
&& echo "[$(date)] ${mod} unloaded" \
|| echo "[$(date)] Could not unload ${mod} (in many kernels sctp cannot be unloaded once used - a node reboot or node group recreation is required)"
fi
done
done
securityContext:
privileged: true
volumeMounts:
- name: host-root
mountPath: /host
resources:
requests:
cpu: 5m
memory: 32Mi
limits:
cpu: 50m
memory: 64Mi
volumes:
- name: host-root
hostPath:
path: /
type: Directory