Skip to content

[Low] ate-api-server accepts client certificates chained to system roots when --pod-identity-ca-certs is unset #2918

Open

Description

@destijl

Summary

When --pod-identity-ca-certs is omitted (empty caCertsPath), cmd/ateapi/main.go:buildServerTLSConfig returns a tls.Config with ClientAuth: tls.VerifyClientCertIfGiven and ClientCAs: nil. Under Go's crypto/tls implementation, a nil ClientCAs causes client certificate verification to fall back to the host system root CAs, allowing any client certificate trusted by a system root containing a SPIFFE URI SAN to authenticate as an mTLS principal.

Details

In cmd/ateapi/main.go:631-643:

func buildServerTLSConfig(ctx context.Context, credBundlePath, caCertsPath string) (*tls.Config, error) {
	serverCert := credbundle.Loader(credBundlePath)
	const clientAuth = tls.VerifyClientCertIfGiven

	if caCertsPath == "" {
		return &tls.Config{
			GetCertificate: serverCert,
			ClientAuth:     clientAuth,
		}, nil
	}

When caCertsPath == "", ClientCAs is left nil. In Go's crypto/tls/handshake_server.go:

opts := x509.VerifyOptions{
    Roots: c.config.ClientCAs,
    CurrentTime: c.config.time(),
    Intermediates: x509.NewCertPool(),
    KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}

When opts.Roots is nil, x509.Certificate.Verify defaults to the host's system root CAs. If a client certificate verified by any system CA includes a URI SAN, apiauthn.UnaryServerInterceptor extracts the URI SAN via mtlsPeerIdentity and treats the caller as an authenticated mTLS principal without inspecting or requiring bearer token authentication.

Impact

Low: in standard deployments (manifests/ate-install/ate-api-server.yaml:180), --pod-identity-ca-certs is always provided. The fallback occurs only when --pod-identity-ca-certs is omitted. In standard deployments (manifests/ate-install/ate-api-server.yaml:180), --pod-identity-ca-certs is always provided, pointing to /run/podidentity.podcert.ate.dev/trust-bundle.pem. The fallback occurs only in non-standard or standalone deployments where --pod-identity-ca-certs is deliberately omitted. Furthermore, obtaining a publicly-trusted certificate containing a URI SAN (e.g., spiffe://...) with the clientAuth EKU is non-trivial under standard Web PKI CA baseline requirements. NetworkPolicies on ate-api-server also restrict ingress on port 443 to control plane pods or namespaces labeled ate.dev/api-access=true.

Suggested fix

When caCertsPath == "", explicitly disable client certificate verification by setting ClientAuth: tls.NoClientCert in the returned tls.Config:

	if caCertsPath == "" {
		return &tls.Config{
			GetCertificate: serverCert,
			ClientAuth:     tls.NoClientCert,
		}, nil
	}

Related

No existing duplicate issues or PRs found.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/securitySecurity related issue/prkind/bugSomething isn't working / bugfixes

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions