Skip to content

Commit f92c45d

Browse files
author
Michel Jansen
committed
feat(kubernetes): configure per-pool Talos sysctls
Worker sysctl requirements extend beyond user namespaces. A string map keeps those settings in the Talos machine config without expanding the application API for each kernel knob. Tenant input crosses an unquoted shell heredoc in the reconcile Job, so preserve scalar contents without allowing shell or YAML injection. Assisted-by: LLM Signed-off-by: Michel Jansen <[email protected]>
1 parent 10e0ef9 commit f92c45d

9 files changed

Lines changed: 184 additions & 19 deletions

File tree

‎api/apps/v1alpha1/kubernetesnodes/types.go‎

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎api/apps/v1alpha1/kubernetesnodes/zz_generated.deepcopy.go‎

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎hack/check-talosconfigtemplate-name-agreement.bats‎

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,87 @@ assert_agreement() {
167167
fi
168168
rm -rf "$TMP"
169169
}
170+
171+
@test "sysctl additions changes and removal keep Job RBAC and bootstrap references in agreement" {
172+
TMP=$(mktemp -d)
173+
render_pool "$TMP/r.yaml"
174+
baseline=$(job_tct_name "$TMP/r.yaml")
175+
render_pool "$TMP/r.yaml" --set-string 'talos.sysctls.user\.max_user_namespaces=11255'
176+
assert_agreement "$TMP" "sysctls enabled"
177+
enabled=$(job_tct_name "$TMP/r.yaml")
178+
[ "$enabled" != "$baseline" ]
179+
render_pool "$TMP/r.yaml" --set-string 'talos.sysctls.user\.max_user_namespaces=0'
180+
assert_agreement "$TMP" "explicit zero sysctl"
181+
[ "$(job_tct_name "$TMP/r.yaml")" != "$enabled" ]
182+
[ "$(job_tct_name "$TMP/r.yaml")" != "$baseline" ]
183+
render_pool "$TMP/r.yaml" --set-json 'talos.sysctls={}'
184+
assert_agreement "$TMP" "sysctls removed"
185+
[ "$(job_tct_name "$TMP/r.yaml")" = "$baseline" ]
186+
rm -rf "$TMP"
187+
}
188+
189+
@test "sysctl names and values survive the actual Job heredoc literally without shell execution" {
190+
TMP=$(mktemp -d)
191+
cat > "$TMP/values.yaml" <<'VALUES'
192+
talos:
193+
sysctls:
194+
user.max_user_namespaces: "11255"
195+
net.ipv4.ip_local_port_range: "1024\t65535"
196+
kernel.panic: "0"
197+
'$(touch "$SYSCTL_MARKER")': '$(touch "$SYSCTL_MARKER")'
198+
'`touch "$SYSCTL_MARKER"`': '`touch "$SYSCTL_MARKER"`'
199+
'${SYSCTL_MARKER}': '${SYSCTL_MARKER}'
200+
'backslash\$(touch "$SYSCTL_MARKER")': 'backslash\$(touch "$SYSCTL_MARKER")'
201+
"quotes\"\\\nEOF\n$(touch \"$SYSCTL_MARKER\")": "quotes\"\\\nEOF\n$(touch \"$SYSCTL_MARKER\")"
202+
"unicode\u0085EOF\u2028EOF\u2029": "unicode\u0085EOF\u2028EOF\u2029"
203+
"empty": ""
204+
VALUES
205+
render_pool "$TMP/r.yaml" -f "$TMP/values.yaml"
206+
yq -r 'select(.kind == "Job") | .spec.template.spec.containers[0].command[2]' "$TMP/r.yaml" > "$TMP/job.sh"
207+
# Execute only the real manifest-producing heredoc, never the surrounding
208+
# kubectl/Talos operations. YAML is parsed on both sides of the shell so
209+
# escaping that prevents execution but corrupts the machine config also fails.
210+
awk '/^TCT_MANIFEST=/{emit=1} emit{print} emit && /^\)"$/{exit}' "$TMP/job.sh" > "$TMP/heredoc.sh"
211+
[ -s "$TMP/heredoc.sh" ]
212+
printf '\nprintf "%%s\\n" "$TCT_MANIFEST"\n' >> "$TMP/heredoc.sh"
213+
yq -o=json '.talos.sysctls | sort_keys(.)' "$TMP/values.yaml" > "$TMP/expected.json"
214+
for shell in bash dash; do
215+
SYSCTL_MARKER="$TMP/executed" TALOS_TOKEN=runtime-token "$shell" "$TMP/heredoc.sh" > "$TMP/tct.yaml"
216+
[ ! -e "$TMP/executed" ]
217+
yq -o=json '.spec.template.spec.data | from_yaml | .machine.sysctls | sort_keys(.)' "$TMP/tct.yaml" > "$TMP/actual.json"
218+
diff -u "$TMP/expected.json" "$TMP/actual.json"
219+
[ "$(yq '.spec.template.spec.data | from_yaml | .machine.token' "$TMP/tct.yaml")" = "runtime-token" ]
220+
done
221+
rm -rf "$TMP"
222+
}
223+
224+
@test "sysctl types are rejected by both the schema and the render-time guard" {
225+
TMP=$(mktemp -d)
226+
for value in '11255' 'false' 'null' '["1"]' '{"evil":"$(id)"}'; do
227+
input="talos.sysctls={\"kernel.panic\":$value}"
228+
if render_pool "$TMP/r.yaml" --set-json "$input"; then
229+
echo "FAIL: schema accepted a non-string sysctl: $value"
230+
false
231+
fi
232+
grep -q 'values don.t meet the specifications' "$TMP/r.yaml.err"
233+
if render_pool "$TMP/r.yaml" --skip-schema-validation --set-json "$input"; then
234+
echo "FAIL: template accepted a non-string sysctl: $value"
235+
false
236+
fi
237+
grep -Fq 'talos.sysctls["kernel.panic"] must be a string' "$TMP/r.yaml.err"
238+
done
239+
for value in '"$(id)"' 'false' '0' '[]' '["1"]'; do
240+
input="talos.sysctls=$value"
241+
if render_pool "$TMP/r.yaml" --set-json "$input"; then
242+
echo "FAIL: schema accepted a non-map sysctls: $value"
243+
false
244+
fi
245+
grep -q 'values don.t meet the specifications' "$TMP/r.yaml.err"
246+
if render_pool "$TMP/r.yaml" --skip-schema-validation --set-json "$input"; then
247+
echo "FAIL: template accepted a non-map sysctls: $value"
248+
false
249+
fi
250+
grep -Fq 'talos.sysctls must be a map of strings' "$TMP/r.yaml.err"
251+
done
252+
rm -rf "$TMP"
253+
}

0 commit comments

Comments
 (0)