Container Network Interface File Permissions Are Restrictive
More Info:
Ensure that the Container Network Interface files have permissions of 644 or more restrictive.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
On every control plane node, identify the directory where CNI configuration files are stored (from the kubelet flag, if present):
ps -ef | grep kubelet | grep -- --cni-conf-dir | sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%'If this prints nothing, use the default
/etc/cni/net.d(or your known CNI conf directory). -
On every control plane node, set permissions on all CNI configuration files in the CNI conf directory to
644:CNI_CONF_DIR=/etc/cni/net.dfind "$CNI_CONF_DIR" -type f -exec chmod 644 {} \; -
On every control plane node, set permissions on all CNI state/network files under
/var/lib/cni/networksto644:find /var/lib/cni/networks -type f 2>/dev/null -exec chmod 644 {} \; -
(Optional hardening) On every control plane node, restrict directory execute/search permissions so only root can traverse:
chmod 755 /etc/cni /etc/cni/net.d 2>/dev/null || truechmod 755 /var/lib/cni /var/lib/cni/networks 2>/dev/null || true -
Verification on every control plane node:
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 permissions=%afind /var/lib/cni/networks -type f 2>/dev/null | xargs --no-run-if-empty stat -c permissions=%aConfirm that all reported permissions are
644or a more restrictive value (e.g.,640,600).
Using kubectl
kubectl cannot modify file permissions on the node filesystem, including /var/lib/cni/networks or any CNI configuration directories. To remediate this finding, you must change permissions directly on every control plane node’s host OS; see the Manual Steps section for the required SSH-based commands.
Automation
#!/usr/bin/env bash
#
# Purpose: Ensure Container Network Interface files have permissions 644 or more restrictive.
# Scope: Run on every control plane node.
# Impact: None on running processes; only file mode changes under CNI dirs.
set -euo pipefail
# Directories to check/fix
CNI_CONF_DIRS=()
CNI_NET_DIRS=("/var/lib/cni/networks")
echo "=== Detecting CNI configuration directories from kubelet arguments (if any) ==="
if pgrep -x kubelet >/dev/null 2>&1; then
# Extract any --cni-conf-dir values from kubelet command line
while read -r dir; do
[ -n "$dir" ] && CNI_CONF_DIRS+=("$dir")
done < <(ps -ef | grep kubelet | grep -- '--cni-conf-dir' | \
sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | sort -u)
else
echo "kubelet process not found; skipping kubelet-based CNI config detection."
fi
# De-duplicate all directories
uniq_dirs() {
awk '!x[$0]++'
}
ALL_DIRS=()
for d in "${CNI_CONF_DIRS[@]}" "${CNI_NET_DIRS[@]}"; do
[ -n "$d" ] && ALL_DIRS+=("$d")
done
# Print unique, existing directories
UNIQ_EXISTING_DIRS=()
printf '%s\n' "${ALL_DIRS[@]}" 2>/dev/null | uniq_dirs | while read -r d; do
if [ -d "$d" ]; then
UNIQ_EXISTING_DIRS+=("$d")
else
echo "Note: directory '$d' does not exist; skipping."
fi
done
# Because arrays aren’t preserved across the while subshell, rebuild from stdout
mapfile -t TARGET_DIRS < <(printf '%s\n' "${ALL_DIRS[@]}" 2>/dev/null | uniq_dirs | while read -r d; do [ -d "$d" ] && echo "$d"; done)
echo "=== Directories to process ==="
if [ "${#TARGET_DIRS[@]}" -eq 0 ]; then
echo "No existing CNI directories found; nothing to change."
exit 0
fi
printf ' %s\n' "${TARGET_DIRS[@]}"
echo "=== Fixing permissions to 0644 where more permissive ==="
for dir in "${TARGET_DIRS[@]}"; do
echo "Processing: $dir"
# Find files with permissions more permissive than 644 (i.e., world or group write/execute)
# and set them to 644. This is idempotent: files already at 644 or stricter remain unchanged.
find "$dir" -type f 2>/dev/null | while read -r f; do
# Get current numeric permissions
perm=$(stat -c '%a' "$f" 2>/dev/null || echo "")
[ -z "$perm" ] && continue
# Normalize to three digits if shorter
perm=$(printf '%03d' "$perm")
# Extract owner/group/other bits
o=${perm:0:1}
g=${perm:1:1}
w=${perm:2:1}
# Determine if more permissive than 644 (i.e. any write/exec beyond 644)
# 644 = owner:6 (rw-), group:4 (r--), other:4 (r--)
change=false
# If owner has exec (1) or setuid bits we leave; we only tighten group/other writes/exec.
# If group >4 or other >4 then it's more permissive.
if [ "$g" -gt 4 ] || [ "$w" -gt 4 ]; then
change=true
fi
if $change; then
chmod 644 "$f"
fi
done
done
echo "=== Verification: listing permissions for CNI files ==="
echo "--- From kubelet --cni-conf-dir (if present) ---"
ps -ef | grep kubelet | grep -- '--cni-conf-dir' | \
sed 's%.*cni-conf-dir[= ]\([^ ]*\).*%\1%' | \
xargs -I{} find {} -mindepth 1 -type f 2>/dev/null | \
xargs --no-run-if-empty stat -c '%n permissions=%a' || true
echo "--- From /var/lib/cni/networks ---"
find /var/lib/cni/networks -type f 2>/dev/null | \
xargs --no-run-if-empty stat -c '%n permissions=%a' || true
echo "=== Completed. Re-run this script at any time; it is idempotent. ==="