rotate: promote k8s + mongo to LIVE-VM via real-target POCs
Ran live cutover POCs in the sandbox VM against the real target software and promoted both drivers from MOCK-ONLY to LIVE-VM in the proof table (data, not driver behaviour): - k8s: proven against real k3s v1.35 kube-apiserver + token controller (lab-provision-k8s.sh). The real apiserver serves a self-signed cert, so the driver gained CA-pinned TLS — blob params ca= (base64 PEM, pinned as the only trusted root) and insecure=1 (lab-only escape hatch), routed through a new clientFor(cr). Neither is test-awareness; a real deployment configures the same CA. Added TestK8sTLSTrust covering CA-pinned / insecure / untrusted-rejection and the blob round-trip. - mongo: proven against real mongod 8.0 (lab-provision-mongo.sh). npm stays MOCK-ONLY by decision (registry.npmjs.org not self-hostable; real create-token requires the account password in the body) — documented as a contract gap rather than faking a LIVE-VM. gcp/twilio/flyio remain MOCK-ONLY. Live POCs surfaced real-target-only behaviour now recorded in the docs: the apiserver's ~10s successful-auth cache (old token kept working ~7s after Secret deletion) and real mongosh's stdout prompt. Full suite 104 green, -race clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,8 +6,10 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -218,6 +220,56 @@ func TestK8sRevokeRequiresSecretName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestK8sTLSTrust proves the ca=/insecure= plumbing actually drives the TLS
|
||||
// handshake when no client is injected (the real-apiserver path). The httptest
|
||||
// server serves a self-signed cert, so a default client rejects it; pinning its CA
|
||||
// or setting insecure=1 must make the same request succeed.
|
||||
func TestK8sTLSTrust(t *testing.T) {
|
||||
v := vault.New()
|
||||
defer v.Purge()
|
||||
ctx := context.Background()
|
||||
const ns, sa, tok = "labns", "labsa", "eyJtls0123"
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/api/v1/namespaces/"+ns+"/serviceaccounts/", func(w http.ResponseWriter, r *http.Request) {
|
||||
json.NewEncoder(w).Encode(map[string]any{"metadata": map[string]any{"name": sa}})
|
||||
})
|
||||
srv := httptest.NewTLSServer(mux)
|
||||
defer srv.Close()
|
||||
host := strings.TrimPrefix(srv.URL, "https://")
|
||||
|
||||
caPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: srv.Certificate().Raw})
|
||||
caB64 := base64.StdEncoding.EncodeToString(caPEM)
|
||||
|
||||
verifyWith := func(blob string) error {
|
||||
k := &K8s{} // NO injected client — exercises clientFor's real TLS path
|
||||
h := v.Store([]byte(blob))
|
||||
return k.Verify(ctx, h, v)
|
||||
}
|
||||
|
||||
base := "k8s://" + host + "/?ns=" + ns + "&sa=" + sa + "&token=" + tok
|
||||
|
||||
if err := verifyWith(base); err == nil {
|
||||
t.Error("untrusted self-signed cert should fail without ca=/insecure=")
|
||||
}
|
||||
if err := verifyWith(base + "&insecure=1"); err != nil {
|
||||
t.Errorf("insecure=1 should skip verification: %v", err)
|
||||
}
|
||||
if err := verifyWith(base + "&ca=" + url.QueryEscape(caB64)); err != nil {
|
||||
t.Errorf("pinned ca= should trust the server cert: %v", err)
|
||||
}
|
||||
|
||||
// Blob round-trip: ca=/insecure= survive build()->parse().
|
||||
cr := k8sCred{base: "https://" + host, ns: ns, sa: sa, token: tok, ca: caB64, insecure: true}
|
||||
got, err := parseK8sCred(v, v.Store([]byte(cr.build())))
|
||||
if err != nil {
|
||||
t.Fatalf("round-trip parse: %v", err)
|
||||
}
|
||||
if got.ca != caB64 || !got.insecure {
|
||||
t.Errorf("ca/insecure did not round-trip: ca-match=%v insecure=%v", got.ca == caB64, got.insecure)
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sRejectsBadSecret(t *testing.T) {
|
||||
v := vault.New()
|
||||
defer v.Purge()
|
||||
|
||||
Reference in New Issue
Block a user