-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnode-security-hardening-daemonset.yaml
More file actions
164 lines (150 loc) · 6.11 KB
/
Copy pathnode-security-hardening-daemonset.yaml
File metadata and controls
164 lines (150 loc) · 6.11 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
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-security-hardening
namespace: kube-system
labels:
app: node-security-hardening
spec:
selector:
matchLabels:
app: node-security-hardening
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
app: node-security-hardening
spec:
# hostPID: enter the host mount+network namespaces (PID 1) via nsenter and
# run the node's own iptables (nft) binary against the host firewall.
hostPID: true
priorityClassName: system-node-critical
nodeSelector:
kubernetes.io/os: linux
yandex.cloud/metadata-server-enabled: "true"
tolerations:
# Tolerate all taints so it lands on every worker node.
- operator: Exists
effect: NoExecute
- operator: Exists
effect: NoSchedule
- key: CriticalAddonsOnly
operator: Exists
initContainers:
- name: apply
image: ubuntu:22.04
env:
# TCP ports to restrict to node-local access, comma-separated.
- name: TARGET_PORTS
value: "15100,15155"
# Host (physical/uplink) interface name matcher. Matched against the WHOLE
# interface name (grep -xE), so pod veths / bridges are never matched.
# Matches e.g. eth0, enp0s3, eno1, enP1p2s0f0, enx001122334455.
- name: HOST_IFACE_REGEX
value: '(en[oPps][0-9]+(f[0-9]+)?(d[0-9]+)?(\.[0-9]+)?)|(eth[0-9]+)'
command:
- /bin/bash
- -c
- |
set -euo pipefail
PORTS="${TARGET_PORTS:-15100,15155}"
REGEX="${HOST_IFACE_REGEX}"
MARK="node-security-hardening"
host() { nsenter -t 1 -m -n -- "$@"; }
echo "========================================="
echo "node-security-hardening"
echo "Node: $(host cat /etc/hostname 2>/dev/null || hostname)"
echo "Date: $(date -u)"
echo "========================================="
# Enumerate host (uplink) interfaces; apply on every physical interface
# (not just the default-route one) so multi-NIC nodes are fully covered.
IFACES="$(host ip -o link show | awk -F': ' '{print $2}' | sed 's/@.*//' | grep -xE "${REGEX}" || true)"
IFACES="$(echo ${IFACES})"
if [ -z "${IFACES}" ]; then
echo "ERROR: no host interface matched /${REGEX}/ ; refusing to proceed" >&2
exit 1
fi
echo "Host interfaces: ${IFACES}"
echo "Target ports: ${PORTS}"
# Idempotency: drop our previous rules (matched by comment marker) before
# re-adding. Re-query line numbers each iteration since they shift on delete.
while :; do
n="$(host iptables -L INPUT --line-numbers -n | awk -v m="${MARK}" '$0 ~ m {print $1; exit}')"
[ -z "${n}" ] && break
host iptables -D INPUT "${n}"
done
# One rule per host interface: reject TCP to the target ports unless the
# source is one of the node's own addresses (--src-type LOCAL).
# * off-node / cross-node clients -> arrive on an uplink, src != LOCAL -> REJECT (tcp-reset)
# * pods on this node -> arrive via pod bridge/veth, not an uplink -> allowed
# * node-local clients -> src IS LOCAL -> allowed
for IF in ${IFACES}; do
echo "Applying on ${IF}..."
host iptables -I INPUT 1 -i "${IF}" -p tcp -m multiport --dports "${PORTS}" \
-m addrtype ! --src-type LOCAL \
-m comment --comment "${MARK}: ${IF}" \
-j REJECT --reject-with tcp-reset
done
echo "Applied rules:"
host iptables -S INPUT | grep -F -- "${MARK}:"
echo "========================================="
echo "Done"
echo "========================================="
securityContext:
privileged: true
containers:
- name: monitor
image: ubuntu:22.04
env:
- name: TARGET_PORTS
value: "15100,15155"
- name: HOST_IFACE_REGEX
value: '(en[oPps][0-9]+(f[0-9]+)?(d[0-9]+)?(\.[0-9]+)?)|(eth[0-9]+)'
# How often to re-check that the rules are still present (seconds).
- name: RECONCILE_INTERVAL
value: "300"
command:
- /bin/bash
- -c
- |
set -uo pipefail
PORTS="${TARGET_PORTS:-15100,15155}"
REGEX="${HOST_IFACE_REGEX}"
INTERVAL="${RECONCILE_INTERVAL:-300}"
MARK="node-security-hardening"
host() { nsenter -t 1 -m -n -- "$@"; }
list_ifaces() {
host ip -o link show | awk -F': ' '{print $2}' | sed 's/@.*//' | grep -xE "${REGEX}" || true
}
rule_present() { # $1 = iface
host iptables -C INPUT -i "$1" -p tcp -m multiport --dports "${PORTS}" \
-m addrtype ! --src-type LOCAL \
-m comment --comment "${MARK}: $1" \
-j REJECT --reject-with tcp-reset 2>/dev/null
}
ensure_iface() { # $1 = iface
rule_present "$1" && return 0
echo "[$(date -u)] rule missing on $1, applying..."
host iptables -I INPUT 1 -i "$1" -p tcp -m multiport --dports "${PORTS}" \
-m addrtype ! --src-type LOCAL \
-m comment --comment "${MARK}: $1" \
-j REJECT --reject-with tcp-reset \
&& echo "[$(date -u)] applied on $1" \
|| echo "[$(date -u)] ERROR: failed to apply on $1"
}
echo "node-security-hardening monitor started on node: $(host cat /etc/hostname 2>/dev/null || hostname)"
echo "Reconcile interval: ${INTERVAL}s"
while true; do
ifaces="$(echo $(list_ifaces))"
if [ -z "${ifaces}" ]; then
echo "[$(date -u)] WARN: no host interface matched, skipping"
else
for IF in ${ifaces}; do ensure_iface "${IF}"; done
fi
sleep "${INTERVAL}"
done
securityContext:
privileged: true