#!/usr/bin/env bash # T17 (run 2 / r2): ServiceEntry protocol (HTTP/TLS) determines filter-chain # matching, RDS routing, and istio_requests_total L7 metric visibility. # # RE-RUN NOTE: run 1 (see ../T17.attempt1) used mock.istio-verify-ext.svc.homelab.local # (an in-cluster k8s Service dressed up as a MESH_EXTERNAL ServiceEntry host) as # the "unregistered/external" test target. Per harness-notes.md CORRECTION # (2026-07-05) bullet 4, an in-cluster k8s Service is already IN Istio's service # registry and is a WRONG stand-in for "unregistered destination" semantics - # independent of the homelab.local vs cluster.local FQDN issue. This run # replaces that target with a genuine external host (httpbin.org), matching the # pattern already established elsewhere in this cluster (mesh-test namespace: # cnn-ext, httpbin-ext, wiki-ext, example-logicaldns, pt-ext all use real # external hosts for MESH_EXTERNAL ServiceEntries). # # ServiceEntry hosts are literal and are NOT affected by istiod's # --domain=cluster.local flag (that only remaps auto-registered k8s Service # hostnames) - so httpbin.org is used as-is for both the SE `hosts:` field and # the client's curl target, per harness-notes bullet 3 ("ServiceEntry hosts: # literal - whatever hostname the client will actually use"). # # Phases: # Phase 0: no ServiceEntry at all (ALLOW_ANY passthrough baseline) # Phase 1: ServiceEntry with protocol: HTTP (correct declaration) for httpbin.org:80 # Phase 2: ServiceEntry with protocol: TLS on the SAME plaintext port 80 # (deliberate misdeclaration) # # Namespace is deleted on exit regardless of success/failure. set -uo pipefail # NOTE: deliberately NOT using `set -e` at top level. Several observation # commands (esp. Phase 2's expected-to-fail curl, and any transient real-world # egress hiccup on Phase 0/1) must not abort the whole run before later phases # and cleanup can execute. Every step below captures/report its own exit code. NS="istio-vt-t17-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] creating namespace ${NS} with istio-injection=enabled" kubectl create namespace "${NS}" kubectl label namespace "${NS}" istio-injection=enabled echo "[apply] client + echo workloads" 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=90s kubectl -n "${NS}" get pods -o wide echo echo "=== Phase 0: ALLOW_ANY, no ServiceEntry registered at all (target: httpbin.org) ===" kubectl -n "${NS}" exec client -c istio-proxy -- curl -s localhost:15000/stats \ | grep '^istio_requests_total' | wc -l kubectl -n "${NS}" exec client -c curl -- curl -s -o /dev/null \ -w 'unregistered=%{http_code}\n' --max-time 15 http://httpbin.org/ echo "unregistered_curl_exit=$?" kubectl -n "${NS}" exec client -c istio-proxy -- curl -s localhost:15000/stats/prometheus \ | grep -c '^istio_requests_total.*httpbin' echo "(0 above means no match yet, expected pre-Phase1)" echo "--- full istio_requests_total lines matching httpbin (Phase 0 diagnostic) ---" kubectl -n "${NS}" exec client -c istio-proxy -- curl -s localhost:15000/stats/prometheus \ | grep '^istio_requests_total.*httpbin' echo echo "=== Phase 1: register protocol: HTTP correctly (httpbin.org:80) ===" kubectl apply -f "${DIR}/httpbin-http-correct-se.yaml" sleep 5 kubectl -n "${NS}" exec client -c curl -- curl -s -o /dev/null \ -w 'registered_http=%{http_code}\n' --max-time 15 http://httpbin.org/ echo "registered_http_curl_exit=$?" istioctl proxy-config route "client.${NS}" -o json \ | jq '[.[] | select(.virtualHosts[]?.domains[]? == "httpbin.org")] | length' kubectl -n "${NS}" exec client -c istio-proxy -- curl -s localhost:15000/stats/prometheus \ | grep -c '^istio_requests_total.*httpbin' echo "--- full istio_requests_total lines matching httpbin (Phase 1 diagnostic) ---" kubectl -n "${NS}" exec client -c istio-proxy -- curl -s localhost:15000/stats/prometheus \ | grep '^istio_requests_total.*httpbin' kubectl -n "${NS}" delete serviceentry httpbin-http-correct-se echo echo "=== Phase 2: misdeclare the SAME plaintext port as protocol: TLS (httpbin.org:80) ===" kubectl apply -f "${DIR}/httpbin-tls-misdeclared-se.yaml" sleep 5 echo "--- 0.0.0.0_80 outbound listener filter chains (diagnostic) ---" istioctl proxy-config listener "client.${NS}" --port 80 -o json \ | jq '.[] | {name, filterChains: [.filterChains[] | {filterChainMatch, filters: [.filters[].name]}]}' kubectl -n "${NS}" exec client -c curl -- curl -v -s -o /dev/null \ -w 'misdeclared_tls=%{http_code}\n' --max-time 4 http://httpbin.org/ echo "exit=$?" echo echo "[done] see result.txt in this directory for this run's captured raw output."