Skip to main content

Container Network Interface File Ownership Should Be

More Info:

Verifies that Container Network Interface configuration files are owned by root:root so only privileged users can modify pod networking configuration.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, identify the CNI config directory used by kubelet (if not already known):

    ps -ef | grep kubelet | grep -- --cni-conf-dir
  2. On every control plane node, list current ownership of CNI configuration files to see which need fixing (use the directory from step 1 if different):

    sudo find /etc/cni/net.d -mindepth 1 -maxdepth 1 -type f -exec stat -c '%n %U:%G' {} \;
  3. On every control plane node, change ownership of all CNI configuration files to root:root:

    sudo chown root:root /etc/cni/net.d/*
  4. On every control plane node, also ensure ownership of any CNI network state files under /var/lib/cni/networks is root:root (if the directory exists):

    if [ -d /var/lib/cni/networks ]; then
    sudo find /var/lib/cni/networks -type f -exec chown root:root {} +
    fi
  5. On every control plane node, verify CNI configuration files now have owner and group root:root:

    ps -ef | grep kubelet | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | \
    xargs -I{} find {} -mindepth 1 | xargs --no-run-if-empty stat -c '%n %U:%G'
  6. On every control plane node, verify CNI network state files (if present) are also owned by root:root:

    find /var/lib/cni/networks -type f 2> /dev/null | xargs --no-run-if-empty stat -c '%n %U:%G'
Using kubectl

kubectl cannot modify Container Network Interface file ownership because this setting is controlled by host-level filesystem permissions on each control plane node. To remediate this finding, log into every control plane node and follow the commands in the Manual Steps section to set the CNI configuration files to root:root.

Automation
#!/usr/bin/env bash
#
# Fix CNI config file ownership to root:root on every control plane node
# Scope: run on every control plane node (as root or with sudo)
# Safe to re-run (idempotent)

set -euo pipefail

echo "=== Detecting CNI configuration directory from kubelet flags (if present) ==="

CNI_CONF_DIRS=()

# Try to detect from running kubelet process
if pgrep -x kubelet >/dev/null 2>&1; then
# Extract --cni-conf-dir argument if present
DETECTED_DIRS=$(ps -ef | grep kubelet | grep -v grep \
| grep -- --cni-conf-dir \
| sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' || true)

if [ -n "${DETECTED_DIRS}" ]; then
while IFS= read -r d; do
[ -n "$d" ] && CNI_CONF_DIRS+=("$d")
done <<< "${DETECTED_DIRS}"
fi
fi

# Fallback to common default if none detected
DEFAULT_DIRS=(
"/etc/cni/net.d"
)
for d in "${DEFAULT_DIRS[@]}"; do
CNI_CONF_DIRS+=("$d")
done

# De-duplicate directories
uniq_dirs=()
for d in "${CNI_CONF_DIRS[@]}"; do
skip=false
for u in "${uniq_dirs[@]}"; do
if [ "$u" = "$d" ]; then
skip=true
break
fi
done
$skip || uniq_dirs+=("$d")
done
CNI_CONF_DIRS=("${uniq_dirs[@]}")

echo "CNI configuration directories to process:"
for d in "${CNI_CONF_DIRS[@]}"; do
echo " - $d"
done

echo
echo "=== Fixing ownership to root:root where directories exist ==="

for dir in "${CNI_CONF_DIRS[@]}"; do
if [ -d "$dir" ]; then
echo "Processing directory: $dir"
# Change ownership of files and subdirectories to root:root (idempotent)
chown -R root:root "$dir"
else
echo "Directory not present, skipping: $dir"
fi
done

# Also fix ownership under /var/lib/cni/networks if present (used by audit)
if [ -d "/var/lib/cni/networks" ]; then
echo "Processing directory: /var/lib/cni/networks"
chown -R root:root /var/lib/cni/networks
else
echo "Directory not present, skipping: /var/lib/cni/networks"
fi

echo
echo "=== Verification (should report only root:root) ==="

# Replicate the audit logic to confirm ownership
# 1) CNI config files
if pgrep -x kubelet >/dev/null 2>&1; then
ps -ef | grep kubelet | grep -v grep | grep -- --cni-conf-dir \
| sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' \
| xargs -I{} find {} -mindepth 1 2>/dev/null \
| xargs --no-run-if-empty stat -c '%n %U:%G' \
| sort -u || true
fi

# Fallback verification for default /etc/cni/net.d
if [ -d "/etc/cni/net.d" ]; then
find /etc/cni/net.d -mindepth 1 -maxdepth 1 -type f 2>/dev/null \
| xargs --no-run-if-empty stat -c '%n %U:%G' \
| sort -u || true
fi

# 2) /var/lib/cni/networks files
if [ -d "/var/lib/cni/networks" ]; then
find /var/lib/cni/networks -type f 2>/dev/null \
| xargs --no-run-if-empty stat -c '%n %U:%G' \
| sort -u || true
fi

echo
echo "If any entries above are not 'root:root', investigate and correct them manually."