Files
certctl/internal/connector/target/k8ssecret/validate_only.go
T
claude 975d1850eb feat(ssh,wincertstore,javakeystore,k8ssecret): explicit ValidateOnly + leverage existing connectors
Phase 9 of the deploy-hardening I master bundle. The four
non-file-server connectors get real ValidateOnly probes that
operators use to preview a deploy without touching the live cert.
Existing DeployCertificate paths already have explicit backup +
rollback semantics (SCP backup / WinCertStore Get-ChildItem
snapshot / keytool snapshot / K8s atomic API).

SSH (validate_only.go):
- Probes via SSHClient.Connect. Confirms agent reachability +
  credentials. Cheap (no remote command runs); released cleanly
  via defer Close.
- A true SCP dry-run requires a no-commit upload (SCP doesn't
  have one). V2 ships the auth probe as the load-bearing check.
- 3 new tests in validate_only_test.go.

WinCertStore (validate_only.go):
- Probes via PowerShell `Get-ChildItem -Path Cert:\<loc>\<store>`
  using the configured StoreLocation + StoreName (defaults
  LocalMachine\My).
- Confirms agent has Windows + the IIS module + the right ACLs.
- 4 new tests including default-store-path verification.

JavaKeystore (validate_only.go):
- Probes via `keytool -list -keystore <path> -storepass <pass>`
  using the configured KeystorePath / KeystorePassword and
  KeytoolPath (default "keytool").
- Confirms keystore exists, password is correct, JRE is on PATH.
- 4 new tests covering succeeds / fails / no-path-sentinel /
  nil-executor-sentinel.

K8s Secret (validate_only.go):
- Probes via K8sClient.GetSecret on the configured Namespace +
  SecretName. Returns nil on success or "not found" (the
  CreateSecret path on Deploy will handle it). Other errors
  (forbidden/unreachable) surface as wrapped.
- 4 new tests covering succeeds / RBAC-error wrapped /
  no-config-sentinel / nil-client-sentinel.

Smoke test connectorsAtPhase3 list shrunk from 7 to 3 entries
(ssh + wincertstore + javakeystore + k8ssecret removed). Only
caddy (file-mode) + envoy + traefik remain — those three
genuinely have no validate-with-target command available.

Race detector clean across all 13 connectors. golangci-lint
v2.11.4 clean.

Phase 10 next: DeployCounters + Prometheus exposer mirroring the
production-hardening-II OCSP counter pattern.
2026-04-30 15:22:17 +00:00

39 lines
1.4 KiB
Go

package k8ssecret
import (
"context"
"fmt"
"github.com/shankar0123/certctl/internal/connector/target"
)
// ValidateOnly — Phase 9. K8s does NOT expose a meaningful dry-run
// for cert deploys via Secret update — the API server's dry-run
// mode confirms admission would succeed but does not validate that
// the cert bytes themselves are well-formed (the kubelet decodes
// them later on the pod side). Phase 9 returns ErrValidateOnlyNotSupported
// per frozen decision 0.6, surfaced explicitly here rather than via
// the default stub so operators can errors.Is to know K8s is
// intentionally a sentinel-return connector.
//
// V3-Pro can extend with API server reachability probe + RBAC
// preflight check.
func (c *Connector) ValidateOnly(ctx context.Context, request target.DeploymentRequest) error {
if c.client == nil {
return target.ErrValidateOnlyNotSupported
}
// Trivial probe: GetSecret on the configured Secret name.
// If we can read it, we have RBAC + reachability; if not,
// surface the actual K8s API error.
if c.config == nil || c.config.Namespace == "" || c.config.SecretName == "" {
return target.ErrValidateOnlyNotSupported
}
_, err := c.client.GetSecret(ctx, c.config.Namespace, c.config.SecretName)
if err != nil {
// "not found" is fine — we'd CREATE the Secret on Deploy.
// Other errors (forbidden, unreachable) surface as wrapped.
return fmt.Errorf("K8s ValidateOnly: GetSecret probe: %w", err)
}
return nil
}