Kube-Proxy Metrics Service Bound To Localhost
More Info:
Binding the kube-proxy metrics service to localhost prevents the metrics endpoint from being reachable across the network. This limits exposure of operational data to the local node only.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify how kube-proxy is started (systemd vs. manifest)
- Run on: every worker node
/bin/ps -fC kube-proxy- From the output, note whether kube-proxy is started via:
- a systemd unit referencing a config file (e.g.
/etc/kubernetes/proxy.conf), or - a static pod manifest (e.g.
/etc/kubernetes/manifests/kube-proxy.yaml).
- a systemd unit referencing a config file (e.g.
- The remaining steps assume the config file
/etc/kubernetes/proxy.confis in use; if not, adjust to the actual kube-proxy config file or manifest used on that node.
-
Edit the kube-proxy configuration to bind metrics to localhost
- Run on: every worker node
- Open the config file:
sudo vi /etc/kubernetes/proxy.conf
- In the
metricsBindAddressfield underkubeProxyConfiguration, set it to localhost if it is set to another address, or add it if missing:apiVersion: kubeproxy.config.k8s.io/v1alpha1kind: KubeProxyConfiguration...metricsBindAddress: "127.0.0.1:10249" - Remove any non-localhost setting such as
0.0.0.0:10249or a specific node IP.
-
If kube-proxy is launched via command-line flags, ensure flags do not override localhost binding
- Run on: every worker node
- Check the kube-proxy process command for a
--metrics-bind-addressflag:/bin/ps -fC kube-proxy | sed -n '2p' - If you see a flag binding to a non-localhost address (for example
--metrics-bind-address=0.0.0.0:10249), edit the systemd unit or launch script shown in the command line (for example/etc/systemd/system/kube-proxy.service), and either:- change it to:
or--metrics-bind-address=127.0.0.1:10249
- remove the flag entirely so the default
127.0.0.1:10249from the config file applies.
- change it to:
- After editing a systemd unit file, reload systemd:
sudo systemctl daemon-reload
-
Restart kube-proxy to apply changes
- Run on: every worker node
- If kube-proxy is managed by systemd:
sudo systemctl restart kube-proxy
- If kube-proxy runs as a static pod from a manifest, saving the edited manifest or config file usually causes the kubelet to automatically restart the pod. No extra command is needed, but be aware that editing a manifest used by kubelet will restart kube-proxy on that node.
-
Verify that metrics are now bound to localhost
- Run on: every worker node
/bin/ps -fC kube-proxy- Confirm in the command line or implied configuration that:
--metrics-bind-addressis either absent or set to127.0.0.1:10249, and- there is no binding of the metrics service to a non-localhost address.
Using kubectl
kubectl cannot modify kube-proxy’s host-level configuration or the /etc/kubernetes/proxy.conf file where the metrics binding is set; this must be fixed directly on every worker node. See the Manual Steps section for how to edit the configuration and restart kube-proxy safely.
Automation
#!/usr/bin/env bash
#
# Remediate CISKubernetes 4.3.1:
# Ensure that the kube-proxy metrics service is bound to localhost (127.0.0.1:10249)
#
# Run this script on every worker node as root.
# It is safe to re-run (idempotent).
#
# Assumptions:
# - kube-proxy is started with: kube-proxy --config=/etc/kubernetes/proxy.conf
# - /etc/kubernetes/proxy.conf is a kube-proxy config file (YAML or JSON)
#
# Operational impact:
# - This script only edits /etc/kubernetes/proxy.conf.
# - kube-proxy must be restarted (by your node/cluster management tooling) to pick up changes.
set -euo pipefail
PROXY_CONF="/etc/kubernetes/proxy.conf"
BACKUP_SUFFIX="$(date +%Y%m%d%H%M%S)"
DESIRED_METRICS_BIND_ADDRESS="127.0.0.1:10249"
log() {
printf '[%s] %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*" >&2
}
require_root() {
if [[ "$(id -u)" -ne 0 ]]; then
log "ERROR: must be run as root."
exit 1
fi
}
check_file_exists() {
if [[ ! -f "$PROXY_CONF" ]]; then
log "ERROR: $PROXY_CONF not found. This script cannot proceed on this node."
exit 1
fi
}
backup_file() {
local src="$1"
local dst="${src}.bak.${BACKUP_SUFFIX}"
cp -p "$src" "$dst"
log "Backup created: $dst"
}
current_setting() {
# Extract any explicit metricsBindAddress setting (basic grep, works for YAML/JSON)
# Returns first match or empty string.
local val
val="$(grep -E 'metricsBindAddress' "$PROXY_CONF" | head -n 1 || true)"
if [[ -z "$val" ]]; then
printf '\n'
return 0
fi
# Try to strip key and whitespace, keep value portion
val="${val#*metricsBindAddress}"
val="${val#*:}"
val="${val//\"/}"
val="${val//\'/}"
val="$(echo "$val" | xargs || true)"
printf '%s\n' "$val"
}
ensure_metrics_bind_address() {
local current
current="$(current_setting)"
if [[ -z "$current" ]]; then
log "No explicit metricsBindAddress found; adding ${DESIRED_METRICS_BIND_ADDRESS}."
backup_file "$PROXY_CONF"
# Heuristic: if file looks like JSON (starts with {), add JSON field; else treat as YAML.
if head -n1 "$PROXY_CONF" | grep -q '^{'; then
# JSON: insert/overwrite using jq if available, otherwise append a comment for manual follow-up
if command -v jq >/dev/null 2>&1; then
tmp="$(mktemp)"
jq --arg v "$DESIRED_METRICS_BIND_ADDRESS" '.metricsBindAddress = $v' "$PROXY_CONF" > "$tmp"
mv "$tmp" "$PROXY_CONF"
log "Updated JSON metricsBindAddress to ${DESIRED_METRICS_BIND_ADDRESS}."
else
log "WARNING: jq not installed; cannot safely edit JSON. Please set metricsBindAddress manually to ${DESIRED_METRICS_BIND_ADDRESS} in $PROXY_CONF."
fi
else
# YAML: append at end
cat <<EOF >>"$PROXY_CONF"
# Enforced by CIS Kubernetes 4.3.1 hardening script
metricsBindAddress: ${DESIRED_METRICS_BIND_ADDRESS}
EOF
log "Appended YAML metricsBindAddress: ${DESIRED_METRICS_BIND_ADDRESS}."
fi
elif [[ "$current" == "$DESIRED_METRICS_BIND_ADDRESS" ]]; then
log "metricsBindAddress already set to ${DESIRED_METRICS_BIND_ADDRESS}; no change needed."
else
log "metricsBindAddress currently set to '${current}', updating to ${DESIRED_METRICS_BIND_ADDRESS}."
backup_file "$PROXY_CONF"
# Replace existing line containing metricsBindAddress (YAML/JSON) with desired value
# This is a simple in-place edit, safe for typical kube-proxy configs.
sed -i.bak.tmp \
-E "s/^( *\"?metricsBindAddress\"?[ :]+).*/\1${DESIRED_METRICS_BIND_ADDRESS}/" \
"$PROXY_CONF"
rm -f "${PROXY_CONF}.bak.tmp"
log "Updated metricsBindAddress to ${DESIRED_METRICS_BIND_ADDRESS}."
fi
}
verify_result() {
log "Verification: checking /etc/kubernetes/proxy.conf for metricsBindAddress=${DESIRED_METRICS_BIND_ADDRESS}"
local after
after="$(current_setting)"
if [[ "$after" == "$DESIRED_METRICS_BIND_ADDRESS" ]]; then
log "OK: metricsBindAddress is set correctly in $PROXY_CONF."
else
log "WARNING: metricsBindAddress not confirmed as ${DESIRED_METRICS_BIND_ADDRESS}."
log "Current parsed value: '${after}' (empty means not found)."
fi
log "Verification: checking running kube-proxy process flags (may require restart to match config)."
if /bin/ps -fC kube-proxy >/dev/null 2>&1; then
/bin/ps -fC kube-proxy || true
else
log "NOTE: kube-proxy process not detected by 'ps -fC kube-proxy'. It may not be running or may use a different process name."
fi
log "If kube-proxy is running with a config file, restart it to apply the updated metricsBindAddress."
}
main() {
require_root
check_file_exists
ensure_metrics_bind_address
verify_result
}
main "$@"