Repository navigation
CTM-679: mutual TLS for Galaxy VM connections - #4934
Conversation
Replaces trust-all with a CA-pinned SSLContext when GALAXY_TLS_CA_CERT, GALAXY_TLS_VM_CERT and GALAXY_TLS_VM_KEY env vars are set (from Secret Manager). Falls back to trust-all when absent (BEE / dev without provisioned secret). Changes: - GalaxyVmConfig gains tlsCaCert/tlsVmCert/tlsVmKey (all Option[String]) read from env vars via reference.conf substitution - BaselineDependenciesBuilder.buildGalaxySslContext: pins to the CA cert when present (X509ExtendedTrustManager wrapping the chain-verifying inner TM, bypassing hostname check since Galaxy VMs are addressed by IP), else trust-all - galaxySslContext shared between vmClient (HttpAppDAO) and ProxyService, removing the duplicate inline trust-all context from ProxyService - GKEInterpreter: injects galaxy-tls-cert / galaxy-tls-key instance metadata when VM cert+key are configured, for the playbook to use Ticket: https://broadworkbench.atlassian.net/browse/CTM-678 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GcpDependenciesBuilderSpec and MockProxyService were missing the new galaxySslContext parameter added to BaselineDependencies and ProxyService. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #4934 +/- ##
===========================================
- Coverage 73.71% 73.36% -0.35%
===========================================
Files 131 131
Lines 11263 11314 +51
Branches 933 927 -6
===========================================
- Hits 8303 8301 -2
- Misses 2960 3013 +53
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
Adds tlsAllowTrustAllFallback (GALAXY_TLS_ALLOW_TRUST_ALL_FALLBACK) to GalaxyVmConfig. Defaults to false: Leo fails at startup if tlsCaCert is absent, so a missing or removed Secret Manager secret is immediately visible rather than silently degrading to trust-all. BEEs set this to true via the helmfile since the leo-galaxy-tls secret is not provisioned there. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ning time Each Galaxy VM now gets a unique TLS cert+key signed by Leo's root CA and injected via GCE instance metadata at createInstance time (GKEInterpreter). A user reading their VM's metadata can only compromise their own connection, not other VMs. Changes: - Adds org.bouncycastle:bcpkix-jdk18on for cert generation; removes the global BouncyCastle exclusion (it was excluded as unused transitive dep) - Replaces tlsVmCert/tlsVmKey config fields with tlsCaKey (the CA private key Leo signs per-VM certs with) - GKEInterpreter.generateVmTlsCert signs a fresh 2048-bit RSA cert per VM - Startup validation: tlsCaCert and tlsCaKey must both be set or both absent Ticket: https://broadworkbench.atlassian.net/browse/CTM-678 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove trust-all fallback path; CA cert is now required at startup - Add Leo client cert+key (GALAXY_TLS_LEO_CLIENT_CERT/KEY) loaded into SSLContext KeyManager for mTLS handshake with Galaxy VMs - Inject galaxy_tls_client_ca into VM metadata so nginx can verify Leo - Fix metadata key names: hyphens -> underscores to match galaxy-k8s-boot PR #144 - Remove tlsAllowTrustAllFallback; BEEs now use real GSM secret (broad-dsde-qa) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… merge strategy bcpkix, bcprov, and bcutil 1.86 each contain META-INF/versions/9/OSGI-INF/MANIFEST.MF with different contents. The default deduplicate strategy fails; use first. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hard-throwing on absent GALAXY_TLS_CA_CERT prevents Leo from starting in environments where the leo-galaxy-tls Secret Manager secret has not been provisioned yet (e.g. BEEs before Terraform PR #2074 is deployed). Replace the throw with an SLF4J warning and fall back to SSLContext.getDefault. Galaxy VM connections will fail at the TLS handshake in those environments, but that is caught per-request and does not block startup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sarahgibs
left a comment
There was a problem hiding this comment.
Looks good to me. Thank you for getting this together so quickly.
| ) | ||
| // Previously excluded BouncyCastle (arrived transitively via io.kubernetes:client-java) | ||
| // but it is now explicitly depended on for per-VM Galaxy TLS cert generation. | ||
| val commonExcludes = List.empty[ExclusionRule] |
There was a problem hiding this comment.
nit, but you could probably just delete this exclude rule
| orchUrl: String, | ||
| drsUrl: String | ||
| drsUrl: String, | ||
| // Root-CA cert (PEM). Added to Leo's trust store for Galaxy VM connection validation. |
There was a problem hiding this comment.
nit, but these could be set together with something like GalaxyTlsConfig(caCert, caKey, leoClientCert, leoClientKey) to make it explicit that they must all be set together
lucymcnatt
left a comment
There was a problem hiding this comment.
left a couple nits, but overall LGTM
- Remove empty commonExcludes and its Settings.scala reference; BouncyCastle is now an explicit dependency so there is nothing to exclude transitively. - Introduce GalaxyTlsConfig grouping all four TLS secrets (caCert, caKey, leoClientCert, leoClientKey) into one type. The reader yields None when any field is missing, enforcing all-or-nothing semantics instead of partial state. Replace the four individual Option[String] fields in GalaxyVmConfig with tls: Option[GalaxyTlsConfig] and update all call sites. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BouncyCastle PEMParser yields PrivateKeyInfo for PKCS#8 keys (OpenSSL 3.x `BEGIN PRIVATE KEY`) and PEMKeyPair for traditional RSA keys (`BEGIN RSA PRIVATE KEY`). The cast to PEMKeyPair crashed on PKCS#8 input. Ticket: https://broadworkbench.atlassian.net/browse/CTM-678 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… TLS cert X500Name(String) re-encodes from RFC 2253, which can reorder RDNs and break PKIX issuer/subject byte matching. Use JcaX509CertificateHolder.getSubject to copy the CA cert's subject field verbatim into the server cert's issuer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Establishes mutual TLS between Leo and Galaxy VMs.
Server authentication (Leo → VM)
Leo validates each Galaxy VM's TLS certificate against a CA. At provisioning time, Leo signs a fresh RSA key pair for the VM using its CA key and injects the cert+key into GCE instance metadata as
galaxy_tls_cert/galaxy_tls_key. The galaxy-k8s-boot playbook reads these and configures nginx to serve them (galaxyproject/galaxy-k8s-boot#144).Client authentication (VM → Leo)
The Galaxy VM verifies that the connecting client is Leo. Leo injects
galaxy_tls_client_ca(the CA cert) into VM metadata so nginx can verify Leo's client cert. Leo presentsGALAXY_TLS_LEO_CLIENT_CERT/GALAXY_TLS_LEO_CLIENT_KEYon every connection via aKeyManagerin itsSSLContext.Startup behaviour when
GALAXY_TLS_CA_CERTis absentGALAXY_TLS_CA_CERTis optional at startup. If absent (e.g. BEEs before the Terraform secret is deployed), Leo logs a warning and falls back toSSLContext.getDefault. Leo starts normally; Galaxy VM connections fail at the TLS handshake per-request rather than blocking startup. Once the Terraform and Helmfile PRs are deployed the cert is present and full mTLS is enforced.Dependencies
bcpkix-jdk18on1.86 for per-VM cert generation (added explicitly; global exclusion rule removed)