Skip to main content

Ensure Make Iptables Util Chains Argument Is Enabled

More Info:

Allow Kubelet to manage iptables.

Risk Level

High

Address

Security

Compliance Standards

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS Critical Security Controls v8
  • CIS EKS
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • 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 and whether the flag is already set:
ps -fC kubelet
cat /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf 2>/dev/null || echo "no 10-kubelet-args.conf"
grep -i makeiptablesutilchains /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf 2>/dev/null || echo "flag not in systemd args"
grep -i makeIPTablesUtilChains /etc/kubernetes/kubelet/kubelet-config.json 2>/dev/null || echo "field not in kubelet-config.json"
  1. If using the kubelet config file (preferred), edit /etc/kubernetes/kubelet/kubelet-config.json on every worker node to set makeIPTablesUtilChains to true (create or update the field in the top-level config object):
vi /etc/kubernetes/kubelet/kubelet-config.json

Add or change the line so it reads (with a trailing comma if required by surrounding JSON):

"makeIPTablesUtilChains": true
  1. Ensure the systemd drop-in does not override this flag; on every worker node, remove any --make-iptables-util-chains argument from the kubelet args file:
sed -i.bak '/--make-iptables-util-chains/d' /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf
  1. If instead you are using only executable arguments (no kubelet config file), edit the kubelet args file on every worker node to explicitly set the flag:
vi /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf

In the line defining KUBELET_ARGS=... (or equivalent), ensure it includes:

--make-iptables-util-chains=true

and save the file.

  1. On every worker node, reload systemd and restart kubelet so the change takes effect (this will restart the kubelet process):
systemctl daemon-reload
systemctl restart kubelet.service
systemctl status kubelet -l
  1. Verify on every worker node that kubelet is now running with --make-iptables-util-chains=true or that the config file contains "makeIPTablesUtilChains": true:
ps -fC kubelet
grep -i makeIPTablesUtilChains /etc/kubernetes/kubelet/kubelet-config.json 2>/dev/null || echo "not using kubelet config file"
grep -i -- '--make-iptables-util-chains' /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf 2>/dev/null || echo "flag not set in systemd args"
Using kubectl

kubectl cannot change the kubelet’s --make-iptables-util-chains setting because it is a host-level configuration controlled by files and systemd units on every worker node (for example /etc/kubernetes/kubelet/kubelet-config.json or /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf). To remediate this finding, follow the guidance in the Manual Steps section on each worker node.

Automation
#!/usr/bin/env bash
#
# Fix CISEKS 3.2.7: Ensure Kubelet --make-iptables-util-chains is enabled
# Scope: every worker node
#
# Usage:
# - Copy to each worker node and run as root:
# chmod +x fix-make-iptables-util-chains.sh
# sudo ./fix-make-iptables-util-chains.sh
#
# Idempotent: safe to re-run.

set -euo pipefail

KUBELET_SYSTEMD_DIR="/etc/systemd/system/kubelet.service.d"
KUBELET_ARGS_DROPIN="${KUBELET_SYSTEMD_DIR}/10-kubelet-args.conf"
KUBELET_CONFIG_JSON="/etc/kubernetes/kubelet/kubelet-config.json"

backup_file() {
local f="$1"
if [ -f "$f" ]; then
local ts
ts="$(date +%Y%m%d%H%M%S)"
cp -p "$f" "${f}.bak-${ts}"
fi
}

ensure_dir() {
local d="$1"
if [ ! -d "$d" ]; then
mkdir -p "$d"
fi
}

update_kubelet_config_json() {
if [ ! -f "$KUBELET_CONFIG_JSON" ]; then
# If this node doesn't use kubelet-config.json, skip this method.
return
fi

backup_file "$KUBELET_CONFIG_JSON"

# If file is empty, initialize minimal JSON
if [ ! -s "$KUBELET_CONFIG_JSON" ]; then
echo '{}' > "$KUBELET_CONFIG_JSON"
fi

# Use jq if available for safe JSON editing
if command -v jq >/dev/null 2>&1; then
local tmp
tmp="$(mktemp)"
jq '.makeIPTablesUtilChains = true' "$KUBELET_CONFIG_JSON" > "$tmp"
mv "$tmp" "$KUBELET_CONFIG_JSON"
else
# Fallback: simple text-based update (assumes file is valid JSON already)
# 1) Remove existing makeIPTablesUtilChains entries
sed -i '/"makeIPTablesUtilChains"/d' "$KUBELET_CONFIG_JSON"
# 2) Insert at end of top-level object before closing }
# Handles both single-line and multi-line JSON objects.
if grep -q '}' "$KUBELET_CONFIG_JSON"; then
# Ensure trailing comma before insertion if needed
# Add comma to last non-empty, non-{, non-[ line before final }
awk '
BEGIN{last_nonbrace_line=0}
NF{
if ($0 !~ /^[[:space:]]*[\{\[]/ && $0 !~ /^[[:space:]]*}$/) last_nonbrace_line=NR
}
{lines[NR]=$0}
END{
for(i=1;i<=NR;i++){
if(i==last_nonbrace_line && lines[i] !~ /, *$/) {
sub(/[[:space:]]*$/,"",lines[i])
lines[i]=lines[i] ","
}
print lines[i]
}
}
' "$KUBELET_CONFIG_JSON" > "${KUBELET_CONFIG_JSON}.tmp"
mv "${KUBELET_CONFIG_JSON}.tmp" "$KUBELET_CONFIG_JSON"
# Insert our field before the final }
sed -i '$i \ "makeIPTablesUtilChains": true' "$KUBELET_CONFIG_JSON"
else
# Worst case: just force a minimal object
echo '{ "makeIPTablesUtilChains": true }' > "$KUBELET_CONFIG_JSON"
fi
fi
}

clean_systemd_arg_override() {
# Remove any explicit --make-iptables-util-chains from drop-in so config file wins
if [ -f "$KUBELET_ARGS_DROPIN" ]; then
backup_file "$KUBELET_ARGS_DROPIN"
# Remove argument occurrences from the KUBELET_ARGS line
sed -i 's/--make-iptables-util-chains[=:][^[:space:]]*//g' "$KUBELET_ARGS_DROPIN"
# Collapse double spaces
sed -i 's/ \+/ /g' "$KUBELET_ARGS_DROPIN"
fi
}

ensure_systemd_arg_if_no_config() {
# If kubelet-config.json does not exist, enforce via systemd args
if [ -f "$KUBELET_CONFIG_JSON" ]; then
return
fi

ensure_dir "$KUBELET_SYSTEMD_DIR"
[ -f "$KUBELET_ARGS_DROPIN" ] || touch "$KUBELET_ARGS_DROPIN"

backup_file "$KUBELET_ARGS_DROPIN"

# Ensure we do not duplicate the argument
if grep -q -- '--make-iptables-util-chains' "$KUBELET_ARGS_DROPIN"; then
# Normalize it to true
sed -i 's/--make-iptables-util-chains[=:][^[:space:]]*/--make-iptables-util-chains:true/g' "$KUBELET_ARGS_DROPIN"
else
if grep -q '^KUBELET_ARGS=' "$KUBELET_ARGS_DROPIN"; then
# Append to existing KUBELET_ARGS
sed -i 's/^KUBELET_ARGS="\([^"]*\)"/KUBELET_ARGS="\1 --make-iptables-util-chains:true"/' "$KUBELET_ARGS_DROPIN"
else
# Create new KUBELET_ARGS line
echo 'KUBELET_ARGS="--make-iptables-util-chains:true"' >> "$KUBELET_ARGS_DROPIN"
fi
fi
}

restart_kubelet() {
systemctl daemon-reload
systemctl restart kubelet.service
systemctl status kubelet -l --no-pager || true
}

verify() {
echo "Verification: checking kubelet process args and live config (if available)..."
if command -v ps >/dev/null 2>&1; then
/bin/ps -fC kubelet || true
fi

# Try configz if kubectl is configured on this node (optional)
if command -v kubectl >/dev/null 2>&1; then
if kubectl cluster-info >/dev/null 2>&1; then
kubectl proxy --port=8001 >/tmp/kubectl-proxy.log 2>&1 &
PROXY_PID=$!
sleep 3
NODE_NAME="$(hostname -f 2>/dev/null || hostname)"
echo "Attempting configz check via API for node: ${NODE_NAME}"
curl -sSL "http://localhost:8001/api/v1/nodes/${NODE_NAME}/proxy/configz" | \
grep -i "makeIPTablesUtilChains" || true
kill "${PROXY_PID}" >/dev/null 2>&1 || true
fi
fi
}

main() {
# Prefer Method 1 if kubelet-config.json exists, per remediation guidance
if [ -f "$KUBELET_CONFIG_JSON" ]; then
update_kubelet_config_json
clean_systemd_arg_override
else
# Fall back to Method 2 (systemd args)
ensure_systemd_arg_if_no_config
fi

restart_kubelet
verify
}

main "$@"

Additional Reading: