Repository navigation
fix(auth): present the full certificate chain for mTLS - #14621
macastelaz wants to merge 6 commits into
Conversation
…kens and mTLS On GKE, agent identity credentials are delivered as a single combined PEM file (certificate chain + PKCS#8 private key) at /var/run/secrets/workload-spiffe-credentials/x509.credential-bundle.private-key.pem. When no certificate configuration exists (GOOGLE_API_CERTIFICATE_CONFIG unset and no ~/.config/gcloud/certificate_config.json), the presence of a readable, non-empty bundle now: - auto-enables mTLS on the client transport (MtlsUtils.useMtlsClientCertificate / getWorkloadCertPath, and the X509Provider created by DefaultMtlsProviderFactory), and - lets ComputeEngineCredentials request certificate-bound tokens with the same certificate (AgentIdentityUtils). GOOGLE_API_USE_CLIENT_CERTIFICATE=false and GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false still opt out. The bundle is never polled for. The GKE fallback is limited to the transport factory path, so workload identity federation X.509 credentials are unchanged. This mirrors googleapis/google-cloud-python#18559.
X509Provider and SecureConnectProvider built their key stores with google-http-client's SecurityUtils.createMtlsKeyStore, which keeps only the first certificate. Java therefore presented only the leaf during the TLS handshake. Google front ends accept that, but peers that trust only the root (for example, agent-to-agent mTLS with a SPIFFE trust bundle) cannot verify the leaf without the intermediate and reject the handshake. Build the key store with every certificate in the PEM input, keeping the first certificate as the leaf, as before.
There was a problem hiding this comment.
Code Review
This pull request introduces support for GKE credential bundles (Pod Certificates) as a fallback client certificate source for mTLS when no explicit certificate configuration is present. It adds MtlsKeyStoreUtils to build a KeyStore that preserves the full certificate chain, and updates MtlsUtils, X509Provider, and AgentIdentityUtils to handle the GKE credential bundle path and establish mTLS intent accordingly. The review feedback suggests two improvements in MtlsKeyStoreUtils: normalizing the "ECDSA" public key algorithm name to "EC" to prevent NoSuchAlgorithmException on standard JDKs, and explicitly calling keyStore.load(null, null) instead of keyStore.load(null) to avoid relying on implicit JDK fallback behavior.
- Choose the private key the same way as SecurityUtils.createMtlsKeyStore: a later key replaces an earlier one until the first certificate is seen. - Once a certificate and key are found, also treat a read error as the end of input, and leave out an extra certificate that fails with a runtime exception, so inputs the upstream method accepted keep working. - Javadoc: describe the differences from upstream and complete @throws; X509Provider.getKeyStore now returns the certificate chain. - Tests: compare key selection with upstream for keys before and after the certificates, pin that upstream keeps only the leaf, cover a key-first GKE bundle with an intermediate, and make a test independent of line endings.
Note
Stacked on #14595. Until #14595 merges, this diff also shows its two commits. Only the last two commits (
fix(auth): present the full certificate chain for mTLSandfix(auth): match upstream private key selection and harden chain parsing) are new here. Once #14595 merges, I'll merge the base branch in so the diff shows just this change.Why
X509ProviderandSecureConnectProviderbuild their mTLS key store with google-http-client'sSecurityUtils.createMtlsKeyStore. That method keeps only the firstCERTIFICATEin the PEM input, so Java presents only the leaf certificate in the TLS handshake. This is unchanged in the latest google-http-client release (2.2.0).*.mtls.googleapis.comare unaffected, because Google's front ends already know the intermediates.load_cert_chain).This bug predates the agent identity work; the agent identity testing caught it.
What changes
New package-private
MtlsKeyStoreUtils.createMtlsKeyStore(InputStream). It replaces the upstream method.PRIVATE KEY), which private key is chosen when there are several, and error messages.Used by:
X509Provider.getKeyStore(), on both the certificate-configuration path (Cloud Run, WIF X.509) and the GKE credential-bundle path;SecureConnectProvider.getKeyStore()(ECP).Inputs that worked before still work. Because more of the input is now parsed, once a certificate and a private key have been found (the point where upstream stopped reading):
Unchanged:
cnfbinding is still the leaf thumbprint, read from the certificate file;Known limitations (unchanged from before):
com.google.api.gax.rpc.mtls.MtlsProvider(ECP-only) still uses the upstream method. GAX's transports don't use it; they usecom.google.auth.mtls.MtlsProviderthroughDefaultMtlsProviderFactory.Tests
Unit tests
MtlsKeyStoreUtilsTest(new), using a generated test chain (root → intermediate → leaf, EC P-256, long validity) intestresources/mtls/test_chain_{cert,key}.pem:SecurityUtils.createMtlsKeyStorekeeps only the leaf for the same input. This pins down the bug, and the test fails if upstream ever fixes it;SecurityUtils.createMtlsKeyStore;X509ProviderTest: a GKE bundle with an intermediate (certificates first, and key first as GKE writes it), and a certificate config whosecert_pathholds a chain, all give leaf + intermediate.SecureConnectProviderTest: helper output with a chain gives leaf + intermediate.The provider tests fail without the fix (
expected: <2> but was: <1>). Fulloauth2_httpsuite: 1107 tests, 0 failures.fmtis clean.Live tests
DefaultMtlsProviderFactorykey store, against a receiver that trusts only the SPIFFE rootBroken pipe(handshake rejected)storage.mtls.googleapis.com, GAX clients over gRPC and HTTP/JSON, bound tokenscert_pathcert_pathThe certificates sent were confirmed from the TLS handshake debug log. The live tests ran on the first commit (
b2876a4d3a9). The second commit only changes how unusual or malformed inputs are handled, plus tests, so well-formed GKE and WIF files behave the same.Internal links (Googlers only): live test results · GKE setup, manifests and harness (shared with #14595)