Skip to main content

Kubelet TLS Cert File And Private Key File Arguments Set As

More Info:

Setting tlsCertFile and tlsPrivateKeyFile provides the kubelet with a proper serving certificate and key for its API. This ensures kubelet connections are secured with trusted TLS material.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, identify the current API server manifest and TLS files:

    sudo ls -l /etc/kubernetes/manifests/kube-apiserver.yaml
    sudo grep -n "tls-cert-file" /etc/kubernetes/manifests/kube-apiserver.yaml || echo "no tls-cert-file set"
    sudo grep -n "tls-private-key-file" /etc/kubernetes/manifests/kube-apiserver.yaml || echo "no tls-private-key-file set"
    sudo ls -l /etc/kubernetes/pki

    Decide which existing certificate/key pair you will use (for example, /etc/kubernetes/pki/apiserver.crt and /etc/kubernetes/pki/apiserver.key), or provision new ones according to your PKI policy.

  2. If you need to generate a new serving certificate and key for the API server on this control plane node, create them (example using openssl; adjust CN/SANs to match your cluster and certificate policy):

    cd /etc/kubernetes/pki
    sudo openssl req -newkey rsa:4096 -nodes -keyout apiserver.key \
    -out apiserver.csr \
    -subj "/CN=kube-apiserver" \
    -addext "subjectAltName=DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.cluster.local,IP:10.96.0.1"

    # Sign with your cluster CA (example: /etc/kubernetes/pki/ca.crt and ca.key)
    sudo openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
    -out apiserver.crt -days 365 -sha256
    sudo chmod 600 apiserver.key
    sudo chmod 644 apiserver.crt
  3. On every control plane node, edit the API server static pod manifest to set the TLS certificate and key flags (this edit will automatically restart the kube-apiserver pod when you save the file):

    sudo cp /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.bak.$(date +%s)
    sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml

    In the command: section of the kube-apiserver container, ensure the following lines exist and point to the chosen files (adjust paths if different):

    - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
    - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key

    Save and exit; kubelet will detect the manifest change and restart the API server pod with the new flags.

  4. If the certificate and key are not yet mounted into the API server container path, ensure the manifest has appropriate volume and volumeMount entries (still on every control plane node editing /etc/kubernetes/manifests/kube-apiserver.yaml):

    volumeMounts:
    - mountPath: /etc/kubernetes/pki
    name: k8s-certs
    readOnly: true
    ...
    volumes:
    - name: k8s-certs
    hostPath:
    path: /etc/kubernetes/pki
    type: DirectoryOrCreate

    Save the file; kubelet will restart the API server pod again if you changed volumes.

  5. After waiting 30–60 seconds for the static pod to restart, verify on each control plane node that the kube-apiserver process is running with the required flags:

    /bin/ps -ef | grep kube-apiserver | grep -v grep

    Confirm the output includes both --tls-cert-file=/etc/kubernetes/pki/apiserver.crt and --tls-private-key-file=/etc/kubernetes/pki/apiserver.key (or your chosen paths) for the running kube-apiserver process.

Using kubectl

kubectl cannot modify kube-apiserver process flags or the static pod manifest on the node. This finding must be fixed by editing /etc/kubernetes/manifests/kube-apiserver.yaml and related TLS files directly on every control plane node; see the Manual Steps section for exact host-level instructions.

Automation
#!/usr/bin/env bash
#
# Automation: Configure kube-apiserver --tls-cert-file and --tls-private-key-file
# Scope: run on every control plane node (with sudo)
#
# This script:
# - Backs up /etc/kubernetes/manifests/kube-apiserver.yaml
# - Ensures --tls-cert-file and --tls-private-key-file arguments are present
# - Uses placeholder paths you must replace with correct certificate/key paths
# - Verifies via the audit command
#
# NOTE: Editing a static pod manifest under /etc/kubernetes/manifests will cause
# the kube-apiserver pod to restart on this node.

set -euo pipefail

APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
BACKUP_DIR="/etc/kubernetes/manifests/backup-$(date +%Y%m%d-%H%M%S)"
# TODO: set these to the correct, existing cert and key paths for your environment
TLS_CERT_PATH="/etc/kubernetes/pki/apiserver.crt"
TLS_KEY_PATH="/etc/kubernetes/pki/apiserver.key"

run_on_control_plane_only() {
if [ ! -f "$APISERVER_MANIFEST" ]; then
echo "This node does not appear to be a control plane node (no $APISERVER_MANIFEST). Skipping."
exit 0
fi
}

require_root() {
if [ "$(id -u)" -ne 0 ]; then
echo "Run this script as root (sudo)."
exit 1
fi
}

validate_paths() {
if [ ! -f "$TLS_CERT_PATH" ]; then
echo "TLS certificate not found at $TLS_CERT_PATH"
echo "Update TLS_CERT_PATH in this script to a valid certificate file before re-running."
exit 1
fi
if [ ! -f "$TLS_KEY_PATH" ]; then
echo "TLS private key not found at $TLS_KEY_PATH"
echo "Update TLS_KEY_PATH in this script to a valid key file before re-running."
exit 1
fi
}

backup_manifest() {
mkdir -p "$BACKUP_DIR"
cp -a "$APISERVER_MANIFEST" "$BACKUP_DIR/kube-apiserver.yaml"
echo "Backed up $APISERVER_MANIFEST to $BACKUP_DIR/kube-apiserver.yaml"
}

ensure_arg() {
local arg_name="$1"
local arg_value="$2"
local file="$3"

# If argument already present, replace its value; if absent, append to command args.
if grep -q -- "$arg_name=" "$file"; then
# Replace existing value (handles both '--arg=val' forms)
sed -i "s#${arg_name}=[^\"'[:space:]]*#${arg_name}=${arg_value}#g" "$file"
else
# Append argument in the container command args. This assumes standard kubeadm-style manifest.
# Insert before the first occurrence of '--advertise-address' if present,
# otherwise just append as an extra - --arg line.
if grep -q -- "--advertise-address" "$file"; then
# Insert line before '--advertise-address'
# Works on typical YAML:
# - --some-arg
# - --advertise-address=...
sed -i "/--advertise-address/ i\ - ${arg_name}=${arg_value}" "$file"
else
# Append near the end of the args list: insert before the closing 'volumeMounts' or 'volumes'
if grep -q "volumeMounts:" "$file"; then
sed -i "/volumeMounts:/ i\ - ${arg_name}=${arg_value}" "$file"
else
# Fallback: append at end of file (still valid for static pod)
printf " - %s=%s\n" "$arg_name" "$arg_value" >> "$file"
fi
fi
fi
}

apply_fix() {
echo "Ensuring --tls-cert-file and --tls-private-key-file are configured in $APISERVER_MANIFEST"

ensure_arg "--tls-cert-file" "$TLS_CERT_PATH" "$APISERVER_MANIFEST"
ensure_arg "--tls-private-key-file" "$TLS_KEY_PATH" "$APISERVER_MANIFEST"

echo "Changes applied. kubelet will restart the kube-apiserver static pod automatically."
}

verify() {
echo "Waiting for kube-apiserver process to reflect new arguments..."

# Give kubelet some time to restart the static pod if it just changed
sleep 10

if /bin/ps -ef | grep kube-apiserver | grep -v grep >/dev/null 2>&1; then
echo "kube-apiserver process detected. Verifying TLS arguments..."
else
echo "kube-apiserver process not found yet. Current processes:"
/bin/ps -ef | grep kube-apiserver || true
exit 1
fi

# Show the relevant arguments for manual confirmation
/bin/ps -ef | grep kube-apiserver | grep -v grep | tr -s ' ' | sed 's/ /\n/g' | \
grep -E -- '--tls-cert-file|--tls-private-key-file' || true

# Automated check for both flags and correct values
if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--tls-cert-file=${TLS_CERT_PATH}" \
&& /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--tls-private-key-file=${TLS_KEY_PATH}"; then
echo "Verification successful: kube-apiserver is running with the desired --tls-cert-file and --tls-private-key-file."
else
echo "Verification FAILED: kube-apiserver does not show the expected TLS arguments."
echo "Inspect the kube-apiserver manifest and process output above to troubleshoot."
exit 1
fi
}

main() {
require_root
run_on_control_plane_only
validate_paths
backup_manifest
apply_fix
verify
}

main "$@"