Skip to content

Verifying Platform Binaries

Why Verify Binaries?

Every Kubernetes cluster runs a set of critical binaries -- kubectl, kubeadm, kubelet, kube-apiserver, etcd, and others. If any of these binaries are compromised, an attacker gains full control over the cluster. Verifying binaries ensures that what you downloaded or what is installed on a node is the exact, unmodified binary published by the Kubernetes project.

Supply Chain Attack Risks

Compromised binaries can be introduced through:

  • Supply chain attacks -- an attacker tampers with the binary at the source, build pipeline, or distribution channel
  • Compromised downloads -- a mirror or CDN serves a modified binary
  • Man-in-the-Middle (MITM) attacks -- an attacker intercepts the download and replaces the binary in transit
  • Post-installation tampering -- an attacker with node access replaces a binary on disk

A tampered kubelet or kube-apiserver binary could exfiltrate secrets, open backdoors, or grant unauthorized access -- all while appearing to function normally.

CKS Exam Relevance

The CKS exam may present a scenario where you suspect a binary has been tampered with on a cluster node. You must know how to:

  1. Download the correct checksum file from the official Kubernetes release
  2. Compute the SHA-256 hash of the installed binary
  3. Compare the computed hash against the official checksum
  4. Determine whether the binary has been modified

This is a straightforward but detail-oriented task. Practice the exact commands so you can execute them quickly under exam pressure.

Binary Verification Flow

Kubernetes Binaries and Their Locations

BinaryPurposeTypical Location(s)
kubectlCLI tool for interacting with the cluster/usr/local/bin/kubectl or /usr/bin/kubectl
kubeadmCluster bootstrapping and management tool/usr/local/bin/kubeadm or /usr/bin/kubeadm
kubeletNode agent that runs on every node/usr/bin/kubelet
kube-apiserverAPI server (control plane)/usr/bin/kube-apiserver or runs in a container
kube-controller-managerController manager (control plane)/usr/bin/kube-controller-manager or runs in a container
kube-schedulerScheduler (control plane)/usr/bin/kube-scheduler or runs in a container
kube-proxyNetwork proxy on each node/usr/bin/kube-proxy or runs in a container
etcdKey-value store for cluster state/usr/local/bin/etcd or runs in a container

Finding Binary Locations

bash
# Find where a binary is located
which kubectl
which kubeadm
which kubelet

# Alternative: use the full path search
whereis kubectl

# For binaries running in static pods (control plane), check the container image
# The binary is inside the container, not directly on the host filesystem
crictl ps | grep kube-apiserver

Downloading Binaries Securely

Official Download Sources

Kubernetes binaries should only be downloaded from official sources:

SourceURLNotes
dl.k8s.iohttps://dl.k8s.ioOfficial download site (redirects to storage.googleapis.com)
GitHub Releaseshttps://github.com/kubernetes/kubernetes/releasesRelease page with changelogs and checksums
storage.googleapis.comhttps://storage.googleapis.com/kubernetes-release/Direct storage bucket

Download a Specific Binary

bash
# Download kubectl for a specific version
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl"

# Download kubeadm
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubeadm"

# Download kubelet
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubelet"

Download the Checksum File

bash
# Download the SHA-256 checksum for kubectl
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl.sha256"

# Download the SHA-256 checksum for kubeadm
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubeadm.sha256"

# Download the SHA-256 checksum for kubelet
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubelet.sha256"

Always Use HTTPS

Never download binaries or checksums over plain HTTP. Always use https:// to prevent MITM attacks during the download itself. If you download the checksum over HTTP, an attacker could replace both the binary and the checksum.

Verifying SHA-256 Checksums

Method 1: Manual Comparison

bash
# Step 1: Compute the SHA-256 hash of the downloaded binary
sha256sum kubectl

# Output:
# e7a7d31e025b5712b1aaca5c065e05f29de6e5b4f2a0a71c9a635ae7c4d38bf2  kubectl

# Step 2: View the official checksum
cat kubectl.sha256

# Output:
# e7a7d31e025b5712b1aaca5c065e05f29de6e5b4f2a0a71c9a635ae7c4d38bf2

# Step 3: Compare them visually or with echo
echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check
# Output: kubectl: OK

Method 2: Automated Verification with --check

This is the recommended method and the fastest approach for the exam:

bash
# Verify kubectl
echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check

# Expected output if binary is authentic:
# kubectl: OK

# Expected output if binary is tampered with:
# kubectl: FAILED
# sha256sum: WARNING: 1 computed checksum did NOT match

Exam Speed Tip

The one-liner echo "$(cat <binary>.sha256) <binary>" | sha256sum --check is the fastest way to verify. Note the two spaces between the checksum and the filename -- this is required by the sha256sum --check format.

Method 3: Using sha512sum

Some releases also provide SHA-512 checksums:

bash
# Download SHA-512 checksum
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl.sha512"

# Verify with sha512sum
echo "$(cat kubectl.sha512)  kubectl" | sha512sum --check

# Output: kubectl: OK

Verifying Binaries Already Installed on a Node

This is the most likely exam scenario: you SSH into a node and need to verify that the installed binary matches the official release.

Step-by-Step Verification of an Installed Binary

bash
# Step 1: Find the binary location
which kubelet
# /usr/bin/kubelet

# Step 2: Check the version of the installed binary
kubelet --version
# Kubernetes v1.31.0

# Step 3: Download the official checksum for that version
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubelet.sha256"

# Step 4: Compute the hash of the installed binary
sha256sum /usr/bin/kubelet
# a1b2c3d4e5f6...  /usr/bin/kubelet

# Step 5: Compare against the official checksum
echo "$(cat kubelet.sha256)  /usr/bin/kubelet" | sha256sum --check
# /usr/bin/kubelet: OK       <-- Binary is authentic
# /usr/bin/kubelet: FAILED   <-- Binary has been tampered with!

Verifying kubectl on a Node

bash
# Check version
kubectl version --client --short 2>/dev/null || kubectl version --client
# Client Version: v1.31.0

# Download checksum
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl.sha256"

# Verify
echo "$(cat kubectl.sha256)  $(which kubectl)" | sha256sum --check
# /usr/local/bin/kubectl: OK

Verifying kubeadm on a Node

bash
# Check version
kubeadm version -o short
# v1.31.0

# Download checksum
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubeadm.sha256"

# Verify
echo "$(cat kubeadm.sha256)  $(which kubeadm)" | sha256sum --check
# /usr/bin/kubeadm: OK

Detecting Tampered Binaries

What Does a Failed Verification Look Like?

bash
echo "$(cat kubelet.sha256)  /usr/bin/kubelet" | sha256sum --check
# /usr/bin/kubelet: FAILED
# sha256sum: WARNING: 1 computed checksum did NOT match

Investigating a Mismatch

A hash mismatch can indicate:

CauseLikelihoodAction
Binary has been tampered withPossibleTreat as a security incident
Wrong versionCommonVerify you downloaded the checksum for the correct version
Wrong architectureCommonEnsure you are comparing linux/amd64 checksum with an amd64 binary
Corrupted downloadPossibleRe-download and verify again
bash
# First, double-check the version
kubelet --version
# Kubernetes v1.31.0

# Verify you have the right checksum file
cat kubelet.sha256

# Compute the hash again to rule out errors
sha256sum /usr/bin/kubelet

# If the version matches but the hash doesn't, this is suspicious
# Download the known-good binary and replace:
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubelet"
echo "$(cat kubelet.sha256)  kubelet" | sha256sum --check
# kubelet: OK

# Replace the compromised binary
sudo cp kubelet /usr/bin/kubelet
sudo chmod +x /usr/bin/kubelet
sudo systemctl restart kubelet

If a Binary Is Tampered

If you confirm a binary has been tampered with on a production cluster:

  1. Isolate the node -- cordon and drain it immediately
  2. Replace the binary with a verified copy from the official release
  3. Investigate how the binary was modified (check audit logs, file modification times, user access)
  4. Check other nodes -- if one node is compromised, others may be too
  5. Rotate credentials -- assume any secrets on the node are compromised

Practical Exam Scenario

Scenario: Verify a Suspected Tampered Binary

Task: You suspect the kubelet binary on node worker-1 has been tampered with. The cluster is running Kubernetes v1.31.0. SSH into the node, verify the binary against the official release checksum, and replace it if necessary.

Solution:

bash
# SSH into the node
ssh worker-1

# Step 1: Confirm the kubelet version
kubelet --version
# Kubernetes v1.31.0

# Step 2: Locate the binary
which kubelet
# /usr/bin/kubelet

# Step 3: Compute the hash of the installed binary
sha256sum /usr/bin/kubelet
# 7f34a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0  /usr/bin/kubelet

# Step 4: Download the official checksum for v1.31.0
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubelet.sha256"

# Step 5: Verify the installed binary
echo "$(cat kubelet.sha256)  /usr/bin/kubelet" | sha256sum --check

# If FAILED: The binary has been tampered with
# Step 6: Download the official binary
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubelet"

# Step 7: Verify the downloaded binary itself
echo "$(cat kubelet.sha256)  kubelet" | sha256sum --check
# kubelet: OK

# Step 8: Replace the compromised binary
sudo cp kubelet /usr/bin/kubelet
sudo chmod +x /usr/bin/kubelet

# Step 9: Restart the kubelet
sudo systemctl restart kubelet
sudo systemctl status kubelet

# Step 10: Verify the replacement
echo "$(cat kubelet.sha256)  /usr/bin/kubelet" | sha256sum --check
# /usr/bin/kubelet: OK

Scenario: Verify Multiple Binaries at Once

bash
#!/bin/bash
# verify-k8s-binaries.sh
# Script to verify all key Kubernetes binaries on a node

VERSION="v1.31.0"
ARCH="linux/amd64"
BASE_URL="https://dl.k8s.io/release/${VERSION}/bin/${ARCH}"
TMPDIR=$(mktemp -d)
FAILED=0

BINARIES=("kubectl" "kubeadm" "kubelet")

for BINARY in "${BINARIES[@]}"; do
    BINARY_PATH=$(which "$BINARY" 2>/dev/null)
    if [ -z "$BINARY_PATH" ]; then
        echo "[SKIP] $BINARY not found on this node"
        continue
    fi

    echo "[INFO] Verifying $BINARY at $BINARY_PATH ..."

    # Download the official checksum
    curl -sLO --output-dir "$TMPDIR" "${BASE_URL}/${BINARY}.sha256"

    # Verify
    if echo "$(cat ${TMPDIR}/${BINARY}.sha256)  ${BINARY_PATH}" | sha256sum --check --status 2>/dev/null; then
        echo "[PASS] $BINARY is authentic"
    else
        echo "[FAIL] $BINARY checksum does NOT match -- possible tampering!"
        FAILED=$((FAILED + 1))
    fi
done

rm -rf "$TMPDIR"

if [ $FAILED -gt 0 ]; then
    echo ""
    echo "WARNING: $FAILED binary(ies) failed verification!"
    exit 1
else
    echo ""
    echo "All binaries verified successfully."
    exit 0
fi
bash
# Run the verification script
chmod +x verify-k8s-binaries.sh
./verify-k8s-binaries.sh

Sample output (all binaries OK):

[INFO] Verifying kubectl at /usr/local/bin/kubectl ...
[PASS] kubectl is authentic
[INFO] Verifying kubeadm at /usr/bin/kubeadm ...
[PASS] kubeadm is authentic
[INFO] Verifying kubelet at /usr/bin/kubelet ...
[PASS] kubelet is authentic

All binaries verified successfully.

Sample output (tampered kubelet):

[INFO] Verifying kubectl at /usr/local/bin/kubectl ...
[PASS] kubectl is authentic
[INFO] Verifying kubeadm at /usr/bin/kubeadm ...
[PASS] kubeadm is authentic
[INFO] Verifying kubelet at /usr/bin/kubelet ...
[FAIL] kubelet checksum does NOT match -- possible tampering!

WARNING: 1 binary(ies) failed verification!

Verifying Control Plane Binaries in Static Pods

Control plane components (kube-apiserver, kube-controller-manager, kube-scheduler) often run as static pods rather than as bare binaries on the host. In this case, the binary is inside the container image.

bash
# Check which image the kube-apiserver is using
kubectl -n kube-system get pod kube-apiserver-controlplane -o jsonpath='{.spec.containers[0].image}'
# registry.k8s.io/kube-apiserver:v1.31.0

# The image tag should match the expected version
# If using kubeadm, check the expected version
kubeadm version -o short

# You can also verify by checking the static pod manifest
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep image:
#     image: registry.k8s.io/kube-apiserver:v1.31.0

Verifying Container Images

For binaries running inside containers, the verification approach shifts from checksum comparison to image digest verification:

bash
# Check the image digest of a running pod
kubectl -n kube-system get pod kube-apiserver-controlplane \
  -o jsonpath='{.status.containerStatuses[0].imageID}'
# docker-pullable://registry.k8s.io/kube-apiserver@sha256:abc123...

# Compare against the official digest from the Kubernetes release

The Complete Verification Process

Quick Reference

Essential Commands

bash
# ---- FIND BINARY LOCATION ----
which kubectl
which kubeadm
which kubelet

# ---- CHECK BINARY VERSION ----
kubectl version --client
kubeadm version -o short
kubelet --version

# ---- DOWNLOAD OFFICIAL CHECKSUM ----
curl -LO "https://dl.k8s.io/release/<VERSION>/bin/linux/amd64/<BINARY>.sha256"

# ---- COMPUTE SHA-256 HASH ----
sha256sum /path/to/binary

# ---- VERIFY WITH ONE COMMAND ----
echo "$(cat <BINARY>.sha256)  /path/to/binary" | sha256sum --check

# ---- DOWNLOAD AND VERIFY A BINARY (FULL FLOW) ----
VERSION="v1.31.0"
BINARY="kubectl"

curl -LO "https://dl.k8s.io/release/${VERSION}/bin/linux/amd64/${BINARY}"
curl -LO "https://dl.k8s.io/release/${VERSION}/bin/linux/amd64/${BINARY}.sha256"
echo "$(cat ${BINARY}.sha256)  ${BINARY}" | sha256sum --check

# ---- VERIFY AN INSTALLED BINARY ----
echo "$(cat kubectl.sha256)  $(which kubectl)" | sha256sum --check

# ---- REPLACE A COMPROMISED BINARY ----
sudo cp <verified-binary> /usr/bin/<binary>
sudo chmod +x /usr/bin/<binary>
sudo systemctl restart <service>   # e.g., kubelet

Checksum URL Pattern

https://dl.k8s.io/release/<VERSION>/bin/<OS>/<ARCH>/<BINARY>.sha256

Examples:
https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl.sha256
https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubeadm.sha256
https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubelet.sha256

Key Exam Takeaways

Summary

  1. Always verify binaries using SHA-256 checksums from the official Kubernetes release (dl.k8s.io)
  2. The one-liner to remember: echo "$(cat binary.sha256) /path/to/binary" | sha256sum --check
  3. Get the version first (kubelet --version) before downloading the checksum -- version mismatch is the most common cause of a hash mismatch
  4. Two spaces are required between the checksum and the filename in the sha256sum --check format
  5. Replace compromised binaries by downloading from the official source, verifying the download, copying into place, and restarting the service
  6. Control plane binaries in static pods are verified by checking container image tags and digests rather than file checksums
  7. Know the URL pattern: https://dl.k8s.io/release/<VERSION>/bin/linux/amd64/<BINARY>.sha256

Common Exam Pitfalls

  • Downloading the checksum for the wrong version -- always check the installed version first
  • Forgetting the two spaces in echo "$(cat file.sha256) binary" | sha256sum --check
  • Confusing the binary path -- use which <binary> to find the actual location
  • Not verifying the replacement binary itself before installing it
  • Forgetting to chmod +x and systemctl restart after replacing a binary

Released under the MIT License.