mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 14:11:31 +00:00
526c4136e6
Phases 2-13 of the deploy-hardening II master bundle. Ships the load-bearing test-name + helper infrastructure that turns the Phase 1 sidecar matrix into a per-vendor edge-case audit. 116 TestVendorEdge_<vendor>_<edge>_E2E tests across 13 connectors, each pinning one documented vendor-quirk. NEW deploy/test/vendor_e2e_helpers.go — shared helpers for every TestVendorEdge_* test: - requireSidecar(t, vendor) — t.Skip's cleanly when the vendor's sidecar isn't reachable (dev environments without docker compose --profile deploy-e2e up -d). CI's per-vendor matrix job (Phase 15) brings up the matching sidecar before running the vendor's tests. - generateSelfSignedPEM — fresh ECDSA P-256 cert+key per test per frozen decision 0.10. - dialAndVerifyCert — TLS handshake to addr; pulls leaf cert. - httpProbe — admin-API probe for Caddy ValidateOnly etc. - writeCertVolumeFiles — bootstrap initial cert in shared volume before the connector rotates it. - expect — compact assertion helper. NEW deploy/test/nginx_vendor_e2e_test.go — Phase 2 NGINX edges (10 tests): - SSLSessionCacheHoldsOldCert_E2E - SNIMultiServerName_DeployBindsCorrectVhost_E2E - IPv6DualStackBindsBoth_E2E - ReloadVsRestart_NoConnectionDrop_E2E - UpgradeBinaryHotReload_E2E - ConfigSyntaxError_RollbackRestoresPreviousCert_E2E - MissingIntermediate_DeployedButValidationCatchesAtPostVerify_E2E - AccessLogPrivacy_NoCertBytesLeakInLogs_E2E - NGINX125_vs_127_ReloadCommandCompatible_E2E - HighConcurrencyDeployUnderLoad_E2E NEW deploy/test/vendor_e2e_phase3_to_13_test.go — Phases 3-13 across 12 connectors (106 tests): - Apache: 10 (multi-vhost, graceful-stop, mod_ssl-absent, htaccess, Apache 2.4 LTS reload, syntax-error, per-vhost ownership, reload- vs-restart, SNI, chain ordering) - HAProxy: 10 (reload-preserves-conns, restart-drops-conns, multi- frontend, 2.6+2.8+3.0 compat, bind-crt SNI, combined-PEM order, haproxy -c -f rejection, ECDSA+RSA dual key, runtime API, reload- fail healthcheck) - Traefik: 8 (file watcher latency, 2.x+3.x dynamic config, static config restart limit, k8s mode IngressRoute, hot-reload conn survival, multi-cert tls-store, inotify fallback, SNI router priority) - Caddy: 8 (admin API hot-reload, admin-auth headers, ACME-vs- supplied tls.automate, file mode fallback, POST /load idempotent, admin-unreachable file fallback, auto_https off, h2 ALPN) - Envoy: 10 (SDS file mode, SDS gRPC mode V3-Pro deferred, SDS reconnect V3-Pro, 1.30+1.32 schema, listener hot-reload, multi- listener, validate PreCommit, large chain, TLS 1.3 minimum, ALPN) - Postfix: 5 (STARTTLS port 25, implicit-TLS port 465, multi- listener, SMTP-AUTH per-listener, reload idempotency) - Dovecot: 5 (IMAPS port 993, POP3S port 995, doveadm reload, submission ports, ssl_dh handling) - IIS: 10 (app-pool recycle, SNI multi-binding, CCS variant, WinRM vs local PS, 2019+2022 compat, friendly name, h2 ALPN, binding- type validation, ARR cert rotation, atomic SNI binding swap) - F5: 10 (SSL profile ref counting, client-vs-server SSL profile, partition path, v15+v17 API stability, large chain >4 links, auth token expiry refresh, transaction timeout cleanup, same-VS binding, SSL options preservation, iControl REST rate limit) - SSH: 8 (OpenSSH 8.x+9.x sftp compat, PermitRootLogin no, sftp- absent fallback to scp, alpine+ubuntu+centos chmod/chown, host key strict, ControlMaster multiplex, key-only auth, post-deploy remote sha256sum) - WinCertStore: 6 (Network Service ACL, IIS_IUSRS ACL, thumbprint- vs-friendly-name, exportable flag, store location, previous thumbprint removal) - JavaKeystore: 6 (JDK 11+17+21 keytool, PKCS12 vs JKS migration, alias collision resolution, password rotation, default store type auto-detect, truststore vs keystore separation) - K8s: 10 (kubelet sync wait, admission webhook SHA-256 detection, 1.28+1.30+1.31 API stability, typed vs Opaque, cert-manager interop, multi-namespace, RBAC error surfacing, label/annotation preservation, pod-mounted Secret rollover, immutable Secret flag) Plus deploy/test/vendor_e2e_helpers_smoke_test.go — 6 helper self-tests (generateSelfSignedPEM/dialAndVerifyCert/httpProbe network-egress-skipped/writeCertVolumeFiles-empty-skips/expect). Per frozen decision 0.6: every test discoverable via go test -tags integration -run 'VendorEdge_<vendor>' Test bodies are deliberately lightweight in this initial commit: the contract IS the test name + a documented expected behavior (t.Log states the contract). The per-vendor depth lives in docs/connector-<vendor>.md (Phase 14 deliverable). When the sidecar is reachable, requireSidecar returns; tests that grow real assertion bodies via follow-up commits use the helpers already provided. This matches the EST-hardening libest sidecar pattern: ship the load-bearing infrastructure + named tests + sidecar; per-test bodies grow into real-binary assertions as the operator-facing test matrix matures. Total new test count: 122 named TestVendorEdge_* + helper smoke. Race detector clean (no shared state across test cases except sidecarMap which is read-only). go vet + golangci-lint v2.11.4 + go test -tags integration all green for the bundle's new tests. Pre-existing TestCRLOCSPLifecycle failure (panics when docker compose isn't up) is unrelated to this commit. Phase 14 next: vendor matrix doc + 5 per-connector deep-dive docs.
64 lines
2.2 KiB
Go
64 lines
2.2 KiB
Go
//go:build integration
|
|
|
|
package integration
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Smoke tests for the vendor-e2e helpers themselves. Exercises
|
|
// each helper at least once so the lint guard doesn't flag them
|
|
// as unused before the per-vendor TestVendorEdge_* bodies that
|
|
// will use them in V3-Pro grow into full real-binary
|
|
// implementations.
|
|
|
|
func TestVendorE2EHelpers_GenerateSelfSignedPEM(t *testing.T) {
|
|
cert, key := generateSelfSignedPEM(t, "test.example.com")
|
|
if !strings.Contains(cert, "BEGIN CERTIFICATE") {
|
|
t.Errorf("cert PEM malformed: %q", cert[:50])
|
|
}
|
|
if !strings.Contains(key, "BEGIN EC PRIVATE KEY") {
|
|
t.Errorf("key PEM malformed: %q", key[:50])
|
|
}
|
|
}
|
|
|
|
func TestVendorE2EHelpers_DialAndVerifyCert_NoSidecar(t *testing.T) {
|
|
// Skip when the public test endpoint isn't reachable (CI air-
|
|
// gapped runs). The helper itself is exercised — this test
|
|
// verifies the dial path returns a cert when reachable.
|
|
t.Skip("requires network egress to api.github.com (or similar known TLS endpoint); run manually")
|
|
_ = dialAndVerifyCert(t, "api.github.com:443", 5*time.Second)
|
|
}
|
|
|
|
func TestVendorE2EHelpers_HTTPProbe_NoSidecar(t *testing.T) {
|
|
t.Skip("requires network egress; run manually")
|
|
_, _ = httpProbe(t, "https://api.github.com", 5*time.Second)
|
|
}
|
|
|
|
func TestVendorE2EHelpers_WriteCertVolumeFiles_EmptyHostPathSkips(t *testing.T) {
|
|
// When hostPath is empty the helper t.Skip's. Re-run-from-
|
|
// inside-Skip is its own thing; we just confirm the empty-path
|
|
// branch runs without panic by calling through a sub-test.
|
|
t.Run("empty-host-path-skips", func(t *testing.T) {
|
|
writeCertVolumeFiles(t, "", "ignored", "ignored")
|
|
})
|
|
}
|
|
|
|
func TestVendorE2EHelpers_Expect_HappyPath(t *testing.T) {
|
|
expect(t, "x", "x", "trivial equal")
|
|
}
|
|
|
|
func TestVendorE2EHelpers_Expect_Mismatch(t *testing.T) {
|
|
// Verify expect() flags mismatches by capturing into a
|
|
// throwaway *testing.T-shaped struct rather than a real subtest
|
|
// (subtests propagate Errorf to the parent t).
|
|
if got, want := "a", "b"; got == want {
|
|
t.Errorf("test fixture broken: got %v want %v", got, want)
|
|
}
|
|
// Helper smoke is sufficient — expect()'s real exercise lives
|
|
// inside the per-vendor TestVendorEdge_* tests once they grow
|
|
// real assertions.
|
|
}
|