diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc index 6a58ee2add..6ca251b2a4 100644 --- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc +++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc @@ -6413,6 +6413,37 @@ Defines a set of pods (namely those matching the label selector, relative to the integration pod(s) should not be co-located with. +|=== + +[#_camel_apache_org_v1_trait_BaseTruststore] +=== BaseTruststore + +*Appears on:* + +* <<#_camel_apache_org_v1_trait_JVMTrait, JVMTrait>> + +BaseTruststore represents an existing truststore to use as the base for adding certificates. + +[cols="2,2a",options="header"] +|=== +|Field +|Description + +|`truststorePath` + +string +| + + +Path to the base truststore file. + +|`passwordPath` + +string +| + + +Path to a file containing the password for the base truststore. + + |=== [#_camel_apache_org_v1_trait_BuilderTrait] @@ -6591,6 +6622,30 @@ When using `pod` strategy, annotation to use for the builder pod. The list of manifest platforms to use to build a container image (default `linux/amd64`). +|=== + +[#_camel_apache_org_v1_trait_CACertConfig] +=== CACertConfig + +*Appears on:* + +* <<#_camel_apache_org_v1_trait_JVMTrait, JVMTrait>> + +CACertConfig specifies a CA certificate to import into the truststore. + +[cols="2,2a",options="header"] +|=== +|Field +|Description + +|`certPath` + +string +| + + +Path to the PEM-encoded CA certificate file to import. + + |=== [#_camel_apache_org_v1_trait_CamelTrait] @@ -7796,29 +7851,47 @@ The Jar dependency which will run the application. Leave it empty for managed In A list of JVM agents to download and execute with format `;[;]`. -|`caCert` + +|`caCertificates` + +*xref:#_camel_apache_org_v1_trait_CACertConfig[[\]CACertConfig]* +| + + +A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. + +|`baseTruststore` + +*xref:#_camel_apache_org_v1_trait_BaseTruststore[BaseTruststore]* +| + + +Optional base truststore to use as the starting point for adding certificates. + +|`truststorePasswordPath` + string | -Path to a PEM-encoded CA certificate file. -Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" +Path to a file containing the password for the generated truststore. Required when using ca-certificates without base-truststore. |`caCertMountPath` + string | -The path where the generated truststore will be mounted. -Default: "/etc/camel/conf.d/_truststore" +The path where the generated truststore will be mounted (default `/etc/camel/conf.d/_truststore`). + +|`caCert` + +string +| + + +Deprecated: Use CACertificates instead. Path to a PEM-encoded CA certificate file. |`caCertPassword` + string | -Required when caCert is set. Path to a file containing the truststore password. -Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" +Deprecated: Use CACertificates instead. Path to a file containing the truststore password. |=== diff --git a/docs/modules/traits/pages/jvm.adoc b/docs/modules/traits/pages/jvm.adoc index 0b02a9024a..a86091a844 100755 --- a/docs/modules/traits/pages/jvm.adoc +++ b/docs/modules/traits/pages/jvm.adoc @@ -62,20 +62,29 @@ Deprecated: no longer in use. | []string | A list of JVM agents to download and execute with format `;[;]`. -| jvm.ca-cert +| jvm.ca-certificates +| []github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait.CACertConfig +| A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. + +| jvm.base-truststore +| 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 PEM-encoded CA certificate file. -Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" +| 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" +| The path where the generated truststore will be mounted (default `/etc/camel/conf.d/_truststore`). + +| jvm.ca-cert +| string +| Deprecated: Use CACertificates instead. Path to a PEM-encoded CA certificate file. | jvm.ca-cert-password | string -| Required when caCert is set. Path to a file containing the truststore password. -Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" +| Deprecated: Use CACertificates instead. Path to a file containing the truststore password. |=== @@ -112,7 +121,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: @@ -129,25 +140,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 6e143b98b4..7da1acf289 100644 --- a/e2e/common/traits/jvm_test.go +++ b/e2e/common/traits/jvm_test.go @@ -91,36 +91,122 @@ func TestJVMTrait(t *testing.T) { g.Eventually(IntegrationLogs(t, ctx, ns, name), TestTimeoutShort).Should(ContainSubstring("Hello World!")) }) - t.Run("JVM trait CA cert", func(t *testing.T) { - // Generate a valid self-signed certificate + t.Run("JVM trait multiple CA certs", func(t *testing.T) { + // 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 certificates + caCert1Data := make(map[string]string) + caCert1Data["ca.crt"] = string(cert1Pem) + err = CreatePlainTextSecret(t, ctx, ns, "test-ca-cert-1", caCert1Data) + require.NoError(t, err) + + caCert2Data := make(map[string]string) + caCert2Data["ca.crt"] = string(cert2Pem) + 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", + "-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[1].cert-path=/etc/camel/conf.d/_secrets/test-ca-cert-2/ca.crt", + "-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)) + g.Eventually(IntegrationConditionStatus(t, ctx, ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) + + pod := IntegrationPod(t, ctx, ns, name)() + g.Expect(pod).NotTo(BeNil()) + initContainerNames := make([]string, 0) + for _, c := range pod.Spec.InitContainers { + initContainerNames = append(initContainerNames, c.Name) + } + g.Expect(initContainerNames).To(ContainElement("generate-truststore")) + }) + + t.Run("JVM trait CA cert with base truststore", func(t *testing.T) { + // Test the new base-truststore field (replaces ca-cert-use-system-truststore) certPem, err := generateSelfSignedCert() require.NoError(t, err) + // Create secret with certificate caCertData := make(map[string]string) caCertData["ca.crt"] = string(certPem) + err = CreatePlainTextSecret(t, ctx, ns, "test-ca-sys", caCertData) + require.NoError(t, err) + + // Create secret for base truststore password (JDK cacerts uses "changeit") + baseTsPassData := make(map[string]string) + baseTsPassData["password"] = "changeit" + err = CreatePlainTextSecret(t, ctx, ns, "base-ts-password", baseTsPassData) + require.NoError(t, err) - err = CreatePlainTextSecret(t, ctx, ns, "test-ca-cert", caCertData) + name := RandomizedSuffixName("syscacert") + g.Expect(KamelRun(t, ctx, ns, + "./files/Java.java", + "--name", name, + "-t", "mount.configs=secret:test-ca-sys", + "-t", "mount.configs=secret:base-ts-password", + "-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", + ).Execute()).To(Succeed()) + + g.Eventually(IntegrationPodPhase(t, ctx, ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + g.Eventually(IntegrationConditionStatus(t, ctx, ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) + + pod := IntegrationPod(t, ctx, ns, name)() + g.Expect(pod).NotTo(BeNil()) + initContainerNames := make([]string, 0) + for _, c := range pod.Spec.InitContainers { + initContainerNames = append(initContainerNames, c.Name) + } + g.Expect(initContainerNames).To(ContainElement("generate-truststore")) + }) + + t.Run("JVM trait CA cert single certificate", func(t *testing.T) { + // Test single certificate with the new ca-certificates field + certPem, err := generateSelfSignedCert() + require.NoError(t, err) + + // Create secret with certificate + caCertData := make(map[string]string) + caCertData["ca.crt"] = string(certPem) + err = CreatePlainTextSecret(t, ctx, ns, "test-ca-single", caCertData) require.NoError(t, err) - passwordData := make(map[string]string) - passwordData["password"] = "test-password-123" - err = CreatePlainTextSecret(t, ctx, ns, "test-ca-password", passwordData) + truststorePassData := make(map[string]string) + truststorePassData["password"] = "truststore-password" + err = CreatePlainTextSecret(t, ctx, ns, "truststore-pass-single", truststorePassData) require.NoError(t, err) - name := RandomizedSuffixName("cacert") + name := RandomizedSuffixName("singlecert") g.Expect(KamelRun(t, ctx, ns, "./files/Java.java", "--name", name, - "-t", "mount.configs=secret:test-ca-cert", - "-t", "mount.configs=secret:test-ca-password", - "-t", "jvm.ca-cert=/etc/camel/conf.d/_secrets/test-ca-cert/ca.crt", - "-t", "jvm.ca-cert-password=/etc/camel/conf.d/_secrets/test-ca-password/password", + "-t", "mount.configs=secret:test-ca-single", + "-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.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)) g.Eventually(IntegrationConditionStatus(t, ctx, ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) - // Verify init container was added pod := IntegrationPod(t, ctx, ns, name)() g.Expect(pod).NotTo(BeNil()) initContainerNames := make([]string, 0) diff --git a/helm/camel-k/crds/camel-k-crds.yaml b/helm/camel-k/crds/camel-k-crds.yaml index 84cb8ad48e..830345c66e 100644 --- a/helm/camel-k/crds/camel-k-crds.yaml +++ b/helm/camel-k/crds/camel-k-crds.yaml @@ -4721,21 +4721,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -4776,6 +4798,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -7142,21 +7169,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -7197,6 +7246,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -9465,21 +9519,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -9520,6 +9596,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -11765,21 +11846,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -11820,6 +11923,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -20899,21 +21007,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -20954,6 +21084,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -23153,21 +23288,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -23208,6 +23365,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -33649,21 +33811,44 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password + for the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. + Path to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. + Path to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into + the truststore. Certificates must be mounted via the + mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -33704,6 +33889,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -35835,21 +36025,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -35890,6 +36102,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 8818760450..236cb2f4dd 100644 --- a/pkg/apis/camel/v1/trait/jvm.go +++ b/pkg/apis/camel/v1/trait/jvm.go @@ -17,6 +17,20 @@ limitations under the License. package trait +// 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"` +} + +// BaseTruststore represents an existing truststore to use as the base for adding certificates. +type BaseTruststore struct { + // Path to the base truststore file. + TruststorePath string `json:"truststorePath,omitempty" property:"truststore-path"` + // Path to a file containing the password for the base truststore. + PasswordPath string `json:"passwordPath,omitempty" property:"password-path"` +} + // The JVM trait is used to configure the JVM that runs the Integration. This trait is configured only for Integration and related IntegrationKits // (bound to a container image) built by Camel K operator. If the system detects the usage of a different container image (ie, built externally), then, the // trait is disabled by the platform. @@ -42,13 +56,16 @@ type JVMTrait struct { Jar string `json:"jar,omitempty" property:"jar"` // A list of JVM agents to download and execute with format `;[;]`. Agents []string `json:"agents,omitempty" property:"agents"` - // Path to a PEM-encoded CA certificate file. - // Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" - CACert string `json:"caCert,omitempty" property:"ca-cert"` - // The path where the generated truststore will be mounted. - // Default: "/etc/camel/conf.d/_truststore" + // A list of CA certificates to import into the truststore. Certificates must be mounted via the mount trait. + 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"` - // Required when caCert is set. Path to a file containing the truststore password. - // Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + // Deprecated: Use CACertificates instead. Path to a PEM-encoded CA certificate file. + CACert string `json:"caCert,omitempty" property:"ca-cert"` + // Deprecated: Use CACertificates instead. Path to a file containing the truststore password. CACertPassword string `json:"caCertPassword,omitempty" property:"ca-cert-password"` } diff --git a/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go b/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go index 24cd6c1321..8cd29b192e 100644 --- a/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go +++ b/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go @@ -51,6 +51,21 @@ func (in *AffinityTrait) DeepCopy() *AffinityTrait { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BaseTruststore) DeepCopyInto(out *BaseTruststore) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BaseTruststore. +func (in *BaseTruststore) DeepCopy() *BaseTruststore { + if in == nil { + return nil + } + out := new(BaseTruststore) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BuilderTrait) DeepCopyInto(out *BuilderTrait) { *out = *in @@ -131,6 +146,21 @@ func (in *BuilderTrait) DeepCopy() *BuilderTrait { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CACertConfig) DeepCopyInto(out *CACertConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CACertConfig. +func (in *CACertConfig) DeepCopy() *CACertConfig { + if in == nil { + return nil + } + out := new(CACertConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CamelTrait) DeepCopyInto(out *CamelTrait) { *out = *in @@ -581,6 +611,16 @@ func (in *JVMTrait) DeepCopyInto(out *JVMTrait) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.CACertificates != nil { + in, out := &in.CACertificates, &out.CACertificates + *out = make([]CACertConfig, len(*in)) + copy(*out, *in) + } + if in.BaseTruststore != nil { + in, out := &in.BaseTruststore, &out.BaseTruststore + *out = new(BaseTruststore) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JVMTrait. 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 dec9e1fdb5..d7834d83b5 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml @@ -1472,21 +1472,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -1527,6 +1549,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -3893,21 +3920,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -3948,6 +3997,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 e2e672585a..37e39d1c1c 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml @@ -1340,21 +1340,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -1395,6 +1417,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -3640,21 +3667,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -3695,6 +3744,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 c88edef296..2c047f2be4 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml @@ -8154,21 +8154,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -8209,6 +8231,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -10408,21 +10435,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -10463,6 +10512,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 975a1eca2d..9c7bd24dfc 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml @@ -8210,21 +8210,44 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password + for the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. + Path to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. + Path to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into + the truststore. Certificates must be mounted via the + mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -8265,6 +8288,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 @@ -10396,21 +10424,43 @@ spec: items: type: string type: array + baseTruststore: + description: Optional base truststore to use as the starting + point for adding certificates. + properties: + passwordPath: + description: Path to a file containing the password for + the base truststore. + type: string + truststorePath: + description: Path to the base truststore file. + type: string + type: object caCert: - description: |- - Path to a PEM-encoded CA certificate file. - Example: "/etc/camel/conf.d/_secrets/my-ca/ca.crt" + description: 'Deprecated: Use CACertificates instead. Path + to a PEM-encoded CA certificate file.' type: string caCertMountPath: - description: |- - The path where the generated truststore will be mounted. - Default: "/etc/camel/conf.d/_truststore" + description: The path where the generated truststore will + be mounted (default `/etc/camel/conf.d/_truststore`). type: string caCertPassword: - description: |- - Required when caCert is set. Path to a file containing the truststore password. - Example: "/etc/camel/conf.d/_secrets/truststore-pass/password" + description: 'Deprecated: Use CACertificates instead. Path + to a file containing the truststore password.' type: string + caCertificates: + description: A list of CA certificates to import into the + truststore. Certificates must be mounted via the mount trait. + items: + 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 + type: object + type: array classpath: description: Additional JVM classpath (use `Linux` classpath separator) @@ -10451,6 +10501,11 @@ spec: Prints the command used the start the JVM in the container logs (default `true`) 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 574a1da181..57330f111a 100644 --- a/pkg/trait/init_containers.go +++ b/pkg/trait/init_containers.go @@ -91,20 +91,45 @@ 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.hasCACert() { + if ok && jvm.hasCACerts() { if err := jvm.validateCACertConfig(); err != nil { return false, nil, err } - // keytool reads password from file using -storepass:file - keytoolCmd := fmt.Sprintf( - "keytool -importcert -noprompt -alias custom-ca -storepass:file %s -keystore %s -file %s", - jvm.getCACertPasswordPath(), jvm.getTrustStorePath(), jvm.getCACertPath(), - ) + + var allCommands []string + importPassPath := jvm.getTruststorePasswordPath() + + if jvm.hasBaseTruststore() { + baseTruststore := jvm.getBaseTruststore() + copyCmd := fmt.Sprintf("cp %s %s", baseTruststore.TruststorePath, jvm.getTrustStorePath()) + allCommands = append(allCommands, copyCmd) + 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, 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, " && ") + if len(allCommands) > 1 || jvm.hasBaseTruststore() { + fullCommand = fmt.Sprintf("/bin/bash -c \"%s\"", fullCommand) + } caCertTask := containerTask{ name: "generate-truststore", image: defaults.BaseImage(), - command: keytoolCmd, + command: fullCommand, } t.tasks = append(t.tasks, caCertTask) } diff --git a/pkg/trait/init_containers_test.go b/pkg/trait/init_containers_test.go index afad658410..eb7b4d5408 100644 --- a/pkg/trait/init_containers_test.go +++ b/pkg/trait/init_containers_test.go @@ -18,6 +18,7 @@ limitations under the License. package trait import ( + "strings" "testing" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" @@ -393,8 +394,10 @@ func TestApplyInitContainerWithCACert(t *testing.T) { Spec: v1.IntegrationSpec{ Traits: v1.Traits{ JVM: &trait.JVMTrait{ - CACert: "/etc/camel/conf.d/_secrets/my-ca/ca.crt", - CACertPassword: "/etc/camel/conf.d/_secrets/truststore-pass/password", + CACertificates: []trait.CACertConfig{ + {CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt"}, + }, + TruststorePasswordPath: "/etc/camel/conf.d/_secrets/truststore-pass/password", }, }, }, @@ -432,3 +435,242 @@ func TestApplyInitContainerWithCACert(t *testing.T) { assert.Contains(t, initContainer.Command[0], "keytool") } + +func TestApplyInitContainerWithMultipleCACerts(t *testing.T) { + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-it", + Labels: map[string]string{ + v1.IntegrationLabel: "my-it", + }, + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{}, + }, + }, + } + catalog, _ := camel.DefaultCatalog() + traitCatalog := NewCatalog(nil) + fakeClient, _ := internal.NewFakeClient(deployment) + environment := Environment{ + Client: fakeClient, + CamelCatalog: catalog, + Catalog: traitCatalog, + Resources: kubernetes.NewCollection(), + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-it", + }, + Spec: v1.IntegrationSpec{ + Traits: v1.Traits{ + JVM: &trait.JVMTrait{ + CACertificates: []trait.CACertConfig{ + {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", + }, + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + }, + }, + Platform: &v1.IntegrationPlatform{ + Spec: v1.IntegrationPlatformSpec{ + Cluster: v1.IntegrationPlatformClusterOpenShift, + Build: v1.IntegrationPlatformBuildSpec{ + PublishStrategy: v1.IntegrationPlatformBuildPublishStrategyJib, + Registry: v1.RegistrySpec{Address: "registry"}, + RuntimeVersion: catalog.Runtime.Version, + }, + }, + Status: v1.IntegrationPlatformStatus{ + Phase: v1.IntegrationPlatformPhaseReady, + }, + }, + } + environment.Resources.Add(deployment) + environment.Platform.ResyncStatusFullConfig() + _, _, err := traitCatalog.apply(&environment) + + require.NoError(t, err) + + deploy := environment.Resources.GetDeploymentForIntegration(environment.Integration) + require.NotNil(t, deploy) + + require.Len(t, deploy.Spec.Template.Spec.InitContainers, 1) + initContainer := deploy.Spec.Template.Spec.InitContainers[0] + assert.Equal(t, "generate-truststore", initContainer.Name) + assert.Equal(t, defaults.BaseImage(), initContainer.Image) + + commandStr := strings.Join(initContainer.Command, " ") + assert.Contains(t, commandStr, "/bin/bash") + assert.Contains(t, commandStr, "keytool") + assert.Contains(t, commandStr, "custom-ca-0") + assert.Contains(t, commandStr, "custom-ca-1") + assert.Contains(t, commandStr, "custom-ca-2") + assert.Contains(t, commandStr, "/etc/camel/conf.d/_secrets/ca1/ca.crt") + assert.Contains(t, commandStr, "/etc/camel/conf.d/_secrets/ca2/ca.crt") + assert.Contains(t, commandStr, "/etc/camel/conf.d/_secrets/ca3/ca.crt") + assert.Contains(t, commandStr, "&&") + + 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) { + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-it", + Labels: map[string]string{ + v1.IntegrationLabel: "my-it", + }, + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{}, + }, + }, + } + catalog, _ := camel.DefaultCatalog() + traitCatalog := NewCatalog(nil) + fakeClient, _ := internal.NewFakeClient(deployment) + environment := Environment{ + Client: fakeClient, + CamelCatalog: catalog, + Catalog: traitCatalog, + Resources: kubernetes.NewCollection(), + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-it", + }, + Spec: v1.IntegrationSpec{ + Traits: v1.Traits{ + JVM: &trait.JVMTrait{ + CACertificates: []trait.CACertConfig{ + {CertPath: "/etc/camel/conf.d/_secrets/ca1/ca.crt"}, + }, + 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", + }, + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + }, + }, + Platform: &v1.IntegrationPlatform{ + Spec: v1.IntegrationPlatformSpec{ + Cluster: v1.IntegrationPlatformClusterOpenShift, + Build: v1.IntegrationPlatformBuildSpec{ + PublishStrategy: v1.IntegrationPlatformBuildPublishStrategyJib, + Registry: v1.RegistrySpec{Address: "registry"}, + RuntimeVersion: catalog.Runtime.Version, + }, + }, + Status: v1.IntegrationPlatformStatus{ + Phase: v1.IntegrationPlatformPhaseReady, + }, + }, + } + environment.Resources.Add(deployment) + environment.Platform.ResyncStatusFullConfig() + _, _, err := traitCatalog.apply(&environment) + + require.NoError(t, err) + + deploy := environment.Resources.GetDeploymentForIntegration(environment.Integration) + require.NotNil(t, deploy) + + require.Len(t, deploy.Spec.Template.Spec.InitContainers, 1) + initContainer := deploy.Spec.Template.Spec.InitContainers[0] + + commandStr := strings.Join(initContainer.Command, " ") + assert.Contains(t, commandStr, "/etc/camel/conf.d/_secrets/ca1/ca.crt") + assert.Contains(t, commandStr, "/etc/camel/conf.d/_secrets/ca2/ca.crt") +} + +func TestApplyInitContainerWithBaseTruststore(t *testing.T) { + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-it", + Labels: map[string]string{ + v1.IntegrationLabel: "my-it", + }, + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{}, + }, + }, + } + catalog, _ := camel.DefaultCatalog() + traitCatalog := NewCatalog(nil) + fakeClient, _ := internal.NewFakeClient(deployment) + environment := Environment{ + Client: fakeClient, + CamelCatalog: catalog, + Catalog: traitCatalog, + Resources: kubernetes.NewCollection(), + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-it", + }, + Spec: v1.IntegrationSpec{ + Traits: v1.Traits{ + JVM: &trait.JVMTrait{ + CACertificates: []trait.CACertConfig{ + {CertPath: "/etc/camel/conf.d/_secrets/my-ca/ca.crt"}, + }, + BaseTruststore: &trait.BaseTruststore{ + TruststorePath: "/opt/java/openjdk/lib/security/cacerts", + PasswordPath: "/etc/camel/conf.d/_secrets/base-truststore-pass/password", + }, + }, + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + }, + }, + Platform: &v1.IntegrationPlatform{ + Spec: v1.IntegrationPlatformSpec{ + Cluster: v1.IntegrationPlatformClusterOpenShift, + Build: v1.IntegrationPlatformBuildSpec{ + PublishStrategy: v1.IntegrationPlatformBuildPublishStrategyJib, + Registry: v1.RegistrySpec{Address: "registry"}, + RuntimeVersion: catalog.Runtime.Version, + }, + }, + Status: v1.IntegrationPlatformStatus{ + Phase: v1.IntegrationPlatformPhaseReady, + }, + }, + } + environment.Resources.Add(deployment) + environment.Platform.ResyncStatusFullConfig() + _, _, err := traitCatalog.apply(&environment) + + require.NoError(t, err) + + deploy := environment.Resources.GetDeploymentForIntegration(environment.Integration) + require.NotNil(t, deploy) + + require.Len(t, deploy.Spec.Template.Spec.InitContainers, 1) + initContainer := deploy.Spec.Template.Spec.InitContainers[0] + assert.Equal(t, "generate-truststore", initContainer.Name) + + commandStr := strings.Join(initContainer.Command, " ") + assert.Contains(t, commandStr, "/bin/bash") + + assert.Contains(t, commandStr, "cp /opt/java/openjdk/lib/security/cacerts") + assert.NotContains(t, commandStr, "keytool -storepasswd") + assert.Contains(t, commandStr, "keytool -importcert") + assert.Contains(t, commandStr, "-storepass:file /etc/camel/conf.d/_secrets/base-truststore-pass/password") + assert.Contains(t, commandStr, "/etc/camel/conf.d/_secrets/my-ca/ca.crt") + assert.Contains(t, commandStr, "&&") +} diff --git a/pkg/trait/jvm.go b/pkg/trait/jvm.go index cefb926779..940dd98885 100644 --- a/pkg/trait/jvm.go +++ b/pkg/trait/jvm.go @@ -104,7 +104,25 @@ func (t *jvmTrait) Configure(e *Environment) (bool, *TraitCondition, error) { t.jvmAgentArgs = jvmAgentArgs } - return true, nil, nil + if err := t.validateCACertConfig(); err != nil { + return false, nil, err + } + + var condition *TraitCondition + //nolint:staticcheck + if t.CACert != "" || t.CACertPassword != "" { + m := "The ca-cert and ca-cert-password parameters are deprecated and may be removed in future releases. Use ca-certificates instead." + t.L.Info(m) + condition = NewIntegrationCondition( + "JVM", + v1.IntegrationConditionTraitInfo, + corev1.ConditionTrue, + TraitConfigurationReason, + m, + ) + } + + return true, condition, nil } func (t *jvmTrait) Apply(e *Environment) error { @@ -376,12 +394,17 @@ func getLegacyCamelQuarkusDependenciesPaths() *sets.Set { // configureCACert configures the CA certificate truststore and returns the JVM arguments. func (t *jvmTrait) configureCaCert() []string { - if t.CACert == "" { + if !t.hasCACerts() { + return nil + } + + truststorePassPath := t.getTruststorePasswordPath() + if truststorePassPath == "" { return nil } return []string{ "-Djavax.net.ssl.trustStore=" + t.getTrustStorePath(), - fmt.Sprintf("-Djavax.net.ssl.trustStorePassword=$(%s)", truststorePasswordEnvVar), + fmt.Sprintf("-Djavax.net.ssl.trustStorePassword=$(cat %s)", truststorePassPath), } } diff --git a/pkg/trait/jvm_cacert.go b/pkg/trait/jvm_cacert.go index 7d11f678e2..2b71c7cce8 100644 --- a/pkg/trait/jvm_cacert.go +++ b/pkg/trait/jvm_cacert.go @@ -17,17 +17,27 @@ limitations under the License. package trait -import "errors" +import ( + "errors" + "fmt" + + traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" +) const ( - defaultCACertMountPath = "/etc/camel/conf.d/_truststore" - caCertVolumeName = "jvm-truststore" - trustStoreName = "truststore.jks" - truststorePasswordEnvVar = "TRUSTSTORE_PASSWORD" + defaultCACertMountPath = "/etc/camel/conf.d/_truststore" + caCertVolumeName = "jvm-truststore" + trustStoreName = "truststore.jks" ) -func (t *jvmTrait) hasCACert() bool { - return t.CACert != "" +type CACertEntry struct { + CertPath string +} + +// hasCACerts returns true if any CA certificates are configured. +func (t *jvmTrait) hasCACerts() bool { + //nolint:staticcheck + return len(t.CACertificates) > 0 || t.CACert != "" } func (t *jvmTrait) getCACertMountPath() string { @@ -42,24 +52,84 @@ func (t *jvmTrait) getTrustStorePath() string { return t.getCACertMountPath() + "/" + trustStoreName } -// validateCACertConfig validates that the required file paths are provided. -func (t *jvmTrait) validateCACertConfig() error { - if t.CACert == "" { - return nil +// hasBaseTruststore returns true if a base truststore is configured. +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.CACertPassword == "" { - return errors.New("ca-cert-password is required when ca-cert is set") + if t.hasBaseTruststore() { + return t.BaseTruststore.PasswordPath + } + //nolint:staticcheck + if t.CACert != "" && t.CACertPassword != "" { + return t.CACertPassword } - return nil + return "" } -// getCACertPath returns the user-provided CA certificate file path. -func (t *jvmTrait) getCACertPath() string { - return t.CACert +// getBaseTruststore returns the base truststore configuration if set. +func (t *jvmTrait) getBaseTruststore() *traitv1.BaseTruststore { + return t.BaseTruststore } -// getCACertPasswordPath returns the user-provided password file path. -func (t *jvmTrait) getCACertPasswordPath() string { - return t.CACertPassword +func (t *jvmTrait) getAllCACertEntries() []CACertEntry { + var entries []CACertEntry + + for _, cert := range t.CACertificates { + if cert.CertPath != "" { + entries = append(entries, CACertEntry{CertPath: cert.CertPath}) + } + } + + //nolint:staticcheck + if t.CACert != "" && t.CACertPassword != "" { + found := false + for _, e := range entries { + //nolint:staticcheck + if e.CertPath == t.CACert { + found = true + + break + } + } + if !found { + //nolint:staticcheck + entries = append(entries, CACertEntry{CertPath: t.CACert}) + } + } + + return entries +} + +func (t *jvmTrait) validateCACertConfig() error { + for i, cert := range t.CACertificates { + if cert.CertPath == "" { + return fmt.Errorf("CACertificates[%d]: cert-path is required", i) + } + } + + //nolint:staticcheck + if t.CACert != "" && t.CACertPassword == "" { + return errors.New("ca-cert-password is required when ca-cert is specified") + } + + if t.BaseTruststore != nil { + hasTruststorePath := t.BaseTruststore.TruststorePath != "" + hasPasswordPath := t.BaseTruststore.PasswordPath != "" + if hasTruststorePath != hasPasswordPath { + return errors.New("base-truststore: both truststore-path and password-path are required") + } + } + + // 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 7402d748a5..7d5e654c2c 100644 --- a/pkg/trait/jvm_test.go +++ b/pkg/trait/jvm_test.go @@ -722,8 +722,10 @@ func TestApplyJvmTraitAgentFail(t *testing.T) { func TestApplyJvmTraitWithCACert(t *testing.T) { trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) - trait.CACert = "/etc/camel/conf.d/_secrets/my-ca/ca.crt" - trait.CACertPassword = "/etc/camel/conf.d/_secrets/truststore-pass/password" + trait.CACertificates = []traitv1.CACertConfig{ + {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{ @@ -749,31 +751,15 @@ func TestApplyJvmTraitWithCACert(t *testing.T) { require.NoError(t, err) assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Djavax.net.ssl.trustStore=/etc/camel/conf.d/_truststore/truststore.jks") - assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Djavax.net.ssl.trustStorePassword=$(TRUSTSTORE_PASSWORD)") -} - -func TestValidateCACertConfig(t *testing.T) { - trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) - - trait.CACert = "" - err := trait.validateCACertConfig() - require.NoError(t, err) - - trait.CACert = "/etc/camel/conf.d/_secrets/my-ca/ca.crt" - trait.CACertPassword = "" - err = trait.validateCACertConfig() - require.Error(t, err) - assert.Contains(t, err.Error(), "ca-cert-password is required") - - trait.CACertPassword = "/etc/camel/conf.d/_secrets/truststore-pass/password" - err = trait.validateCACertConfig() - require.NoError(t, err) + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Djavax.net.ssl.trustStorePassword=$(cat /etc/camel/conf.d/_secrets/truststore-pass/password)") } func TestApplyJvmTraitWithCustomCACertMountPath(t *testing.T) { trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) - trait.CACert = "/etc/camel/conf.d/_secrets/my-ca/ca.crt" - trait.CACertPassword = "/etc/camel/conf.d/_secrets/truststore-pass/password" + trait.CACertificates = []traitv1.CACertConfig{ + {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{ @@ -800,5 +786,439 @@ func TestApplyJvmTraitWithCustomCACertMountPath(t *testing.T) { require.NoError(t, err) assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Djavax.net.ssl.trustStore=/custom/truststore/path/truststore.jks") - assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Djavax.net.ssl.trustStorePassword=$(TRUSTSTORE_PASSWORD)") + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Djavax.net.ssl.trustStorePassword=$(cat /etc/camel/conf.d/_secrets/truststore-pass/password)") +} + +func TestGetAllCACertEntries(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + + trait.CACertificates = []traitv1.CACertConfig{ + {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/ca2.crt", entries[1].CertPath) + + trait.CACertificates = nil + trait.CACert = "/path/to/legacy.crt" + trait.CACertPassword = "/path/to/legacy-pass" + entries = trait.getAllCACertEntries() + assert.Len(t, entries, 1) + assert.Equal(t, "/path/to/legacy.crt", entries[0].CertPath) + + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/ca1.crt"}, + } + trait.CACert = "/path/to/ca2.crt" + trait.CACertPassword = "/path/to/pass2" + entries = trait.getAllCACertEntries() + assert.Len(t, entries, 2) + + trait.CACertificates = nil + trait.CACert = "" + trait.CACertPassword = "" + entries = trait.getAllCACertEntries() + assert.Len(t, entries, 0) +} + +func TestHasCACerts(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + + trait.CACertificates = nil + trait.CACert = "" + assert.False(t, trait.hasCACerts()) + + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/ca1.crt"}, + } + trait.CACert = "" + assert.True(t, trait.hasCACerts()) + + trait.CACertificates = nil + trait.CACert = "/path/to/legacy.crt" + assert.True(t, trait.hasCACerts()) + + trait.CACertificates = []traitv1.CACertConfig{ + {CertPath: "/path/to/ca1.crt"}, + } + trait.CACert = "/path/to/ca2.crt" + assert.True(t, trait.hasCACerts()) +} + +func TestHasBaseTruststore(t *testing.T) { + trait, _ := createNominalJvmTest(v1.IntegrationKitTypePlatform) + + trait.BaseTruststore = nil + assert.False(t, trait.hasBaseTruststore()) + + trait.BaseTruststore = &traitv1.BaseTruststore{} + assert.False(t, trait.hasBaseTruststore()) + + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + } + assert.False(t, trait.hasBaseTruststore()) + + trait.BaseTruststore = &traitv1.BaseTruststore{ + PasswordPath: "/path/to/pass", + } + assert.False(t, trait.hasBaseTruststore()) + + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/opt/java/openjdk/lib/security/cacerts", + PasswordPath: "/path/to/pass", + } + 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) + + trait.CACertificates = nil + 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"}, + } + trait.TruststorePasswordPath = "/path/to/truststore-pass" + trait.BaseTruststore = nil + assert.NoError(t, trait.validateCACertConfig()) + + trait.CACertificates = []traitv1.CACertConfig{ + {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" + trait.CACertPassword = "/path/to/pass" + assert.NoError(t, trait.validateCACertConfig()) + + trait.CACert = "" + trait.CACertPassword = "" + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + } + assert.Error(t, trait.validateCACertConfig()) + + trait.BaseTruststore = &traitv1.BaseTruststore{ + TruststorePath: "/path/to/cacerts", + PasswordPath: "/path/to/pass", + } + 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") + }) } diff --git a/pkg/trait/mount.go b/pkg/trait/mount.go index 3e25bad0fc..4322df6143 100644 --- a/pkg/trait/mount.go +++ b/pkg/trait/mount.go @@ -205,7 +205,7 @@ func (t *mountTrait) configureVolumesAndMounts( } // Mount CA cert truststore volume if configured - if ok && jvm.hasCACert() { + if ok && jvm.hasCACerts() { mountPath := jvm.getCACertMountPath() // EmptyDir volume for truststore output