#!/usr/bin/env bash # T58 (re-run 2, after CORRECTION 2026-07-05): PeerAuthentication/RequestAuthentication is # attribute-only; AuthorizationPolicy is the real gate. # # Run-1 confound: curl targets used the *.svc.homelab.local FQDN (real cluster DNS domain), # but istiod's service registry / Envoy vhost matching keys off *.svc.cluster.local regardless # of clusterDomain. Requests to the homelab.local FQDN fell through to PassthroughCluster and # were rejected by STRICT PeerAuthentication as plaintext -> deterministic 503 on all 6 checks, # no signal about identity/AuthorizationPolicy semantics at all. # # Fix applied here (per harness-notes.md CORRECTION): curl the k8s service by SHORT NAME # (http://echo./), which resolves via the pod's DNS search path AND matches the Envoy vhost # (registry name). PeerAuthentication/AuthorizationPolicy/RequestAuthentication selectors are # label-based (unaffected by DNS domain); the AuthorizationPolicy principal string correctly # uses the cluster.local trust domain (SPIFFE ID), which is a separate concept from cluster DNS. set -euo pipefail NS=istio-vt-t58-r2 DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cleanup() { echo "[cleanup] deleting namespace $NS" kubectl delete namespace "$NS" --wait=false --ignore-not-found } trap cleanup EXIT echo "[setup] create namespace $NS with istio-injection=enabled" kubectl create namespace "$NS" --dry-run=client -o yaml | kubectl apply -f - kubectl label namespace "$NS" istio-injection=enabled --overwrite echo "[apply] client pod + echo deployment/service" kubectl apply -f "$DIR/client-echo.yaml" kubectl -n "$NS" wait --for=condition=Ready pod/client --timeout=90s kubectl -n "$NS" rollout status deployment/echo --timeout=120s echo "[apply] PeerAuthentication STRICT on echo" kubectl apply -f "$DIR/strict-mtls.yaml" sleep 10 echo "[check] peerauth_strict_only (expect 200 - identity mismatch alone is not blocked by PeerAuthentication)" kubectl -n "$NS" exec client -c curl -- curl -s -o /dev/null -w 'peerauth_strict_only=%{http_code}\n' \ "http://echo.$NS/" echo "[apply] AuthorizationPolicy ALLOW matching wrong principal only" kubectl apply -f "$DIR/allow-wrong-principal.yaml" sleep 10 echo "[check] after_wrong_principal_allow (expect 403 - deny-by-default once any ALLOW policy exists)" kubectl -n "$NS" exec client -c curl -- curl -s -o /dev/null -w 'after_wrong_principal_allow=%{http_code}\n' \ "http://echo.$NS/" kubectl delete authorizationpolicy allow-wrong-principal -n "$NS" echo "[generate] ephemeral JWT + JWKS (RSA, openssl+python3, no external network dependency)" python3 - <<'PYEOF' import subprocess, base64, json, time subprocess.run(['openssl','genrsa','-out','/tmp/jwt.key','2048'], check=True, capture_output=True) subprocess.run(['openssl','rsa','-in','/tmp/jwt.key','-pubout','-out','/tmp/jwt.pub'], check=True, capture_output=True) mod = subprocess.run(['openssl','rsa','-pubin','-in','/tmp/jwt.pub','-noout','-modulus'], capture_output=True, text=True, check=True).stdout.strip().split('=')[1] def b64u(b): return base64.urlsafe_b64encode(b).rstrip(b'=').decode() n = b64u(bytes.fromhex(mod)); e = 'AQAB' jwks = {'keys':[{'kty':'RSA','kid':'k1','use':'sig','alg':'RS256','n':n,'e':e}]} open('/tmp/jwks.json','w').write(json.dumps(jwks)) hdr = b64u(json.dumps({'alg':'RS256','typ':'JWT','kid':'k1'}, separators=(',',':')).encode()) pl = b64u(json.dumps({'iss':'test-issuer@istio-verify','sub':'tester','aud':'istio-verify-test','exp':int(time.time())+3600}, separators=(',',':')).encode()) signing_input = f'{hdr}.{pl}'.encode() sig = subprocess.run(['openssl','dgst','-sha256','-sign','/tmp/jwt.key'], input=signing_input, capture_output=True, check=True).stdout open('/tmp/valid.jwt','w').write(f'{hdr}.{pl}.{b64u(sig)}') print('jwt+jwks generated') PYEOF kubectl -n "$NS" create configmap tmp-jwks --from-file=jwks.json=/tmp/jwks.json --dry-run=client -o yaml | kubectl apply -f - JWKS_INLINE=$(python3 -c 'import json; print(json.dumps(json.load(open("/tmp/jwks.json"))))') echo "[apply] RequestAuthentication with inline JWKS" kubectl apply -f - <