Skip to main content

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​

Manual Steps
  1. On every worker node, check how kubelet is configured (config file vs flags):

    ps -fC kubelet

    If 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.

  2. 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.yaml

    If there is no readOnlyPort line, add one under the top-level # kubeletConfiguration block, for example:

    sudo sed -i '/^kind: KubeletConfiguration/a readOnlyPort: 0' /var/lib/kubelet/config.yaml
  3. 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.conf
    sudo sed -i 's/^KUBELET_ARGS="/KUBELET_ARGS="--read-only-port=0 /' /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf

    If there is no KUBELET_ARGS line, append one:

    echo 'KUBELET_ARGS="--read-only-port=0"' | sudo tee -a /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf
  4. Reload systemd and restart kubelet on every worker node:

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet.service
    sudo systemctl status kubelet -l
  5. Verify on every worker node that kubelet is running with readOnlyPort disabled:

    ps -fC kubelet

    Confirm either that:

    • the process is started with --config=/var/lib/kubelet/config.yaml and that file contains readOnlyPort: 0, and
    • there is no --read-only-port flag with a non-zero value in the kubelet command line.
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."

Additional Reading:​