This is an automated email from the ASF dual-hosted git repository. pcongiusti pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit c2ef248b370aabf7801366ab67a5806adb758a00 Author: Pranjul Kalsi <[email protected]> AuthorDate: Sun Jan 18 11:35:28 2026 +0530 fix(jvm): simplify CA certificate add process by removing per-cert PasswordPath and cleanup tests --- docs/modules/ROOT/partials/apis/camel-k-crds.adoc | 16 +- docs/modules/traits/pages/jvm.adoc | 63 +++- e2e/common/traits/jvm_test.go | 33 +- helm/camel-k/crds/camel-k-crds.yaml | 104 ++++--- pkg/apis/camel/v1/trait/jvm.go | 6 +- .../camel.apache.org_integrationplatforms.yaml | 26 +- .../camel.apache.org_integrationprofiles.yaml | 26 +- .../crd/bases/camel.apache.org_integrations.yaml | 26 +- .../config/crd/bases/camel.apache.org_pipes.yaml | 26 +- pkg/trait/init_containers.go | 25 +- pkg/trait/init_containers_test.go | 44 +-- pkg/trait/jvm.go | 15 +- pkg/trait/jvm_cacert.go | 45 +-- pkg/trait/jvm_test.go | 335 +++++++++++++++++++-- 14 files changed, 571 insertions(+), 219 deletions(-) diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc index f34bf5175..b14d94b53 100644 --- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc +++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc @@ -6666,7 +6666,7 @@ The list of manifest platforms to use to build a container image (default `linux * <<#_camel_apache_org_v1_trait_JVMTrait, JVMTrait>> -CACertConfig associates a CA certificate with its password file. +CACertConfig specifies a CA certificate to import into the truststore. [cols="2,2a",options="header"] |=== @@ -6680,13 +6680,6 @@ string Path to the PEM-encoded CA certificate file to import. -|`passwordPath` + -string -| - - -Path to a file containing the password for importing this certificate. - |=== @@ -7912,6 +7905,13 @@ A list of CA certificates to import into the truststore. Certificates must be mo Optional base truststore to use as the starting point for adding certificates. +|`truststorePasswordPath` + +string +| + + +Path to a file containing the password for the generated truststore. Required when using ca-certificates without base-truststore. + |`caCertMountPath` + string | diff --git a/docs/modules/traits/pages/jvm.adoc b/docs/modules/traits/pages/jvm.adoc index 34f128d9f..f60e1d654 100755 --- a/docs/modules/traits/pages/jvm.adoc +++ b/docs/modules/traits/pages/jvm.adoc @@ -71,6 +71,10 @@ Deprecated: no longer in use. | github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait.BaseTruststore | Optional base truststore to use as the starting point for adding certificates. +| jvm.truststore-password-path +| string +| Path to a file containing the password for the generated truststore. Required when using ca-certificates without base-truststore. + | jvm.ca-cert-mount-path | string | The path where the generated truststore will be mounted (default `/etc/camel/conf.d/_truststore`). @@ -118,7 +122,9 @@ $ kamel run --resource configmap:my-dep -t jvm.classpath=/etc/camel/resources/my == Trusting Custom CA Certificates -When connecting to services that use TLS with certificates signed by a private CA (e.g., internal Elasticsearch, Kafka, or databases), you can use the `ca-cert` option to add the CA certificate to the JVM's truststore. +When connecting to services that use TLS with certificates signed by a private CA (e.g., internal Elasticsearch, Kafka, or databases), you can use the `ca-certificates` option to add CA certificates to the JVM's truststore. + +=== Single Certificate First, create Kubernetes Secrets containing the CA certificate and truststore password: @@ -135,25 +141,66 @@ Then mount the secrets using the mount trait and reference the file paths: $ kamel run MyRoute.java \ -t mount.configs=secret:my-private-ca \ -t mount.configs=secret:my-truststore-pwd \ - -t jvm.ca-cert=/etc/camel/conf.d/_secrets/my-private-ca/ca.crt \ - -t jvm.ca-cert-password=/etc/camel/conf.d/_secrets/my-truststore-pwd/password + -t jvm.ca-certificates[0].cert-path=/etc/camel/conf.d/_secrets/my-private-ca/ca.crt \ + -t jvm.truststore-password-path=/etc/camel/conf.d/_secrets/my-truststore-pwd/password ---- -If your secret uses a different key name for the certificate: +=== Multiple Certificates + +You can add multiple CA certificates to the truststore: + +[source,console] +---- +$ kamel run MyRoute.java \ + -t mount.configs=secret:ca1 \ + -t mount.configs=secret:ca2 \ + -t mount.configs=secret:truststore-pwd \ + -t jvm.ca-certificates[0].cert-path=/etc/camel/conf.d/_secrets/ca1/ca.crt \ + -t jvm.ca-certificates[1].cert-path=/etc/camel/conf.d/_secrets/ca2/ca.crt \ + -t jvm.truststore-password-path=/etc/camel/conf.d/_secrets/truststore-pwd/password +---- + +=== Using a Base Truststore (Preserving JDK Public CAs) + +To preserve the JDK's default public CA certificates while adding your custom certificates, use the `base-truststore` option: [source,console] ---- $ kamel run MyRoute.java \ -t mount.configs=secret:my-private-ca \ - -t mount.configs=secret:my-truststore-pwd \ - -t jvm.ca-cert=/etc/camel/conf.d/_secrets/my-private-ca/custom-ca.pem \ - -t jvm.ca-cert-password=/etc/camel/conf.d/_secrets/my-truststore-pwd/password + -t mount.configs=secret:cacerts-pwd \ + -t jvm.base-truststore.truststore-path=/opt/java/openjdk/lib/security/cacerts \ + -t jvm.base-truststore.password-path=/etc/camel/conf.d/_secrets/cacerts-pwd/password \ + -t jvm.ca-certificates[0].cert-path=/etc/camel/conf.d/_secrets/my-private-ca/ca.crt ---- +NOTE: When using `base-truststore`, you can optionally provide `truststore-password-path` to set a different password for the output truststore. If not provided, the base truststore password is used. + +=== Truststore Password Resolution + +The truststore password is determined using this priority: + +1. `truststore-password-path` (if explicitly provided) +2. `base-truststore.password-path` (if base-truststore is configured) +3. Validation error (password is required when using `ca-certificates`) + This will automatically: 1. Mount the secrets to the integration container (via mount trait) 2. Generate a JVM truststore using an init container 3. Configure the JVM to use the generated truststore via `-Djavax.net.ssl.trustStore` -NOTE: The `ca-cert-password` option is **required** when using `ca-cert`. Both values must be file paths to the mounted secrets. +=== Deprecated Syntax (Backward Compatible) + +The legacy `ca-cert` and `ca-cert-password` options are still supported but deprecated: + +[source,console] +---- +$ kamel run MyRoute.java \ + -t mount.configs=secret:my-private-ca \ + -t mount.configs=secret:my-truststore-pwd \ + -t jvm.ca-cert=/etc/camel/conf.d/_secrets/my-private-ca/ca.crt \ + -t jvm.ca-cert-password=/etc/camel/conf.d/_secrets/my-truststore-pwd/password +---- + +NOTE: We recommend migrating to the new `ca-certificates` syntax for better multi-certificate support and explicit truststore password configuration. diff --git a/e2e/common/traits/jvm_test.go b/e2e/common/traits/jvm_test.go index e4557b739..7da1acf28 100644 --- a/e2e/common/traits/jvm_test.go +++ b/e2e/common/traits/jvm_test.go @@ -92,36 +92,38 @@ func TestJVMTrait(t *testing.T) { }) t.Run("JVM trait multiple CA certs", func(t *testing.T) { - // Test the new ca-certificates field with multiple certificates, each with its own password + // Test the new ca-certificates field with multiple certificates cert1Pem, err := generateSelfSignedCert() require.NoError(t, err) cert2Pem, err := generateSelfSignedCert() require.NoError(t, err) - // Create secrets with both cert and password together + // Create secrets with certificates caCert1Data := make(map[string]string) caCert1Data["ca.crt"] = string(cert1Pem) - caCert1Data["password"] = "test-password-1" err = CreatePlainTextSecret(t, ctx, ns, "test-ca-cert-1", caCert1Data) require.NoError(t, err) caCert2Data := make(map[string]string) caCert2Data["ca.crt"] = string(cert2Pem) - caCert2Data["password"] = "test-password-2" err = CreatePlainTextSecret(t, ctx, ns, "test-ca-cert-2", caCert2Data) require.NoError(t, err) + truststorePassData := make(map[string]string) + truststorePassData["password"] = "truststore-password" + err = CreatePlainTextSecret(t, ctx, ns, "truststore-pass-multi", truststorePassData) + require.NoError(t, err) + name := RandomizedSuffixName("multicacert") g.Expect(KamelRun(t, ctx, ns, "./files/Java.java", "--name", name, "-t", "mount.configs=secret:test-ca-cert-1", "-t", "mount.configs=secret:test-ca-cert-2", - // Using new ca-certificates field: each certificate with its own password path + "-t", "mount.configs=secret:truststore-pass-multi", "-t", "jvm.ca-certificates[0].cert-path=/etc/camel/conf.d/_secrets/test-ca-cert-1/ca.crt", - "-t", "jvm.ca-certificates[0].password-path=/etc/camel/conf.d/_secrets/test-ca-cert-1/password", "-t", "jvm.ca-certificates[1].cert-path=/etc/camel/conf.d/_secrets/test-ca-cert-2/ca.crt", - "-t", "jvm.ca-certificates[1].password-path=/etc/camel/conf.d/_secrets/test-ca-cert-2/password", + "-t", "jvm.truststore-password-path=/etc/camel/conf.d/_secrets/truststore-pass-multi/password", ).Execute()).To(Succeed()) g.Eventually(IntegrationPodPhase(t, ctx, ns, name), TestTimeoutLong*2).Should(Equal(corev1.PodRunning)) @@ -141,10 +143,9 @@ func TestJVMTrait(t *testing.T) { certPem, err := generateSelfSignedCert() require.NoError(t, err) - // Create secret with cert and password + // Create secret with certificate caCertData := make(map[string]string) caCertData["ca.crt"] = string(certPem) - caCertData["password"] = "test-password-456" err = CreatePlainTextSecret(t, ctx, ns, "test-ca-sys", caCertData) require.NoError(t, err) @@ -160,11 +161,9 @@ func TestJVMTrait(t *testing.T) { "--name", name, "-t", "mount.configs=secret:test-ca-sys", "-t", "mount.configs=secret:base-ts-password", - // Using new base-truststore field with JDK cacerts as base "-t", "jvm.base-truststore.truststore-path=/opt/java/openjdk/lib/security/cacerts", "-t", "jvm.base-truststore.password-path=/etc/camel/conf.d/_secrets/base-ts-password/password", "-t", "jvm.ca-certificates[0].cert-path=/etc/camel/conf.d/_secrets/test-ca-sys/ca.crt", - "-t", "jvm.ca-certificates[0].password-path=/etc/camel/conf.d/_secrets/test-ca-sys/password", ).Execute()).To(Succeed()) g.Eventually(IntegrationPodPhase(t, ctx, ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) @@ -184,21 +183,25 @@ func TestJVMTrait(t *testing.T) { certPem, err := generateSelfSignedCert() require.NoError(t, err) - // Create secret with both cert and password + // Create secret with certificate caCertData := make(map[string]string) caCertData["ca.crt"] = string(certPem) - caCertData["password"] = "changeit" err = CreatePlainTextSecret(t, ctx, ns, "test-ca-single", caCertData) require.NoError(t, err) + truststorePassData := make(map[string]string) + truststorePassData["password"] = "truststore-password" + err = CreatePlainTextSecret(t, ctx, ns, "truststore-pass-single", truststorePassData) + require.NoError(t, err) + name := RandomizedSuffixName("singlecert") g.Expect(KamelRun(t, ctx, ns, "./files/Java.java", "--name", name, "-t", "mount.configs=secret:test-ca-single", - // Using new ca-certificates field with explicit password + "-t", "mount.configs=secret:truststore-pass-single", "-t", "jvm.ca-certificates[0].cert-path=/etc/camel/conf.d/_secrets/test-ca-single/ca.crt", - "-t", "jvm.ca-certificates[0].password-path=/etc/camel/conf.d/_secrets/test-ca-single/password", + "-t", "jvm.truststore-password-path=/etc/camel/conf.d/_secrets/truststore-pass-single/password", ).Execute()).To(Succeed()) g.Eventually(IntegrationPodPhase(t, ctx, ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) diff --git a/helm/camel-k/crds/camel-k-crds.yaml b/helm/camel-k/crds/camel-k-crds.yaml index bb582cbac..9cbf81416 100644 --- a/helm/camel-k/crds/camel-k-crds.yaml +++ b/helm/camel-k/crds/camel-k-crds.yaml @@ -4803,17 +4803,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -4858,6 +4854,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -7311,17 +7312,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -7366,6 +7363,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -9719,17 +9721,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -9774,6 +9772,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -12104,17 +12107,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -12159,6 +12158,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -21347,17 +21351,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -21402,6 +21402,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -23688,17 +23693,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -23743,6 +23744,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -34296,17 +34302,13 @@ spec: the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate - with its password file. + description: CACertConfig specifies a CA certificate + to import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -34351,6 +34353,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for + the generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -36567,17 +36574,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -36622,6 +36625,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait diff --git a/pkg/apis/camel/v1/trait/jvm.go b/pkg/apis/camel/v1/trait/jvm.go index 7c5d904c9..6adaa572d 100644 --- a/pkg/apis/camel/v1/trait/jvm.go +++ b/pkg/apis/camel/v1/trait/jvm.go @@ -17,12 +17,10 @@ limitations under the License. package trait -// CACertConfig associates a CA certificate with its password file. +// CACertConfig specifies a CA certificate to import into the truststore. type CACertConfig struct { // Path to the PEM-encoded CA certificate file to import. CertPath string `json:"certPath,omitempty" property:"cert-path"` - // Path to a file containing the password for importing this certificate. - PasswordPath string `json:"passwordPath,omitempty" property:"password-path"` } // BaseTruststore represents an existing truststore to use as the base for adding certificates. @@ -65,6 +63,8 @@ type JVMTrait struct { CACertificates []CACertConfig `json:"caCertificates,omitempty" property:"ca-certificates"` // Optional base truststore to use as the starting point for adding certificates. BaseTruststore *BaseTruststore `json:"baseTruststore,omitempty" property:"base-truststore"` + // Path to a file containing the password for the generated truststore. Required when using ca-certificates without base-truststore. + TruststorePasswordPath string `json:"truststorePasswordPath,omitempty" property:"truststore-password-path"` // The path where the generated truststore will be mounted (default `/etc/camel/conf.d/_truststore`). CACertMountPath string `json:"caCertMountPath,omitempty" property:"ca-cert-mount-path"` // Deprecated: Use CACertificates instead. Path to a PEM-encoded CA certificate file. diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml index 3bd65993a..0f73c3592 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml @@ -1529,17 +1529,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -1584,6 +1580,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -4037,17 +4038,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -4092,6 +4089,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml index 4cc3ea37f..471165280 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml @@ -1395,17 +1395,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -1450,6 +1446,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -3780,17 +3781,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -3835,6 +3832,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml index e9d7b0178..c89bf48de 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml @@ -8233,17 +8233,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -8288,6 +8284,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -10574,17 +10575,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -10629,6 +10626,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait diff --git a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml index a183f0500..f6c7187eb 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml @@ -8290,17 +8290,13 @@ spec: the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate - with its password file. + description: CACertConfig specifies a CA certificate + to import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -8345,6 +8341,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for + the generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait @@ -10561,17 +10562,13 @@ spec: description: A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. items: - description: CACertConfig associates a CA certificate with - its password file. + description: CACertConfig specifies a CA certificate to + import into the truststore. properties: certPath: description: Path to the PEM-encoded CA certificate file to import. type: string - passwordPath: - description: Path to a file containing the password - for importing this certificate. - type: string type: object type: array classpath: @@ -10616,6 +10613,11 @@ spec: Deprecated: no longer in use. type: boolean + truststorePasswordPath: + description: Path to a file containing the password for the + generated truststore. Required when using ca-certificates + without base-truststore. + type: string type: object kamelets: description: The configuration of Kamelets trait diff --git a/pkg/trait/init_containers.go b/pkg/trait/init_containers.go index a1e7074ba..57330f111 100644 --- a/pkg/trait/init_containers.go +++ b/pkg/trait/init_containers.go @@ -91,33 +91,38 @@ func (t *initContainersTrait) Configure(e *Environment) (bool, *TraitCondition, } t.tasks = append(t.tasks, agentDownloadTask) } - // Set the CA cert truststore init container if configured if ok && jvm.hasCACerts() { + if err := jvm.validateCACertConfig(); err != nil { + return false, nil, err + } + var allCommands []string + importPassPath := jvm.getTruststorePasswordPath() - var truststorePassPath string if jvm.hasBaseTruststore() { baseTruststore := jvm.getBaseTruststore() - truststorePassPath = baseTruststore.PasswordPath copyCmd := fmt.Sprintf("cp %s %s", baseTruststore.TruststorePath, jvm.getTrustStorePath()) allCommands = append(allCommands, copyCmd) - } else { - certEntries := jvm.getAllCACertEntries() - if len(certEntries) > 0 { - truststorePassPath = certEntries[0].PasswordPath - } + importPassPath = baseTruststore.PasswordPath } for i, entry := range jvm.getAllCACertEntries() { cmd := fmt.Sprintf( "keytool -importcert -noprompt -alias custom-ca-%d -storepass:file %s -keystore %s -file %s", - i, truststorePassPath, jvm.getTrustStorePath(), entry.CertPath, + i, importPassPath, jvm.getTrustStorePath(), entry.CertPath, ) allCommands = append(allCommands, cmd) } + if jvm.hasBaseTruststore() && jvm.TruststorePasswordPath != "" { + storepasswdCmd := fmt.Sprintf( + "keytool -storepasswd -keystore %s -storepass:file %s -new \"$(cat %s)\"", + jvm.getTrustStorePath(), jvm.getBaseTruststore().PasswordPath, jvm.TruststorePasswordPath, + ) + allCommands = append(allCommands, storepasswdCmd) + } + fullCommand := strings.Join(allCommands, " && ") - // Wrap in bash shell when there are multiple commands or shell features are used if len(allCommands) > 1 || jvm.hasBaseTruststore() { fullCommand = fmt.Sprintf("/bin/bash -c \"%s\"", fullCommand) } diff --git a/pkg/trait/init_containers_test.go b/pkg/trait/init_containers_test.go index e2fa15b90..eb7b4d540 100644 --- a/pkg/trait/init_containers_test.go +++ b/pkg/trait/init_containers_test.go @@ -395,11 +395,9 @@ func TestApplyInitContainerWithCACert(t *testing.T) { Traits: v1.Traits{ JVM: &trait.JVMTrait{ CACertificates: []trait.CACertConfig{ - { - CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt", - PasswordPath: "/etc/camel/conf.d/_secrets/truststore-pass/password", - }, + {CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt"}, }, + TruststorePasswordPath: "/etc/camel/conf.d/_secrets/truststore-pass/password", }, }, }, @@ -468,19 +466,11 @@ func TestApplyInitContainerWithMultipleCACerts(t *testing.T) { Traits: v1.Traits{ JVM: &trait.JVMTrait{ CACertificates: []trait.CACertConfig{ - { - CertPath: "/etc/camel/conf.d/_secrets/ca1/ca.crt", - PasswordPath: "/etc/camel/conf.d/_secrets/pass1/password", - }, - { - CertPath: "/etc/camel/conf.d/_secrets/ca2/ca.crt", - PasswordPath: "/etc/camel/conf.d/_secrets/pass2/password", - }, - { - CertPath: "/etc/camel/conf.d/_secrets/ca3/ca.crt", - PasswordPath: "/etc/camel/conf.d/_secrets/pass3/password", - }, + {CertPath: "/etc/camel/conf.d/_secrets/ca1/ca.crt"}, + {CertPath: "/etc/camel/conf.d/_secrets/ca2/ca.crt"}, + {CertPath: "/etc/camel/conf.d/_secrets/ca3/ca.crt"}, }, + TruststorePasswordPath: "/etc/camel/conf.d/_secrets/truststore/password", }, }, }, @@ -527,11 +517,8 @@ func TestApplyInitContainerWithMultipleCACerts(t *testing.T) { assert.Contains(t, commandStr, "/etc/camel/conf.d/_secrets/ca3/ca.crt") assert.Contains(t, commandStr, "&&") - firstPasswordCount := strings.Count(commandStr, "/etc/camel/conf.d/_secrets/pass1/password") - assert.Equal(t, 3, firstPasswordCount, "All 3 keytool commands should use the first certificate's password") - - assert.NotContains(t, commandStr, "storepass:file /etc/camel/conf.d/_secrets/pass2/password") - assert.NotContains(t, commandStr, "storepass:file /etc/camel/conf.d/_secrets/pass3/password") + truststorePasswordCount := strings.Count(commandStr, "/etc/camel/conf.d/_secrets/truststore/password") + assert.Equal(t, 3, truststorePasswordCount, "All 3 keytool commands should use the truststore password") } func TestApplyInitContainerWithCACertsBackwardCompatibility(t *testing.T) { @@ -564,13 +551,11 @@ func TestApplyInitContainerWithCACertsBackwardCompatibility(t *testing.T) { Traits: v1.Traits{ JVM: &trait.JVMTrait{ CACertificates: []trait.CACertConfig{ - { - CertPath: "/etc/camel/conf.d/_secrets/ca1/ca.crt", - PasswordPath: "/etc/camel/conf.d/_secrets/pass1/password", - }, + {CertPath: "/etc/camel/conf.d/_secrets/ca1/ca.crt"}, }, - CACert: "/etc/camel/conf.d/_secrets/ca2/ca.crt", - CACertPassword: "/etc/camel/conf.d/_secrets/pass2/password", + TruststorePasswordPath: "/etc/camel/conf.d/_secrets/truststore/password", + CACert: "/etc/camel/conf.d/_secrets/ca2/ca.crt", + CACertPassword: "/etc/camel/conf.d/_secrets/pass2/password", }, }, }, @@ -639,10 +624,7 @@ func TestApplyInitContainerWithBaseTruststore(t *testing.T) { Traits: v1.Traits{ JVM: &trait.JVMTrait{ CACertificates: []trait.CACertConfig{ - { - CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt", - PasswordPath: "/etc/camel/conf.d/_secrets/truststore-pass/password", - }, + {CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt"}, }, BaseTruststore: &trait.BaseTruststore{ TruststorePath: "/opt/java/openjdk/lib/security/cacerts", diff --git a/pkg/trait/jvm.go b/pkg/trait/jvm.go index fcfd29bda..3c2729e8a 100644 --- a/pkg/trait/jvm.go +++ b/pkg/trait/jvm.go @@ -398,18 +398,9 @@ func (t *jvmTrait) configureCaCert() []string { return nil } - // Determine which password to use for the truststore: - // If base truststore exists, use its password (we keep the original truststore password) - // Otherwise, use the first certificate's password - var truststorePassPath string - if t.hasBaseTruststore() { - truststorePassPath = t.getBaseTruststore().PasswordPath - } else { - entries := t.getAllCACertEntries() - if len(entries) == 0 { - return nil - } - truststorePassPath = entries[0].PasswordPath + truststorePassPath := t.getTruststorePasswordPath() + if truststorePassPath == "" { + return nil } return []string{ diff --git a/pkg/trait/jvm_cacert.go b/pkg/trait/jvm_cacert.go index 1c5112653..2b71c7cce 100644 --- a/pkg/trait/jvm_cacert.go +++ b/pkg/trait/jvm_cacert.go @@ -30,10 +30,8 @@ const ( trustStoreName = "truststore.jks" ) -// CACertEntry represents a resolved CA certificate configuration. type CACertEntry struct { - CertPath string - PasswordPath string + CertPath string } // hasCACerts returns true if any CA certificates are configured. @@ -59,21 +57,32 @@ func (t *jvmTrait) hasBaseTruststore() bool { return t.BaseTruststore != nil && t.BaseTruststore.TruststorePath != "" && t.BaseTruststore.PasswordPath != "" } +func (t *jvmTrait) getTruststorePasswordPath() string { + if t.TruststorePasswordPath != "" { + return t.TruststorePasswordPath + } + if t.hasBaseTruststore() { + return t.BaseTruststore.PasswordPath + } + //nolint:staticcheck + if t.CACert != "" && t.CACertPassword != "" { + return t.CACertPassword + } + + return "" +} + // getBaseTruststore returns the base truststore configuration if set. func (t *jvmTrait) getBaseTruststore() *traitv1.BaseTruststore { return t.BaseTruststore } -// getAllCACertEntries returns all configured CA certificate entries. func (t *jvmTrait) getAllCACertEntries() []CACertEntry { var entries []CACertEntry for _, cert := range t.CACertificates { - if cert.CertPath != "" && cert.PasswordPath != "" { - entries = append(entries, CACertEntry{ - CertPath: cert.CertPath, - PasswordPath: cert.PasswordPath, - }) + if cert.CertPath != "" { + entries = append(entries, CACertEntry{CertPath: cert.CertPath}) } } @@ -89,23 +98,18 @@ func (t *jvmTrait) getAllCACertEntries() []CACertEntry { } } if !found { - entries = append(entries, CACertEntry{ - //nolint:staticcheck - CertPath: t.CACert, - //nolint:staticcheck - PasswordPath: t.CACertPassword, - }) + //nolint:staticcheck + entries = append(entries, CACertEntry{CertPath: t.CACert}) } } return entries } -// validateCACertConfig validates the CA certificate configuration. func (t *jvmTrait) validateCACertConfig() error { for i, cert := range t.CACertificates { - if cert.CertPath == "" || cert.PasswordPath == "" { - return fmt.Errorf("CACertificates[%d]: both cert-path and password-path are required", i) + if cert.CertPath == "" { + return fmt.Errorf("CACertificates[%d]: cert-path is required", i) } } @@ -122,5 +126,10 @@ func (t *jvmTrait) validateCACertConfig() error { } } + // For new CACertificates field, require explicit truststore password or base truststore + if len(t.CACertificates) > 0 && t.TruststorePasswordPath == "" && !t.hasBaseTruststore() { + return errors.New("truststore-password-path is required when using ca-certificates without base-truststore") + } + return nil } diff --git a/pkg/trait/jvm_test.go b/pkg/trait/jvm_test.go index 37ae9633e..7d5e654c2 100644 --- a/pkg/trait/jvm_test.go +++ b/pkg/trait/jvm_test.go @@ -723,11 +723,9 @@ func TestApplyJvmTraitAgentFail(t *testing.T) { func TestApplyJvmTraitWithCACert(t *testing.T) { trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) trait.CACertificates = []traitv1.CACertConfig{ - { - CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt", - PasswordPath: "/etc/camel/conf.d/_secrets/truststore-pass/password", - }, + {CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt"}, } + trait.TruststorePasswordPath = "/etc/camel/conf.d/_secrets/truststore-pass/password" d := appsv1.Deployment{ Spec: appsv1.DeploymentSpec{ @@ -759,11 +757,9 @@ func TestApplyJvmTraitWithCACert(t *testing.T) { func TestApplyJvmTraitWithCustomCACertMountPath(t *testing.T) { trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) trait.CACertificates = []traitv1.CACertConfig{ - { - CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt", - PasswordPath: "/etc/camel/conf.d/_secrets/truststore-pass/password", - }, + {CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt"}, } + trait.TruststorePasswordPath = "/etc/camel/conf.d/_secrets/truststore-pass/password" trait.CACertMountPath = "/custom/truststore/path" d := appsv1.Deployment{ @@ -797,17 +793,15 @@ func TestGetAllCACertEntries(t *testing.T) { trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) trait.CACertificates = []traitv1.CACertConfig{ - {CertPath: "/path/to/ca1.crt", PasswordPath: "/path/to/pass1"}, - {CertPath: "/path/to/ca2.crt", PasswordPath: "/path/to/pass2"}, + {CertPath: "/path/to/ca1.crt"}, + {CertPath: "/path/to/ca2.crt"}, } trait.CACert = "" trait.CACertPassword = "" entries := trait.getAllCACertEntries() assert.Len(t, entries, 2) assert.Equal(t, "/path/to/ca1.crt", entries[0].CertPath) - assert.Equal(t, "/path/to/pass1", entries[0].PasswordPath) assert.Equal(t, "/path/to/ca2.crt", entries[1].CertPath) - assert.Equal(t, "/path/to/pass2", entries[1].PasswordPath) trait.CACertificates = nil trait.CACert = "/path/to/legacy.crt" @@ -815,10 +809,9 @@ func TestGetAllCACertEntries(t *testing.T) { entries = trait.getAllCACertEntries() assert.Len(t, entries, 1) assert.Equal(t, "/path/to/legacy.crt", entries[0].CertPath) - assert.Equal(t, "/path/to/legacy-pass", entries[0].PasswordPath) trait.CACertificates = []traitv1.CACertConfig{ - {CertPath: "/path/to/ca1.crt", PasswordPath: "/path/to/pass1"}, + {CertPath: "/path/to/ca1.crt"}, } trait.CACert = "/path/to/ca2.crt" trait.CACertPassword = "/path/to/pass2" @@ -840,7 +833,7 @@ func TestHasCACerts(t *testing.T) { assert.False(t, trait.hasCACerts()) trait.CACertificates = []traitv1.CACertConfig{ - {CertPath: "/path/to/ca1.crt", PasswordPath: "/path/to/pass1"}, + {CertPath: "/path/to/ca1.crt"}, } trait.CACert = "" assert.True(t, trait.hasCACerts()) @@ -850,7 +843,7 @@ func TestHasCACerts(t *testing.T) { assert.True(t, trait.hasCACerts()) trait.CACertificates = []traitv1.CACertConfig{ - {CertPath: "/path/to/ca1.crt", PasswordPath: "/path/to/pass1"}, + {CertPath: "/path/to/ca1.crt"}, } trait.CACert = "/path/to/ca2.crt" assert.True(t, trait.hasCACerts()) @@ -882,6 +875,61 @@ func TestHasBaseTruststore(t *testing.T) { assert.True(t, trait.hasBaseTruststore()) } +func TestGetTruststorePasswordPath(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + trait.CACert = "" + trait.CACertPassword = "" + assert.Equal(t, "", trait.getTruststorePasswordPath()) + + trait.TruststorePasswordPath = "/path/to/user-provided-pass" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + PasswordPath: "/path/to/base-pass", + } + trait.CACert = "/path/to/legacy.crt" + trait.CACertPassword = "/path/to/legacy-pass" + assert.Equal(t, "/path/to/user-provided-pass", trait.getTruststorePasswordPath()) + + trait.TruststorePasswordPath = "/path/to/user-provided-pass" + trait.BaseTruststore = nil + trait.CACert = "/path/to/legacy.crt" + trait.CACertPassword = "/path/to/legacy-pass" + assert.Equal(t, "/path/to/user-provided-pass", trait.getTruststorePasswordPath()) + + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + trait.CACert = "/path/to/legacy.crt" + trait.CACertPassword = "/path/to/legacy-pass" + assert.Equal(t, "/path/to/legacy-pass", trait.getTruststorePasswordPath()) + + trait.TruststorePasswordPath = "/path/to/user-provided-pass" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + } + trait.CACert = "" + trait.CACertPassword = "" + assert.Equal(t, "/path/to/user-provided-pass", trait.getTruststorePasswordPath()) + + trait.TruststorePasswordPath = "" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + } + trait.CACert = "/path/to/legacy.crt" + trait.CACertPassword = "/path/to/legacy-pass" + assert.Equal(t, "/path/to/legacy-pass", trait.getTruststorePasswordPath()) + + trait.TruststorePasswordPath = "" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + } + trait.CACert = "" + trait.CACertPassword = "" + assert.Equal(t, "", trait.getTruststorePasswordPath()) +} + func TestValidateCACertConfig(t *testing.T) { trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) @@ -889,21 +937,54 @@ func TestValidateCACertConfig(t *testing.T) { trait.CACert = "" trait.CACertPassword = "" trait.BaseTruststore = nil + trait.TruststorePasswordPath = "" + assert.NoError(t, trait.validateCACertConfig()) + + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/ca.crt"}, + } + trait.TruststorePasswordPath = "/path/to/truststore-pass" + assert.NoError(t, trait.validateCACertConfig()) + + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/ca.crt"}, + } + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + assert.Error(t, trait.validateCACertConfig()) + assert.Contains(t, trait.validateCACertConfig().Error(), "truststore-password-path is required") + + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/ca.crt"}, + } + trait.TruststorePasswordPath = "" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + PasswordPath: "/path/to/base-pass", + } assert.NoError(t, trait.validateCACertConfig()) trait.CACertificates = []traitv1.CACertConfig{ - {CertPath: "/path/to/ca.crt", PasswordPath: "/path/to/pass"}, + {CertPath: "/path/to/ca.crt"}, } + trait.TruststorePasswordPath = "/path/to/truststore-pass" + trait.BaseTruststore = nil assert.NoError(t, trait.validateCACertConfig()) trait.CACertificates = []traitv1.CACertConfig{ - {CertPath: "/path/to/ca.crt", PasswordPath: ""}, + {CertPath: "/path/to/ca.crt"}, } + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + trait.CACert = "/path/to/legacy.crt" + trait.CACertPassword = "/path/to/legacy-pass" assert.Error(t, trait.validateCACertConfig()) + assert.Contains(t, trait.validateCACertConfig().Error(), "truststore-password-path is required") trait.CACertificates = nil trait.CACert = "/path/to/ca.crt" trait.CACertPassword = "" + trait.TruststorePasswordPath = "" assert.Error(t, trait.validateCACertConfig()) trait.CACert = "/path/to/ca.crt" @@ -923,3 +1004,221 @@ func TestValidateCACertConfig(t *testing.T) { } assert.NoError(t, trait.validateCACertConfig()) } + +func TestAllCACertScenarios(t *testing.T) { + t.Run("Scenario1_NothingConfigured", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = nil + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + trait.CACert = "" + trait.CACertPassword = "" + + assert.False(t, trait.hasCACerts()) + assert.Equal(t, "", trait.getTruststorePasswordPath()) + }) + + t.Run("Scenario2_CACertificatesNoPassword", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/cert"}, + } + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + trait.CACert = "" + trait.CACertPassword = "" + + assert.True(t, trait.hasCACerts()) + assert.Equal(t, "", trait.getTruststorePasswordPath()) + err := trait.validateCACertConfig() + assert.Error(t, err) + assert.Contains(t, err.Error(), "truststore-password-path is required") + }) + + t.Run("Scenario3_CACertificatesWithTruststorePassword", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/cert"}, + } + trait.TruststorePasswordPath = "/path/to/ts-pass" + trait.BaseTruststore = nil + trait.CACert = "" + trait.CACertPassword = "" + + assert.True(t, trait.hasCACerts()) + assert.Equal(t, "/path/to/ts-pass", trait.getTruststorePasswordPath()) + assert.NoError(t, trait.validateCACertConfig()) + }) + + t.Run("Scenario4_CACertificatesWithBaseTruststore", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/cert"}, + } + trait.TruststorePasswordPath = "" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + PasswordPath: "/path/to/base-pass", + } + trait.CACert = "" + trait.CACertPassword = "" + + assert.True(t, trait.hasCACerts()) + assert.True(t, trait.hasBaseTruststore()) + assert.Equal(t, "/path/to/base-pass", trait.getTruststorePasswordPath()) + assert.NoError(t, trait.validateCACertConfig()) + }) + + t.Run("Scenario5_AllPasswordSourcesTruststorePasswordWins", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/cert"}, + } + trait.TruststorePasswordPath = "/path/to/ts-pass" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + PasswordPath: "/path/to/base-pass", + } + trait.CACert = "" + trait.CACertPassword = "" + + assert.True(t, trait.hasCACerts()) + assert.True(t, trait.hasBaseTruststore()) + assert.Equal(t, "/path/to/ts-pass", trait.getTruststorePasswordPath()) + assert.NoError(t, trait.validateCACertConfig()) + }) + + t.Run("Scenario6_CACertificatesWithLegacyNoExplicitPassword", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/cert"}, + } + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + trait.CACert = "/path/to/legacy-cert" + trait.CACertPassword = "/path/to/legacy-pass" + + assert.True(t, trait.hasCACerts()) + assert.Equal(t, "/path/to/legacy-pass", trait.getTruststorePasswordPath()) + err := trait.validateCACertConfig() + assert.Error(t, err) + assert.Contains(t, err.Error(), "truststore-password-path is required") + }) + + t.Run("Scenario7_LegacyOnly", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = nil + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + trait.CACert = "/path/to/legacy-cert" + trait.CACertPassword = "/path/to/legacy-pass" + + assert.True(t, trait.hasCACerts()) + assert.Equal(t, "/path/to/legacy-pass", trait.getTruststorePasswordPath()) + assert.NoError(t, trait.validateCACertConfig()) + }) + + t.Run("Scenario8_LegacyWithoutPassword", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = nil + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + trait.CACert = "/path/to/legacy-cert" + trait.CACertPassword = "" + + assert.True(t, trait.hasCACerts()) + assert.Equal(t, "", trait.getTruststorePasswordPath()) + err := trait.validateCACertConfig() + assert.Error(t, err) + assert.Contains(t, err.Error(), "ca-cert-password is required") + }) + + t.Run("Scenario9_BaseTruststoreOnlyNoCerts", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = nil + trait.TruststorePasswordPath = "" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + PasswordPath: "/path/to/base-pass", + } + trait.CACert = "" + trait.CACertPassword = "" + + assert.False(t, trait.hasCACerts()) + assert.True(t, trait.hasBaseTruststore()) + assert.Equal(t, "/path/to/base-pass", trait.getTruststorePasswordPath()) + assert.NoError(t, trait.validateCACertConfig()) + }) + + t.Run("Scenario10_IncompleteBaseTruststoreWithTsPassword", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/cert"}, + } + trait.TruststorePasswordPath = "/path/to/ts-pass" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + PasswordPath: "", + } + trait.CACert = "" + trait.CACertPassword = "" + + assert.True(t, trait.hasCACerts()) + assert.False(t, trait.hasBaseTruststore()) + assert.Equal(t, "/path/to/ts-pass", trait.getTruststorePasswordPath()) + + err := trait.validateCACertConfig() + assert.Error(t, err) + assert.Contains(t, err.Error(), "base-truststore: both truststore-path and password-path are required") + }) + + t.Run("Scenario11_IncompleteBaseTruststoreNoTsPassword", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/cert"}, + } + trait.TruststorePasswordPath = "" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + PasswordPath: "", + } + trait.CACert = "" + trait.CACertPassword = "" + + assert.True(t, trait.hasCACerts()) + assert.False(t, trait.hasBaseTruststore()) + assert.Equal(t, "", trait.getTruststorePasswordPath()) + err := trait.validateCACertConfig() + assert.Error(t, err) + assert.Contains(t, err.Error(), "base-truststore: both truststore-path and password-path are required") + }) + + t.Run("Scenario12_EmptyCACertificatesArray", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = []traitv1.CACertConfig{} + trait.TruststorePasswordPath = "" + trait.BaseTruststore = nil + trait.CACert = "" + trait.CACertPassword = "" + + assert.False(t, trait.hasCACerts()) + assert.Equal(t, "", trait.getTruststorePasswordPath()) + assert.NoError(t, trait.validateCACertConfig()) + }) + + t.Run("Scenario13_CACertificatesEntryMissingCertPath", func(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: ""}, + } + trait.TruststorePasswordPath = "/path/to/ts-pass" + trait.BaseTruststore = nil + trait.CACert = "" + trait.CACertPassword = "" + + assert.True(t, trait.hasCACerts()) + err := trait.validateCACertConfig() + assert.Error(t, err) + assert.Contains(t, err.Error(), "cert-path is required") + }) +}
