Ensure Read Only Port Argument Is Set 0
More Info:
Disable the read-only port.
Risk Level
Medium
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS Critical Security Controls v8
- CIS GKE
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIS2 Directive
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
On every worker node, check how kubelet is configured (config file vs flags):
ps -fC kubeletIf you see
--config=/var/lib/kubelet/config.yaml, it is using the config file; if you see--read-only-port=or no--config, it is using flags. -
If kubelet uses the config file
/var/lib/kubelet/config.yaml, edit it on every worker node:sudo sed -i 's/^[[:space:]]*readOnlyPort:.*$/readOnlyPort: 0/' /var/lib/kubelet/config.yamlIf there is no
readOnlyPortline, add one under the top-level# kubeletConfigurationblock, for example:sudo sed -i '/^kind: KubeletConfiguration/a readOnlyPort: 0' /var/lib/kubelet/config.yaml -
If kubelet uses executable arguments, edit the systemd drop-in on every worker node:
sudo sed -i 's/\b--read-only-port=[0-9]*\b//g' /etc/systemd/system/kubelet.service.d/10-kubelet-args.confsudo sed -i 's/^KUBELET_ARGS="/KUBELET_ARGS="--read-only-port=0 /' /etc/systemd/system/kubelet.service.d/10-kubelet-args.confIf there is no
KUBELET_ARGSline, append one:echo 'KUBELET_ARGS="--read-only-port=0"' | sudo tee -a /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf -
Reload systemd and restart kubelet on every worker node:
sudo systemctl daemon-reloadsudo systemctl restart kubelet.servicesudo systemctl status kubelet -l -
Verify on every worker node that kubelet is running with
readOnlyPortdisabled:ps -fC kubeletConfirm either that:
- the process is started with
--config=/var/lib/kubelet/config.yamland that file containsreadOnlyPort: 0, and - there is no
--read-only-portflag with a non-zero value in thekubeletcommand line.
- the process is started with
Using kubectl
kubectl cannot modify kubelet process flags or its host-level configuration file at /var/lib/kubelet/config.yaml; those must be changed directly on every worker node (e.g., in the kubelet config and/or systemd unit files). See the Manual Steps section for the exact on-node configuration changes and restart commands required to remediate this finding.
Automation
#!/usr/bin/env bash
#
# Harden kubelet read-only port on every worker node.
# - Sets readOnlyPort: 0 in kubelet config (YAML or JSON)
# - Adds --read-only-port=0 to systemd args if used
# - Restarts kubelet if changes were made
#
# Run this script on each worker node as root.
set -euo pipefail
KUBELET_CONFIG_YAML="/var/lib/kubelet/config.yaml"
KUBELET_CONFIG_JSON="/etc/kubernetes/kubelet/kubelet-config.json"
KUBELET_ARGS_DROPIN="/etc/systemd/system/kubelet.service.d/10-kubelet-args.conf"
changed=0
echo "[*] Ensuring kubelet read-only port is disabled (readOnlyPort / --read-only-port=0)"
########################################
# Helper: restart kubelet if needed
########################################
restart_kubelet_if_needed() {
if [ "$changed" -eq 1 ]; then
echo "[*] Changes detected, reloading systemd and restarting kubelet..."
systemctl daemon-reload
systemctl restart kubelet.service
else
echo "[*] No changes made, kubelet restart not required."
fi
echo "[*] Kubelet status:"
systemctl status kubelet -l --no-pager || true
}
########################################
# 1. Update kubelet YAML config, if present
########################################
if [ -f "$KUBELET_CONFIG_YAML" ]; then
echo "[*] Found kubelet YAML config at $KUBELET_CONFIG_YAML"
# Check current readOnlyPort (if set)
current_val="$(grep -E '^[[:space:]]*readOnlyPort:' "$KUBELET_CONFIG_YAML" 2>/dev/null | awk '{print $2}' || true)"
if [ -n "$current_val" ] && [ "$current_val" -eq 0 ] 2>/dev/null; then
echo " - readOnlyPort is already set to 0 in YAML; no update needed."
else
echo " - Setting readOnlyPort: 0 in YAML config (idempotent edit)."
# If key exists, replace; else add under top-level (simple append)
if grep -qE '^[[:space:]]*readOnlyPort:' "$KUBELET_CONFIG_YAML"; then
sed -i -E 's/^[[:space:]]*readOnlyPort:.*/readOnlyPort: 0/' "$KUBELET_CONFIG_YAML"
else
# Append at end; for most kubelet config layouts this is acceptable
printf "\nreadOnlyPort: 0\n" >> "$KUBELET_CONFIG_YAML"
fi
changed=1
fi
else
echo "[*] No kubelet YAML config found at $KUBELET_CONFIG_YAML (this may be normal for your setup)."
fi
########################################
# 2. Update kubelet JSON config, if present
########################################
if [ -f "$KUBELET_CONFIG_JSON" ]; then
echo "[*] Found kubelet JSON config at $KUBELET_CONFIG_JSON"
# Try to read current JSON value using jq if available, else do a simple grep
current_json=""
if command -v jq >/dev/null 2>&1; then
current_json="$(jq -r '.readOnlyPort // empty' "$KUBELET_CONFIG_JSON" 2>/dev/null || true)"
else
# Fallback: naive grep/awk; expects "readOnlyPort": <num>
current_json="$(grep -E '"readOnlyPort"' "$KUBELET_CONFIG_JSON" 2>/dev/null | sed -E 's/.*"readOnlyPort"[[:space:]]*:[[:space:]]*([0-9]+).*/\1/' || true)"
fi
if [ -n "$current_json" ] && [ "$current_json" -eq 0 ] 2>/dev/null; then
echo " - readOnlyPort is already set to 0 in JSON; no update needed."
else
echo " - Setting \"readOnlyPort\": 0 in JSON config (idempotent edit)."
if command -v jq >/dev/null 2>&1; then
# Use jq to ensure well-formed JSON
tmp_file="$(mktemp)"
jq '.readOnlyPort = 0' "$KUBELET_CONFIG_JSON" > "$tmp_file"
mv "$tmp_file" "$KUBELET_CONFIG_JSON"
else
# Fallback: simple replace/add; assumes reasonably formatted JSON
if grep -q '"readOnlyPort"' "$KUBELET_CONFIG_JSON"; then
sed -i -E 's/"readOnlyPort"[[:space:]]*:[[:space:]]*[0-9]+/"readOnlyPort": 0/' "$KUBELET_CONFIG_JSON"
else
# Insert before last closing brace
sed -i -E 's/}\s*$/ "readOnlyPort": 0\n}/' "$KUBELET_CONFIG_JSON"
fi
fi
changed=1
fi
else
echo "[*] No kubelet JSON config found at $KUBELET_CONFIG_JSON (this may be normal for your setup)."
fi
########################################
# 3. Update systemd kubelet args, if present
########################################
if [ -f "$KUBELET_ARGS_DROPIN" ]; then
echo "[*] Found kubelet systemd drop-in at $KUBELET_ARGS_DROPIN"
if grep -Eq -- '--read-only-port=0(\s|$|")' "$KUBELET_ARGS_DROPIN"; then
echo " - --read-only-port=0 already present in kubelet args; no update needed."
else
echo " - Adding --read-only-port=0 to kubelet args (idempotent append)."
# Append to existing KUBELET_ARGS or ExecStart flags
if grep -q 'KUBELET_ARGS' "$KUBELET_ARGS_DROPIN"; then
# Append to KUBELET_ARGS environment variable
sed -i -E 's/^(Environment=.*KUBELET_ARGS="[^"]*)"/\1 --read-only-port=0"/' "$KUBELET_ARGS_DROPIN" \
|| echo 'Environment="KUBELET_ARGS=--read-only-port=0"' >> "$KUBELET_ARGS_DROPIN"
elif grep -q 'ExecStart=' "$KUBELET_ARGS_DROPIN"; then
sed -i -E 's#^(ExecStart=.*kubelet.*)$#\1 --read-only-port=0#' "$KUBELET_ARGS_DROPIN"
else
# Fallback: just append an Environment line
echo 'Environment="KUBELET_ARGS=--read-only-port=0"' >> "$KUBELET_ARGS_DROPIN"
fi
changed=1
fi
else
echo "[*] No kubelet systemd args drop-in found at $KUBELET_ARGS_DROPIN (this may be normal for your setup)."
fi
########################################
# 4. Restart kubelet if anything changed
########################################
restart_kubelet_if_needed
########################################
# 5. Verification (same basis as audit)
########################################
echo
echo "[*] Verification: kubelet process flags and config"
# Show kubelet process with full command-line
/bin/ps -fC kubelet || true
# Verify read-only port set to 0 in YAML (if file exists)
if [ -f "$KUBELET_CONFIG_YAML" ]; then
echo
echo "[*] readOnlyPort in $KUBELET_CONFIG_YAML:"
grep -E '^[[:space:]]*readOnlyPort:' "$KUBELET_CONFIG_YAML" || echo " (no explicit readOnlyPort found)"
fi
# Verify read-only port set to 0 in JSON (if file exists)
if [ -f "$KUBELET_CONFIG_JSON" ]; then
echo
echo "[*] readOnlyPort in $KUBELET_CONFIG_JSON:"
if command -v jq >/dev/null 2>&1; then
jq '.readOnlyPort' "$KUBELET_CONFIG_JSON" || true
else
grep '"readOnlyPort"' "$KUBELET_CONFIG_JSON" || echo ' (no explicit "readOnlyPort" found)'
fi
fi
echo
echo "[*] Ensure that kubelet either has readOnlyPort: 0 in its config or is started with --read-only-port=0."