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:
- Download the correct checksum file from the official Kubernetes release
- Compute the SHA-256 hash of the installed binary
- Compare the computed hash against the official checksum
- 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
| Binary | Purpose | Typical Location(s) |
|---|---|---|
| kubectl | CLI tool for interacting with the cluster | /usr/local/bin/kubectl or /usr/bin/kubectl |
| kubeadm | Cluster bootstrapping and management tool | /usr/local/bin/kubeadm or /usr/bin/kubeadm |
| kubelet | Node agent that runs on every node | /usr/bin/kubelet |
| kube-apiserver | API server (control plane) | /usr/bin/kube-apiserver or runs in a container |
| kube-controller-manager | Controller manager (control plane) | /usr/bin/kube-controller-manager or runs in a container |
| kube-scheduler | Scheduler (control plane) | /usr/bin/kube-scheduler or runs in a container |
| kube-proxy | Network proxy on each node | /usr/bin/kube-proxy or runs in a container |
| etcd | Key-value store for cluster state | /usr/local/bin/etcd or runs in a container |
Finding Binary Locations
# 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-apiserverDownloading Binaries Securely
Official Download Sources
Kubernetes binaries should only be downloaded from official sources:
| Source | URL | Notes |
|---|---|---|
| dl.k8s.io | https://dl.k8s.io | Official download site (redirects to storage.googleapis.com) |
| GitHub Releases | https://github.com/kubernetes/kubernetes/releases | Release page with changelogs and checksums |
| storage.googleapis.com | https://storage.googleapis.com/kubernetes-release/ | Direct storage bucket |
Download a Specific Binary
# 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
# 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
# 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: OKMethod 2: Automated Verification with --check
This is the recommended method and the fastest approach for the exam:
# 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 matchExam 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:
# 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: OKVerifying 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
# 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
# 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: OKVerifying kubeadm on a Node
# 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: OKDetecting Tampered Binaries
What Does a Failed Verification Look Like?
echo "$(cat kubelet.sha256) /usr/bin/kubelet" | sha256sum --check
# /usr/bin/kubelet: FAILED
# sha256sum: WARNING: 1 computed checksum did NOT matchInvestigating a Mismatch
A hash mismatch can indicate:
| Cause | Likelihood | Action |
|---|---|---|
| Binary has been tampered with | Possible | Treat as a security incident |
| Wrong version | Common | Verify you downloaded the checksum for the correct version |
| Wrong architecture | Common | Ensure you are comparing linux/amd64 checksum with an amd64 binary |
| Corrupted download | Possible | Re-download and verify again |
# 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 kubeletIf a Binary Is Tampered
If you confirm a binary has been tampered with on a production cluster:
- Isolate the node -- cordon and drain it immediately
- Replace the binary with a verified copy from the official release
- Investigate how the binary was modified (check audit logs, file modification times, user access)
- Check other nodes -- if one node is compromised, others may be too
- Rotate credentials -- assume any secrets on the node are compromised
Practical Exam Scenario
Scenario: Verify a Suspected Tampered Binary
Task: You suspect the
kubeletbinary on nodeworker-1has been tampered with. The cluster is running Kubernetesv1.31.0. SSH into the node, verify the binary against the official release checksum, and replace it if necessary.
Solution:
# 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: OKScenario: Verify Multiple Binaries at Once
#!/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# Run the verification script
chmod +x verify-k8s-binaries.sh
./verify-k8s-binaries.shSample 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.
# 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.0Verifying Container Images
For binaries running inside containers, the verification approach shifts from checksum comparison to image digest verification:
# 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 releaseThe Complete Verification Process
Quick Reference
Essential Commands
# ---- 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., kubeletChecksum 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.sha256Key Exam Takeaways
Summary
- Always verify binaries using SHA-256 checksums from the official Kubernetes release (
dl.k8s.io) - The one-liner to remember:
echo "$(cat binary.sha256) /path/to/binary" | sha256sum --check - Get the version first (
kubelet --version) before downloading the checksum -- version mismatch is the most common cause of a hash mismatch - Two spaces are required between the checksum and the filename in the
sha256sum --checkformat - Replace compromised binaries by downloading from the official source, verifying the download, copying into place, and restarting the service
- Control plane binaries in static pods are verified by checking container image tags and digests rather than file checksums
- 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 +xandsystemctl restartafter replacing a binary