From 39476b57160a668227c3eb8106dc4c0d5b546af9 Mon Sep 17 00:00:00 2001 From: Yevhen Ivantsov Date: Tue, 12 Sep 2023 07:32:59 +1000 Subject: [PATCH 1/2] Add certs to Java trust store --- src/main/charts/bamboo/README.md | 1 + src/main/charts/bamboo/templates/_helpers.tpl | 19 ++ .../charts/bamboo/templates/config-jvm.yaml | 3 + .../charts/bamboo/templates/statefulset.yaml | 12 + src/main/charts/bamboo/values.yaml | 6 + src/main/charts/bitbucket/README.md | 2 + .../charts/bitbucket/templates/_helpers.tpl | 23 ++ .../bitbucket/templates/config-jvm-mesh.yaml | 3 + .../bitbucket/templates/config-jvm.yaml | 3 + .../bitbucket/templates/statefulset-mesh.yaml | 23 ++ .../bitbucket/templates/statefulset.yaml | 16 ++ src/main/charts/bitbucket/values.yaml | 12 + src/main/charts/confluence/README.md | 2 + .../charts/confluence/templates/_helpers.tpl | 38 ++++ .../confluence/templates/config-jvm.yaml | 3 + .../templates/statefulset-synchrony.yaml | 13 ++ .../confluence/templates/statefulset.yaml | 12 + .../templates/synchrony-start-script.yaml | 3 + src/main/charts/confluence/values.yaml | 12 + src/main/charts/crowd/README.md | 1 + src/main/charts/crowd/templates/_helpers.tpl | 21 +- .../charts/crowd/templates/config-jvm.yaml | 3 + .../charts/crowd/templates/statefulset.yaml | 12 + src/main/charts/crowd/values.yaml | 6 + src/main/charts/jira/README.md | 1 + src/main/charts/jira/templates/_helpers.tpl | 19 ++ .../charts/jira/templates/config-jvm.yaml | 3 + .../charts/jira/templates/statefulset.yaml | 12 + src/main/charts/jira/values.yaml | 6 + src/test/config/kind/common-values.yaml | 6 + .../java/test/AdditionalCertificatesTest.java | 206 ++++++++++++++++++ .../bamboo-agent/output.yaml | 1 - .../expected_helm_output/bamboo/output.yaml | 1 - .../bitbucket/output.yaml | 2 - .../confluence/output.yaml | 2 - .../expected_helm_output/crowd/output.yaml | 1 - .../expected_helm_output/jira/output.yaml | 1 - src/test/scripts/kind/deploy_app.sh | 3 + 38 files changed, 504 insertions(+), 9 deletions(-) create mode 100644 src/test/java/test/AdditionalCertificatesTest.java diff --git a/src/main/charts/bamboo/README.md b/src/main/charts/bamboo/README.md index 5c32c566f..4fb487eb2 100644 --- a/src/main/charts/bamboo/README.md +++ b/src/main/charts/bamboo/README.md @@ -32,6 +32,7 @@ Kubernetes: `>=1.21.x-0` | bamboo.accessLog.localHomeSubPath | string | `"log"` | The subdirectory within the local-home volume where access logs should be stored. | | bamboo.accessLog.mountPath | string | `"/opt/atlassian/bamboo/logs"` | The path within the Bamboo container where the local-home volume should be mounted in order to capture access logs. | | bamboo.additionalBundledPlugins | list | `[]` | Specifies a list of additional Bamboo plugins that should be added to the Bamboo container. Note plugins installed via this method will appear as bundled plugins rather than user plugins. These should be specified in the same manner as the 'additionalLibraries' property. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ NOTE: only .jar files can be loaded using this approach. OBR's can be extracted (unzipped) to access the associated .jar An alternative to this method is to install the plugins via "Manage Apps" in the product system administration UI. | +| bamboo.additionalCertificates | object | `{"customCmd":null,"secretName":null}` | Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates | | bamboo.additionalEnvironmentVariables | list | `[]` | Defines any additional environment variables to be passed to the Bamboo container. See https://hub.docker.com/r/atlassian/bamboo-server for supported variables. | | bamboo.additionalJvmArgs | list | `[]` | Specifies a list of additional arguments that can be passed to the Bamboo JVM, e.g. system properties. | | bamboo.additionalLibraries | list | `[]` | Specifies a list of additional Java libraries that should be added to the Bamboo container. Each item in the list should specify the name of the volume that contains the library, as well as the name of the library file within that volume's root directory. Optionally, a subDirectory field can be included to specify which directory in the volume contains the library file. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ | diff --git a/src/main/charts/bamboo/templates/_helpers.tpl b/src/main/charts/bamboo/templates/_helpers.tpl index d6978561f..92a5d7ebf 100644 --- a/src/main/charts/bamboo/templates/_helpers.tpl +++ b/src/main/charts/bamboo/templates/_helpers.tpl @@ -111,6 +111,10 @@ on Tomcat's logs directory. THis ensures that Tomcat+Bamboo logs get captured in {{- if .Values.volumes.sharedHome.subPath }} subPath: {{ .Values.volumes.sharedHome.subPath | quote }} {{- end }} +{{- if .Values.bamboo.additionalCertificates.secretName }} +- name: keystore + mountPath: /var/ssl +{{- end }} {{- end }} {{/* @@ -205,6 +209,13 @@ For each additional plugin declared, generate a volume mount that injects that l {{- with .Values.volumes.additional }} {{- toYaml . | nindent 0 }} {{- end }} +{{- if .Values.bamboo.additionalCertificates.secretName }} +- name: keystore + emptyDir: {} +- name: certs + secret: + secretName: {{ .Values.bamboo.additionalCertificates.secretName }} +{{- end }} {{- end }} {{- define "bamboo.volumes.localHome" -}} @@ -307,3 +318,11 @@ Define additional hosts here to allow template overrides when used as a sub char {{- toYaml . }} {{- end }} {{- end }} + +{{- define "bamboo.addCrtToKeystoreCmd" }} +{{- if .Values.bamboo.additionalCertificates.customCmd}} +{{ .Values.bamboo.additionalCertificates.customCmd}} +{{- else }} +set -e; for crt in /tmp/crt/*.*; do echo "Adding $crt to keystore"; keytool -import -cacerts -storepass changeit -noprompt -alias $(echo $(basename $crt)) -file $crt; done; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts +{{- end }} +{{- end }} diff --git a/src/main/charts/bamboo/templates/config-jvm.yaml b/src/main/charts/bamboo/templates/config-jvm.yaml index 08cc47b8d..4001bdba0 100644 --- a/src/main/charts/bamboo/templates/config-jvm.yaml +++ b/src/main/charts/bamboo/templates/config-jvm.yaml @@ -10,6 +10,9 @@ data: {{ . }} {{- end }} -XX:ActiveProcessorCount={{ include "flooredCPU" .Values.bamboo.resources.container.requests.cpu }} + {{- if .Values.bamboo.additionalCertificates.secretName }} + -Djavax.net.ssl.trustStore=/var/ssl/cacerts + {{- end }} {{ include "common.jmx.javaagent" . | indent 4 | trim }} max_heap: {{ .Values.bamboo.resources.jvm.maxHeap }} min_heap: {{ .Values.bamboo.resources.jvm.minHeap }} diff --git a/src/main/charts/bamboo/templates/statefulset.yaml b/src/main/charts/bamboo/templates/statefulset.yaml index b0eecc7e7..874347102 100644 --- a/src/main/charts/bamboo/templates/statefulset.yaml +++ b/src/main/charts/bamboo/templates/statefulset.yaml @@ -55,6 +55,18 @@ spec: command: ["sh", "-c", {{ include "bamboo.sharedHome.permissionFix.command" . | quote }}] {{- end }} {{- include "common.jmx.initContainer" . | nindent 8 }} + {{- if .Values.bamboo.additionalCertificates.secretName }} + - name: import-certs + image: {{ include "bamboo.image" . | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: keystore + mountPath: /var/ssl + - name: certs + mountPath: /tmp/crt + command: ["/bin/bash"] + args: ["-c", {{ include "bamboo.addCrtToKeystoreCmd" . }}] + {{- end }} containers: - name: {{ if .Values.bamboo.useHelmReleaseNameAsContainerName}}{{ include "common.names.fullname" . }}{{ else }}{{ .Chart.Name }}{{ end }} image: {{ include "bamboo.image" . | quote }} diff --git a/src/main/charts/bamboo/values.yaml b/src/main/charts/bamboo/values.yaml index 17d6d69df..ef67a873d 100644 --- a/src/main/charts/bamboo/values.yaml +++ b/src/main/charts/bamboo/values.yaml @@ -829,6 +829,12 @@ bamboo: # labelSelector: # matchLabels: + # -- Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates + # + additionalCertificates: + secretName: + customCmd: + # Monitoring # monitoring: diff --git a/src/main/charts/bitbucket/README.md b/src/main/charts/bitbucket/README.md index 739eed745..8a2a14810 100644 --- a/src/main/charts/bitbucket/README.md +++ b/src/main/charts/bitbucket/README.md @@ -31,6 +31,7 @@ Kubernetes: `>=1.21.x-0` | additionalLabels | object | `{}` | Additional labels that should be applied to all resources | | affinity | object | `{}` | Standard Kubernetes affinities that will be applied to all Bitbucket pods Due to the performance requirements it is highly recommended running all Bitbucket pods in the same availability zone as your dedicated NFS server. To achieve this, you can define `affinity` and `podAffinity` rules that will place all pods into the same zone, and therefore minimise the real distance between the application pods and the shared storage. More specific documentation can be found in the official Affinity and Anti-affinity documentation: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity This is an example on how to ensure the pods are in the same zone as NFS server that is labeled with `role=nfs-server`: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: role operator: In values: - nfs-server # needs to be the same value as NFS server deployment topologyKey: topology.kubernetes.io/zone | | bitbucket.additionalBundledPlugins | list | `[]` | Specifies a list of additional Bitbucket plugins that should be added to the Bitbucket container. Note plugins installed via this method will appear as bundled plugins rather than user plugins. These should be specified in the same manner as the 'additionalLibraries' property. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ NOTE: only .jar files can be loaded using this approach. OBR's can be extracted (unzipped) to access the associated .jar An alternative to this method is to install the plugins via "Manage Apps" in the product system administration UI. | +| bitbucket.additionalCertificates | object | `{"customCmd":null,"secretName":null}` | Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates | | bitbucket.additionalEnvironmentVariables | list | `[]` | Defines any additional environment variables to be passed to the Bitbucket container. See https://hub.docker.com/r/atlassian/bitbucket-server for supported variables. | | bitbucket.additionalJvmArgs | list | `[]` | Specifies a list of additional arguments that can be passed to the Bitbucket JVM, e.g. system properties. | | bitbucket.additionalLibraries | list | `[]` | Specifies a list of additional Java libraries that should be added to the Bitbucket container. Each item in the list should specify the name of the volume that contains the library, as well as the name of the library file within that volume's root directory. Optionally, a subDirectory field can be included to specify which directory in the volume contains the library file. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ | @@ -55,6 +56,7 @@ Kubernetes: `>=1.21.x-0` | bitbucket.livenessProbe.initialDelaySeconds | int | `60` | Time to wait before starting the first probe | | bitbucket.livenessProbe.periodSeconds | int | `5` | How often (in seconds) the Bitbucket container liveness probe will run | | bitbucket.livenessProbe.timeoutSeconds | int | `1` | Number of seconds after which the probe times out | +| bitbucket.mesh.additionalCertificates | object | `{"customCmd":null,"secretName":null}` | Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates | | bitbucket.mesh.additionalEnvironmentVariables | object | `{}` | Defines any additional environment variables to be passed to the Bitbucket mesh containers. | | bitbucket.mesh.additionalFiles | string | `nil` | Additional existing ConfigMaps and Secrets not managed by Helm that should be mounted into service container | | bitbucket.mesh.additionalInitContainers | object | `{}` | Additional initContainer definitions that will be added to all Bitbucket pods | diff --git a/src/main/charts/bitbucket/templates/_helpers.tpl b/src/main/charts/bitbucket/templates/_helpers.tpl index 85291269e..168a6dfcd 100644 --- a/src/main/charts/bitbucket/templates/_helpers.tpl +++ b/src/main/charts/bitbucket/templates/_helpers.tpl @@ -254,6 +254,13 @@ Define additional hosts here to allow template overrides when used as a sub char {{- with .Values.volumes.additional }} {{- toYaml . | nindent 0 }} {{- end }} +{{- if .Values.bitbucket.additionalCertificates.secretName }} +- name: keystore + emptyDir: {} +- name: certs + secret: + secretName: {{ .Values.bitbucket.additionalCertificates.secretName }} +{{- end }} {{- end }} {{- define "bitbucket.volumes.localHome" -}} @@ -445,3 +452,19 @@ volumeClaimTemplates: {{- . }} {{- end }} {{- end}} + +{{- define "bitbucket.addCrtToKeystoreCmd" }} +{{- if .Values.bitbucket.additionalCertificates.customCmd}} +{{ .Values.bitbucket.additionalCertificates.customCmd}} +{{- else }} +set -e; for crt in /tmp/crt/*.*; do echo "Adding $crt to keystore"; keytool -import -cacerts -storepass changeit -noprompt -alias $(echo $(basename $crt)) -file $crt; done; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts +{{- end }} +{{- end }} + +{{- define "bitbucketMesh.addCrtToKeystoreCmd" }} +{{- if .Values.bitbucket.mesh.additionalCertificates.customCmd}} +{{ .Values.bitbucket.mesh.additionalCertificates.customCmd}} +{{- else }} +set -e; for crt in /tmp/crt/*.*; do echo "Adding $crt to keystore"; keytool -import -cacerts -storepass changeit -noprompt -alias $(echo $(basename $crt)) -file $crt; done; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts +{{- end }} +{{- end }} diff --git a/src/main/charts/bitbucket/templates/config-jvm-mesh.yaml b/src/main/charts/bitbucket/templates/config-jvm-mesh.yaml index 771b911f3..745bb2d13 100644 --- a/src/main/charts/bitbucket/templates/config-jvm-mesh.yaml +++ b/src/main/charts/bitbucket/templates/config-jvm-mesh.yaml @@ -13,5 +13,8 @@ data: {{- if .Values.monitoring.exposeJmxMetrics }} -javaagent:{{ .Values.monitoring.jmxExporterCustomJarLocation | default (printf "%s/jmx_prometheus_javaagent.jar" ( .Values.bitbucket.mesh.volume.mountPath)) }}={{ .Values.monitoring.jmxExporterPort}}:/opt/atlassian/jmx/jmx-config.yaml {{- end }} + {{- if .Values.bitbucket.mesh.additionalCertificates.secretName }} + -Djavax.net.ssl.trustStore=/var/ssl/cacerts + {{- end }} max_heap: {{ .Values.bitbucket.mesh.resources.jvm.maxHeap }} min_heap: {{ .Values.bitbucket.mesh.resources.jvm.minHeap }} diff --git a/src/main/charts/bitbucket/templates/config-jvm.yaml b/src/main/charts/bitbucket/templates/config-jvm.yaml index 6dcc95f0a..cb462037e 100644 --- a/src/main/charts/bitbucket/templates/config-jvm.yaml +++ b/src/main/charts/bitbucket/templates/config-jvm.yaml @@ -14,5 +14,8 @@ data: {{- if .Values.monitoring.exposeJmxMetrics }} -Dplugin.bitbucket-git.mesh.sidecar.jvmArgs=-javaagent:{{ .Values.monitoring.jmxExporterCustomJarLocation | default (printf "%s/jmx_prometheus_javaagent.jar" .Values.volumes.sharedHome.mountPath) }}=9998:/opt/atlassian/jmx/jmx-config.yaml {{- end }} + {{- if .Values.bitbucket.additionalCertificates.secretName }} + -Djavax.net.ssl.trustStore=/var/ssl/cacerts + {{- end }} max_heap: {{ .Values.bitbucket.resources.jvm.maxHeap }} min_heap: {{ .Values.bitbucket.resources.jvm.minHeap }} diff --git a/src/main/charts/bitbucket/templates/statefulset-mesh.yaml b/src/main/charts/bitbucket/templates/statefulset-mesh.yaml index 40bd7e76b..b23316f85 100644 --- a/src/main/charts/bitbucket/templates/statefulset-mesh.yaml +++ b/src/main/charts/bitbucket/templates/statefulset-mesh.yaml @@ -47,6 +47,18 @@ spec: securityContext: runAsUser: 0 # make sure we run as root to avoid permission denied {{- end }} + {{- if .Values.bitbucket.mesh.additionalCertificates.secretName }} + - name: import-certs + image: {{ .Values.bitbucket.mesh.image.repository }}:{{ .Values.bitbucket.mesh.image.tag }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: keystore + mountPath: /var/ssl + - name: certs + mountPath: /tmp/crt + command: ["/bin/bash"] + args: ["-c", {{ include "bitbucketMesh.addCrtToKeystoreCmd" . }}] + {{- end }} containers: - name: {{ if .Values.bitbucket.useHelmReleaseNameAsContainerName}}{{ include "common.names.fullname" . }}-mesh{{ else }}{{ .Chart.Name }}-mesh{{ end }} image: {{ .Values.bitbucket.mesh.image.repository }}:{{ .Values.bitbucket.mesh.image.tag }} @@ -74,6 +86,10 @@ spec: mountPath: {{ .mountPath }}/{{ .key }} subPath: {{ .key }} {{ end }} + {{- if .Values.bitbucket.mesh.additionalCertificates.secretName }} + - name: keystore + mountPath: /var/ssl + {{- end }} {{- include "common.jmx.config.volumeMounts" . | nindent 12 }} {{- with .Values.bitbucket.mesh.resources.container }} resources: @@ -147,6 +163,13 @@ spec: - key: {{ .key }} path: {{ .key }} {{ end }} + {{- if .Values.bitbucket.mesh.additionalCertificates.secretName }} + - name: keystore + emptyDir: {} + - name: certs + secret: + secretName: {{ .Values.bitbucket.mesh.additionalCertificates.secretName }} + {{- end }} {{ include "common.jmx.config.volume" . | nindent 8 }} {{ include "bitbucket.mesh.volumeClaimTemplates" . | nindent 2 }} {{ end }} diff --git a/src/main/charts/bitbucket/templates/statefulset.yaml b/src/main/charts/bitbucket/templates/statefulset.yaml index fb8adc2a3..97d6e8a21 100644 --- a/src/main/charts/bitbucket/templates/statefulset.yaml +++ b/src/main/charts/bitbucket/templates/statefulset.yaml @@ -68,6 +68,18 @@ spec: command: ["sh", "-c", {{ include "bitbucket.sharedHome.permissionFix.command" . | quote }}] {{- end }} {{- include "common.jmx.initContainer" . | nindent 8 }} + {{- if .Values.bitbucket.additionalCertificates.secretName }} + - name: import-certs + image: {{ include "bitbucket.image" . | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: keystore + mountPath: /var/ssl + - name: certs + mountPath: /tmp/crt + command: ["/bin/bash"] + args: ["-c", {{ include "bitbucket.addCrtToKeystoreCmd" . }}] + {{- end }} containers: - name: {{ if .Values.bitbucket.useHelmReleaseNameAsContainerName}}{{ include "common.names.fullname" . }}{{ else }}{{ .Chart.Name }}{{ end }} image: {{ include "bitbucket.image" . | quote }} @@ -131,6 +143,10 @@ spec: subPath: {{ .Values.volumes.sharedHome.subPath | quote }} {{- end }} {{- end }} + {{- if .Values.bitbucket.additionalCertificates.secretName }} + - name: keystore + mountPath: /var/ssl + {{- end }} {{- include "bitbucket.additionalVolumeMounts" . | nindent 12 }} {{- include "common.jmx.config.volumeMounts" . | nindent 12 }} {{- include "bitbucket.additionalLibraries" . | nindent 12 }} diff --git a/src/main/charts/bitbucket/values.yaml b/src/main/charts/bitbucket/values.yaml index fb697ef3e..4cc875ac7 100644 --- a/src/main/charts/bitbucket/values.yaml +++ b/src/main/charts/bitbucket/values.yaml @@ -1028,6 +1028,12 @@ bitbucket: # terminationGracePeriodSeconds: 35 + # -- Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates + # + additionalCertificates: + secretName: + customCmd: + # -- Specifies a list of additional arguments that can be passed to the Bitbucket JVM, e.g. # system properties. # @@ -1102,6 +1108,12 @@ bitbucket: # labelSelector: # matchLabels: + # -- Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates + # + additionalCertificates: + secretName: + customCmd: + # Monitoring # monitoring: diff --git a/src/main/charts/confluence/README.md b/src/main/charts/confluence/README.md index 065313bf6..5bbbab109 100644 --- a/src/main/charts/confluence/README.md +++ b/src/main/charts/confluence/README.md @@ -34,6 +34,7 @@ Kubernetes: `>=1.21.x-0` | confluence.accessLog.localHomeSubPath | string | `"logs"` | The subdirectory within the local-home volume where access logs should be stored. | | confluence.accessLog.mountPath | string | `"/opt/atlassian/confluence/logs"` | The path within the Confluence container where the local-home volume should be mounted in order to capture access logs. | | confluence.additionalBundledPlugins | list | `[]` | Specifies a list of additional Confluence plugins that should be added to the Confluence container. Note plugins installed via this method will appear as bundled plugins rather than user plugins. These should be specified in the same manner as the 'additionalLibraries' property. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ NOTE: only .jar files can be loaded using this approach. OBR's can be extracted (unzipped) to access the associated .jar An alternative to this method is to install the plugins via "Manage Apps" in the product system administration UI. | +| confluence.additionalCertificates | object | `{"customCmd":null,"secretName":null}` | Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates | | confluence.additionalEnvironmentVariables | list | `[]` | Defines any additional environment variables to be passed to the Confluence container. See https://hub.docker.com/r/atlassian/confluence-server for supported variables. | | confluence.additionalJvmArgs | list | `[]` | Specifies a list of additional arguments that can be passed to the Confluence JVM, e.g. system properties. | | confluence.additionalLibraries | list | `[]` | Specifies a list of additional Java libraries that should be added to the Confluence container. Each item in the list should specify the name of the volume that contains the library, as well as the name of the library file within that volume's root directory. Optionally, a subDirectory field can be included to specify which directory in the volume contains the library file. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ | @@ -157,6 +158,7 @@ Kubernetes: `>=1.21.x-0` | serviceAccount.name | string | `nil` | The name of the ServiceAccount to be used by the pods. If not specified, but the "serviceAccount.create" flag is set to 'true', then the ServiceAccount name will be auto-generated, otherwise the 'default' ServiceAccount will be used. https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server | | serviceAccount.role.create | bool | `true` | Create a role for Hazelcast client with privileges to get and list pods and endpoints in the namespace. Set to false if you need to create a Role and RoleBinding manually | | serviceAccount.roleBinding | object | `{"create":true}` | Grant permissions defined in Role (list and get pods and endpoints) to a service account. | +| synchrony.additionalCertificates | object | `{"customCmd":null,"secretName":null}` | Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates | | synchrony.additionalJvmArgs | list | `[]` | Specifies a list of additional arguments that can be passed to the Synchrony JVM, e.g. system properties. | | synchrony.additionalLibraries | list | `[]` | Specifies a list of additional Java libraries that should be added to the Synchrony container. Each item in the list should specify the name of the volume that contains the library, as well as the name of the library file within that volume's root directory. Optionally, a subDirectory field can be included to specify which directory in the volume contains the library file. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ | | synchrony.additionalPorts | list | `[]` | Defines any additional ports for the Synchrony container. | diff --git a/src/main/charts/confluence/templates/_helpers.tpl b/src/main/charts/confluence/templates/_helpers.tpl index 65b3d1c83..49f180f03 100644 --- a/src/main/charts/confluence/templates/_helpers.tpl +++ b/src/main/charts/confluence/templates/_helpers.tpl @@ -224,6 +224,10 @@ on Tomcat's logs directory. THis ensures that Tomcat+Confluence logs get capture mountPath: /opt/atlassian/confluence/confluence/WEB-INF/classes/seraph-config.xml subPath: seraph-config.xml {{- end }} +{{- if .Values.confluence.additionalCertificates.secretName }} +- name: keystore + mountPath: /var/ssl +{{- end }} {{ end }} {{/* @@ -232,6 +236,10 @@ Defines the volume mounts used by the Synchrony container. {{ define "synchrony.volumeMounts" }} - name: synchrony-home mountPath: {{ .Values.volumes.synchronyHome.mountPath | quote }} +{{- if .Values.synchrony.additionalCertificates.secretName }} +- name: keystore + mountPath: /var/ssl +{{- end }} {{ end }} {{/* @@ -395,6 +403,13 @@ For each additional plugin declared, generate a volume mount that injects that l - key: seraph-config.xml path: seraph-config.xml {{- end }} +{{- if .Values.confluence.additionalCertificates.secretName }} +- name: keystore + emptyDir: {} +- name: certs + secret: + secretName: {{ .Values.confluence.additionalCertificates.secretName }} +{{- end }} {{- end }} {{- define "synchrony.volumes" -}} @@ -405,6 +420,13 @@ For each additional plugin declared, generate a volume mount that injects that l {{- with .Values.volumes.additionalSynchrony }} {{- toYaml . | nindent 0 }} {{- end }} +{{- if .Values.synchrony.additionalCertificates.secretName }} +- name: keystore + emptyDir: {} +- name: certs + secret: + secretName: {{ .Values.synchrony.additionalCertificates.secretName }} +{{- end }} {{- end }} {{- define "confluence.volumes.localHome" -}} @@ -577,3 +599,19 @@ volumeClaimTemplates: {{- . }} {{- end }} {{- end}} + +{{- define "confluence.addCrtToKeystoreCmd" }} +{{- if .Values.confluence.additionalCertificates.customCmd}} +{{ .Values.confluence.additionalCertificates.customCmd}} +{{- else }} +set -e; for crt in /tmp/crt/*.*; do echo "Adding $crt to keystore"; keytool -import -cacerts -storepass changeit -noprompt -alias $(echo $(basename $crt)) -file $crt; done; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts +{{- end }} +{{- end }} + +{{- define "synchrony.addCrtToKeystoreCmd" }} +{{- if .Values.synchrony.additionalCertificates.customCmd}} +{{ .Values.synchrony.additionalCertificates.customCmd}} +{{- else }} +set -e; for crt in /tmp/crt/*.*; do echo "Adding $crt to keystore"; keytool -import -cacerts -storepass changeit -noprompt -alias $(echo $(basename $crt)) -file $crt; done; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts +{{- end }} +{{- end }} diff --git a/src/main/charts/confluence/templates/config-jvm.yaml b/src/main/charts/confluence/templates/config-jvm.yaml index 576f363f7..a049b9654 100644 --- a/src/main/charts/confluence/templates/config-jvm.yaml +++ b/src/main/charts/confluence/templates/config-jvm.yaml @@ -19,6 +19,9 @@ data: {{- if .Values.serviceAccount.eksIrsa.roleArn }} -Daws.webIdentityTokenFile=/var/run/secrets/eks.amazonaws.com/serviceaccount/token {{- end }} + {{- if .Values.confluence.additionalCertificates.secretName }} + -Djavax.net.ssl.trustStore=/var/ssl/cacerts + {{- end }} {{ include "common.jmx.javaagent" . | indent 4 | trim }} {{ include "confluence.sysprop.s3Config" . | indent 4 | trim }} max_heap: {{ .Values.confluence.resources.jvm.maxHeap }} diff --git a/src/main/charts/confluence/templates/statefulset-synchrony.yaml b/src/main/charts/confluence/templates/statefulset-synchrony.yaml index a1c102723..f78fe3ea7 100644 --- a/src/main/charts/confluence/templates/statefulset-synchrony.yaml +++ b/src/main/charts/confluence/templates/statefulset-synchrony.yaml @@ -32,6 +32,19 @@ spec: {{- end }} hostAliases: {{- include "confluence.additionalHosts" . | nindent 8 }} + {{- if .Values.synchrony.additionalCertificates.secretName }} + initContainers: + - name: import-certs + image: {{ include "confluence.image" . | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: keystore + mountPath: /var/ssl + - name: certs + mountPath: /tmp/crt + command: ["/bin/bash"] + args: ["-c", {{ include "synchrony.addCrtToKeystoreCmd" . }}] + {{- end }} containers: - name: synchrony image: {{ include "confluence.image" . | quote }} diff --git a/src/main/charts/confluence/templates/statefulset.yaml b/src/main/charts/confluence/templates/statefulset.yaml index 354d46a54..f83c6f38e 100644 --- a/src/main/charts/confluence/templates/statefulset.yaml +++ b/src/main/charts/confluence/templates/statefulset.yaml @@ -60,6 +60,18 @@ spec: command: ["sh", "-c", {{ include "confluence.sharedHome.permissionFix.command" . | quote }}] {{- end }} {{- include "common.jmx.initContainer" . | nindent 8 }} + {{- if .Values.confluence.additionalCertificates.secretName }} + - name: import-certs + image: {{ include "confluence.image" . | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: keystore + mountPath: /var/ssl + - name: certs + mountPath: /tmp/crt + command: ["/bin/bash"] + args: ["-c", {{ include "confluence.addCrtToKeystoreCmd" . }}] + {{- end }} containers: - name: {{ if .Values.confluence.useHelmReleaseNameAsContainerName}}{{ include "common.names.fullname" . }}{{ else }}{{ .Chart.Name }}{{ end }} image: {{ include "confluence.image" . | quote }} diff --git a/src/main/charts/confluence/templates/synchrony-start-script.yaml b/src/main/charts/confluence/templates/synchrony-start-script.yaml index b0da60d18..9e4c7647f 100644 --- a/src/main/charts/confluence/templates/synchrony-start-script.yaml +++ b/src/main/charts/confluence/templates/synchrony-start-script.yaml @@ -21,6 +21,9 @@ data: -Xss{{ .Values.synchrony.resources.jvm.stackSize }} \ -Dsynchrony.port={{ .Values.synchrony.ports.http }} \ -Dcluster.listen.port={{ .Values.synchrony.ports.hazelcast }} \ + {{- if .Values.synchrony.additionalCertificates.secretName }} + -Djavax.net.ssl.trustStore=/var/ssl/cacerts \ + {{- end }} {{- range .Values.synchrony.additionalJvmArgs }} {{ . }} \ {{- end }} diff --git a/src/main/charts/confluence/values.yaml b/src/main/charts/confluence/values.yaml index a1705d643..a7880af65 100644 --- a/src/main/charts/confluence/values.yaml +++ b/src/main/charts/confluence/values.yaml @@ -933,6 +933,12 @@ confluence: # port 5005 open. enabled: false + # -- Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates + # + additionalCertificates: + secretName: + customCmd: + # Monitoring # monitoring: @@ -1200,6 +1206,12 @@ synchrony: # matchLabels: # app.kubernetes.io/name: confluence-synchrony + # -- Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates + # + additionalCertificates: + secretName: + customCmd: + # Fluentd configuration # # Confluence log collection and aggregation can be enabled using Fluentd. This config diff --git a/src/main/charts/crowd/README.md b/src/main/charts/crowd/README.md index 1c96b8e08..c4182f193 100644 --- a/src/main/charts/crowd/README.md +++ b/src/main/charts/crowd/README.md @@ -34,6 +34,7 @@ Kubernetes: `>=1.21.x-0` | crowd.accessLog.localHomeSubPath | string | `"logs"` | The subdirectory within the local-home volume where access logs should be stored. | | crowd.accessLog.mountPath | string | `"/opt/atlassian/crowd/apache-tomcat/logs"` | The path within the Crowd container where the local-home volume should be mounted in order to capture access logs. | | crowd.additionalBundledPlugins | list | `[]` | Specifies a list of additional Crowd plugins that should be added to the Crowd container. Note plugins installed via this method will appear as bundled plugins rather than user plugins. These should be specified in the same manner as the 'additionalLibraries' property. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ NOTE: only .jar files can be loaded using this approach. OBR's can be extracted (unzipped) to access the associated .jar An alternative to this method is to install the plugins via "Manage Apps" in the product system administration UI. | +| crowd.additionalCertificates | object | `{"customCmd":null,"secretName":null}` | Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates | | crowd.additionalEnvironmentVariables | list | `[]` | Defines any additional environment variables to be passed to the Crowd container. See https://hub.docker.com/r/atlassian/crowd for supported variables. | | crowd.additionalJvmArgs | list | `[]` | Specifies a list of additional arguments that can be passed to the Crowd JVM, e.g. system properties. | | crowd.additionalLibraries | list | `[]` | Specifies a list of additional Java libraries that should be added to the Crowd container. Each item in the list should specify the name of the volume that contains the library, as well as the name of the library file within that volume's root directory. Optionally, a subDirectory field can be included to specify which directory in the volume contains the library file. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ | diff --git a/src/main/charts/crowd/templates/_helpers.tpl b/src/main/charts/crowd/templates/_helpers.tpl index f1c6d518f..b4c202494 100644 --- a/src/main/charts/crowd/templates/_helpers.tpl +++ b/src/main/charts/crowd/templates/_helpers.tpl @@ -96,6 +96,10 @@ on Tomcat's logs directory. THis ensures that Tomcat+Crowd logs get captured in {{- if .Values.volumes.sharedHome.subPath }} subPath: {{ .Values.volumes.sharedHome.subPath | quote }} {{- end }} +{{- if .Values.crowd.additionalCertificates.secretName }} +- name: keystore + mountPath: /var/ssl +{{- end }} {{ end }} {{/* @@ -190,6 +194,13 @@ For each additional plugin declared, generate a volume mount that injects that l {{- with .Values.volumes.additional }} {{- toYaml . | nindent 0 }} {{- end }} +{{- if .Values.crowd.additionalCertificates.secretName }} +- name: keystore + emptyDir: {} +- name: certs + secret: + secretName: {{ .Values.crowd.additionalCertificates.secretName }} +{{- end }} {{- end }} {{- define "crowd.volumes.localHome" -}} @@ -265,4 +276,12 @@ Define additional hosts here to allow template overrides when used as a sub char {{- with .Values.additionalHosts }} {{- toYaml . }} {{- end }} -{{- end }} \ No newline at end of file +{{- end }} + +{{- define "crowd.addCrtToKeystoreCmd" }} +{{- if .Values.crowd.additionalCertificates.customCmd}} +{{ .Values.crowd.additionalCertificates.customCmd}} +{{- else }} +set -e; for crt in /tmp/crt/*.*; do echo "Adding $crt to keystore"; keytool -import -cacerts -storepass changeit -noprompt -alias $(echo $(basename $crt)) -file $crt; done; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts +{{- end }} +{{- end }} diff --git a/src/main/charts/crowd/templates/config-jvm.yaml b/src/main/charts/crowd/templates/config-jvm.yaml index 8fe33bfed..da161cfa5 100644 --- a/src/main/charts/crowd/templates/config-jvm.yaml +++ b/src/main/charts/crowd/templates/config-jvm.yaml @@ -12,6 +12,9 @@ data: {{ . }} {{- end }} -XX:ActiveProcessorCount={{ include "flooredCPU" .Values.crowd.resources.container.requests.cpu }} + {{- if .Values.crowd.additionalCertificates.secretName }} + -Djavax.net.ssl.trustStore=/var/ssl/cacerts + {{- end }} {{ include "common.jmx.javaagent" . | indent 4 | trim }} max_heap: {{ .Values.crowd.resources.jvm.maxHeap }} min_heap: {{ .Values.crowd.resources.jvm.minHeap }} diff --git a/src/main/charts/crowd/templates/statefulset.yaml b/src/main/charts/crowd/templates/statefulset.yaml index b5dbf78e7..178f16657 100644 --- a/src/main/charts/crowd/templates/statefulset.yaml +++ b/src/main/charts/crowd/templates/statefulset.yaml @@ -60,6 +60,18 @@ spec: command: ["sh", "-c", {{ include "crowd.sharedHome.permissionFix.command" . | quote }}] {{- end }} {{- include "common.jmx.initContainer" . | nindent 8 }} + {{- if .Values.crowd.additionalCertificates.secretName }} + - name: import-certs + image: {{ include "crowd.image" . | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: keystore + mountPath: /var/ssl + - name: certs + mountPath: /tmp/crt + command: ["/bin/bash"] + args: ["-c", {{ include "crowd.addCrtToKeystoreCmd" . }}] + {{- end }} containers: - name: {{ if .Values.crowd.useHelmReleaseNameAsContainerName}}{{ include "common.names.fullname" . }}{{ else }}{{ .Chart.Name }}{{ end }} image: {{ include "crowd.image" . | quote }} diff --git a/src/main/charts/crowd/values.yaml b/src/main/charts/crowd/values.yaml index dc7ec5a6d..9e9268260 100644 --- a/src/main/charts/crowd/values.yaml +++ b/src/main/charts/crowd/values.yaml @@ -388,6 +388,12 @@ crowd: # labelSelector: # matchLabels: + # -- Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates + # + additionalCertificates: + secretName: + customCmd: + # Ingress configuration # # To make the Atlassian product available from outside the K8s cluster an Ingress diff --git a/src/main/charts/jira/README.md b/src/main/charts/jira/README.md index 526509f2e..f53dc9cb2 100644 --- a/src/main/charts/jira/README.md +++ b/src/main/charts/jira/README.md @@ -65,6 +65,7 @@ Kubernetes: `>=1.21.x-0` | jira.accessLog.localHomeSubPath | string | `"log"` | The subdirectory within the local-home volume where access logs should be stored. | | jira.accessLog.mountPath | string | `"/opt/atlassian/jira/logs"` | The path within the Jira container where the local-home volume should be mounted in order to capture access logs. | | jira.additionalBundledPlugins | list | `[]` | Specifies a list of additional Jira plugins that should be added to the Jira container. Note plugins installed via this method will appear as bundled plugins rather than user plugins. These should be specified in the same manner as the 'additionalLibraries' property. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ NOTE: only .jar files can be loaded using this approach. OBR's can be extracted (unzipped) to access the associated .jar An alternative to this method is to install the plugins via "Manage Apps" in the product system administration UI. | +| jira.additionalCertificates | object | `{"customCmd":null,"secretName":null}` | Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates | | jira.additionalEnvironmentVariables | list | `[]` | Defines any additional environment variables to be passed to the Jira container. See https://hub.docker.com/r/atlassian/jira-software for supported variables. | | jira.additionalJvmArgs | list | `[]` | | | jira.additionalLibraries | list | `[]` | Specifies a list of additional Java libraries that should be added to the Jira container. Each item in the list should specify the name of the volume that contains the library, as well as the name of the library file within that volume's root directory. Optionally, a subDirectory field can be included to specify which directory in the volume contains the library file. Additional details: https://atlassian.github.io/data-center-helm-charts/examples/external_libraries/EXTERNAL_LIBS/ | diff --git a/src/main/charts/jira/templates/_helpers.tpl b/src/main/charts/jira/templates/_helpers.tpl index a6e540d47..794c3a27d 100644 --- a/src/main/charts/jira/templates/_helpers.tpl +++ b/src/main/charts/jira/templates/_helpers.tpl @@ -111,6 +111,10 @@ on Tomcat's logs directory. THis ensures that Tomcat+Jira logs get captured in t mountPath: /opt/atlassian/jira/atlassian-jira/WEB-INF/classes/seraph-config.xml subPath: seraph-config.xml {{- end }} +{{- if .Values.jira.additionalCertificates.secretName }} +- name: keystore + mountPath: /var/ssl +{{- end }} {{- end }} {{/* @@ -223,6 +227,13 @@ For each additional plugin declared, generate a volume mount that injects that l - key: seraph-config.xml path: seraph-config.xml {{- end }} +{{- if .Values.jira.additionalCertificates.secretName }} +- name: keystore + emptyDir: {} +- name: certs + secret: + secretName: {{ .Values.jira.additionalCertificates.secretName }} +{{- end }} {{- end }} {{- define "jira.volumes.localHome" -}} @@ -366,3 +377,11 @@ volumeClaimTemplates: {{- . }} {{- end }} {{- end}} + +{{- define "jira.addCrtToKeystoreCmd" }} +{{- if .Values.jira.additionalCertificates.customCmd}} +{{ .Values.jira.additionalCertificates.customCmd}} +{{- else }} +set -e; for crt in /tmp/crt/*.*; do echo "Adding $crt to keystore"; keytool -import -cacerts -storepass changeit -noprompt -alias $(echo $(basename $crt)) -file $crt; done; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts +{{- end }} +{{- end }} diff --git a/src/main/charts/jira/templates/config-jvm.yaml b/src/main/charts/jira/templates/config-jvm.yaml index bef7ed4f8..5db47554a 100644 --- a/src/main/charts/jira/templates/config-jvm.yaml +++ b/src/main/charts/jira/templates/config-jvm.yaml @@ -14,6 +14,9 @@ data: {{- if .Values.serviceAccount.eksIrsa.roleArn }} -Daws.webIdentityTokenFile=/var/run/secrets/eks.amazonaws.com/serviceaccount/token {{- end }} + {{- if .Values.jira.additionalCertificates.secretName }} + -Djavax.net.ssl.trustStore=/var/ssl/cacerts + {{- end }} {{ include "common.jmx.javaagent" . | indent 4 | trim }} max_heap: {{ .Values.jira.resources.jvm.maxHeap }} min_heap: {{ .Values.jira.resources.jvm.minHeap }} diff --git a/src/main/charts/jira/templates/statefulset.yaml b/src/main/charts/jira/templates/statefulset.yaml index d3e5407b4..19f673c82 100644 --- a/src/main/charts/jira/templates/statefulset.yaml +++ b/src/main/charts/jira/templates/statefulset.yaml @@ -60,6 +60,18 @@ spec: command: ["sh", "-c", {{ include "jira.sharedHome.permissionFix.command" . | quote }}] {{- end }} {{- include "common.jmx.initContainer" . | nindent 8 }} + {{- if .Values.jira.additionalCertificates.secretName }} + - name: import-certs + image: {{ include "jira.image" . | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: keystore + mountPath: /var/ssl + - name: certs + mountPath: /tmp/crt + command: ["/bin/bash"] + args: ["-c", {{ include "jira.addCrtToKeystoreCmd" . }}] + {{- end }} containers: - name: {{ if .Values.jira.useHelmReleaseNameAsContainerName}}{{ include "common.names.fullname" . }}{{ else }}{{ .Chart.Name }}{{ end }} image: {{ include "jira.image" . | quote }} diff --git a/src/main/charts/jira/values.yaml b/src/main/charts/jira/values.yaml index a914770d8..79dd6eeb0 100644 --- a/src/main/charts/jira/values.yaml +++ b/src/main/charts/jira/values.yaml @@ -795,6 +795,12 @@ jira: # labelSelector: # matchLabels: + # -- Certificates to be added to Java truststore. Provide reference to a secret that contains the certificates + # + additionalCertificates: + secretName: + customCmd: + # Monitoring # monitoring: diff --git a/src/test/config/kind/common-values.yaml b/src/test/config/kind/common-values.yaml index 9e78e9407..337715623 100644 --- a/src/test/config/kind/common-values.yaml +++ b/src/test/config/kind/common-values.yaml @@ -21,6 +21,10 @@ DC_APP_REPLACEME: # this applies to Bamboo only and will be ignored in other Helm charts disableAgentAuth: true + # check if init container not failing when importing a custom crt into the default Java keystore + additionalCertificates: + secretName: certificate + database: type: DB_TYPE_REPLACEME url: jdbc:postgresql://postgres:5432/DC_APP_REPLACEME @@ -61,3 +65,5 @@ synchrony: requests: cpu: 20m memory: 1G + additionalCertificates: + secretName: certificate diff --git a/src/test/java/test/AdditionalCertificatesTest.java b/src/test/java/test/AdditionalCertificatesTest.java new file mode 100644 index 000000000..2a82d1803 --- /dev/null +++ b/src/test/java/test/AdditionalCertificatesTest.java @@ -0,0 +1,206 @@ +package test; + +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import test.helm.Helm; +import test.model.Product; + +import java.util.Map; + +import static test.jackson.JsonNodeAssert.assertThat; +import static test.model.Kind.ConfigMap; + +public class AdditionalCertificatesTest { + private Helm helm; + String expectedCmd = "set -e; for crt in /tmp/crt/*.*; do echo \"Adding $crt to keystore\"; keytool -import -cacerts -storepass changeit -noprompt -alias $(echo $(basename $crt)) -file $crt; done; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts"; + + @BeforeEach + void initHelm(TestInfo testInfo) { + helm = new Helm(testInfo); + } + + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE) + void additional_certificates_jvm_prop(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".additionalCertificates.secretName", "mycrt", + "volumes.sharedHome.persistentVolumeClaim.create", "true" + )); + final var jvmConfigMap = resources.get(ConfigMap, product.getHelmReleaseName() + "-jvm-config"); + assertThat(jvmConfigMap.getConfigMapData().path("additional_jvm_args")).hasTextContaining("-Djavax.net.ssl.trustStore=/var/ssl/cacerts"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"confluence"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_jvm_prop_synchrony(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + "synchrony.enabled", "true", + "synchrony.additionalCertificates.secretName", "mycrt" + )); + final var jvmConfigMap = resources.get(ConfigMap, product.getHelmReleaseName() + "-synchrony-entrypoint"); + assertThat(jvmConfigMap.getConfigMapData().path("start-synchrony.sh")).hasTextContaining("-Djavax.net.ssl.trustStore=/var/ssl/cacerts"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bitbucket"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_jvm_prop_mesh(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".mesh.enabled", "true", + product.name() + ".mesh.additionalCertificates.secretName", "mycrt" + )); + final var bitbucketMeshJvmConfigMap = resources.get(ConfigMap, product.getHelmReleaseName() + "-jvm-config-mesh"); + assertThat(bitbucketMeshJvmConfigMap.getConfigMapData().path("additional_jvm_args")).hasTextContaining("-Djavax.net.ssl.trustStore=/var/ssl/cacerts"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE) + void additional_certificates_init_container(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".additionalCertificates.secretName", "mycrt", + "volumes.sharedHome.persistentVolumeClaim.create", "true" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()); + assertThat(statefulSet.getInitContainers().get(1).path("name")).hasTextEqualTo("import-certs"); + assertThat(statefulSet.getInitContainers().get(1).path("volumeMounts").path(0).get("name")).hasTextEqualTo("keystore"); + assertThat(statefulSet.getInitContainers().get(1).path("volumeMounts").path(0).get("mountPath")).hasTextEqualTo("/var/ssl"); + assertThat(statefulSet.getInitContainers().get(1).path("args").path(1)).hasTextEqualTo(expectedCmd); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"confluence"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_init_container_synchrony(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + "synchrony.enabled", "true", + "synchrony.additionalCertificates.secretName", "mycrt" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()+"-synchrony"); + assertThat(statefulSet.getInitContainers().get(0).path("name")).hasTextEqualTo("import-certs"); + assertThat(statefulSet.getInitContainers().get(0).path("volumeMounts").path(0).get("name")).hasTextEqualTo("keystore"); + assertThat(statefulSet.getInitContainers().get(0).path("volumeMounts").path(0).get("mountPath")).hasTextEqualTo("/var/ssl"); + assertThat(statefulSet.getInitContainers().get(0).path("args").path(1)).hasTextEqualTo(expectedCmd); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bitbucket"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_init_container_bitbucket_mesh(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".mesh.enabled", "true", + product.name() + ".mesh.additionalCertificates.secretName", "mycrt" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()+"-mesh"); + assertThat(statefulSet.getInitContainers().get(0).path("name")).hasTextEqualTo("import-certs"); + assertThat(statefulSet.getInitContainers().get(0).path("volumeMounts").path(0).get("name")).hasTextEqualTo("keystore"); + assertThat(statefulSet.getInitContainers().get(0).path("volumeMounts").path(0).get("mountPath")).hasTextEqualTo("/var/ssl"); + assertThat(statefulSet.getInitContainers().get(0).path("args").path(1)).hasTextEqualTo(expectedCmd); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE) + void additional_certificates_volumeMounts(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".additionalCertificates.secretName", "mycrt" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()); + JsonNode keystoreVolumeMount = statefulSet.getContainer(product.name()).getVolumeMount("keystore"); + assertThat(keystoreVolumeMount.path("mountPath")).hasTextEqualTo("/var/ssl"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bitbucket"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_volumeMounts_bitbucket_mesh(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".mesh.enabled", "true", + product.name() + ".mesh.additionalCertificates.secretName", "mycrt" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()+"-mesh"); + JsonNode keystoreVolumeMount = statefulSet.getContainer(product.name()+"-mesh").getVolumeMount("keystore"); + assertThat(keystoreVolumeMount.path("mountPath")).hasTextEqualTo("/var/ssl"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"confluence"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_volumeMounts_synchrony(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + "synchrony.enabled", "true", + "synchrony.additionalCertificates.secretName", "mycrt" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()+"-synchrony"); + JsonNode keystoreVolumeMount = statefulSet.getContainer("synchrony").getVolumeMount("keystore"); + assertThat(keystoreVolumeMount.path("mountPath")).hasTextEqualTo("/var/ssl"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE) + void additional_certificates_volumes(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".additionalCertificates.secretName", "mycrt" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()); + assertThat(statefulSet.getVolume("keystore").get().path("emptyDir")).isEmpty(); + assertThat(statefulSet.getVolume("certs").get().path("secret").path("secretName")).hasTextEqualTo("mycrt"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bitbucket"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_volumes_bitbucket_mesh(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".mesh.enabled", "true", + product.name() + ".mesh.additionalCertificates.secretName", "mycrt" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()+"-mesh"); + assertThat(statefulSet.getVolume("keystore").get().path("emptyDir")).isEmpty(); + assertThat(statefulSet.getVolume("certs").get().path("secret").path("secretName")).hasTextEqualTo("mycrt"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"confluence"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_volumes_synchrony(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + "synchrony.enabled", "true", + "synchrony.additionalCertificates.secretName", "mycrt" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()+"-synchrony"); + assertThat(statefulSet.getVolume("keystore").get().path("emptyDir")).isEmpty(); + assertThat(statefulSet.getVolume("certs").get().path("secret").path("secretName")).hasTextEqualTo("mycrt"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE) + void additional_certificates_custom_cmd(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".additionalCertificates.secretName", "mycrt", + "volumes.sharedHome.persistentVolumeClaim.create", "true", + product.name() + ".additionalCertificates.customCmd", "echo \"My custom command\"" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()); + assertThat(statefulSet.getInitContainers().get(1).path("args").path(1)).hasTextEqualTo("echo \"My custom command\""); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bitbucket"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_custom_cmd_bitbucket_mesh(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + product.name() + ".mesh.enabled", "true", + product.name() + ".mesh.additionalCertificates.secretName", "mycrt", + product.name() + ".mesh.additionalCertificates.customCmd", "echo \"My custom command\"" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()+"-mesh"); + assertThat(statefulSet.getInitContainers().get(0).path("args").path(1)).hasTextEqualTo("echo \"My custom command\""); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"confluence"}, mode = EnumSource.Mode.INCLUDE) + void additional_certificates_synchrony_custom_cmd(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + "synchrony.enabled", "true", + "synchrony.additionalCertificates.secretName", "mycrt", + "synchrony.additionalCertificates.customCmd", "echo \"My custom command\"" + )); + final var statefulSet = resources.getStatefulSet(product.getHelmReleaseName()+"-synchrony"); + assertThat(statefulSet.getInitContainers().get(0).path("args").path(1)).hasTextEqualTo("echo \"My custom command\""); + } +} diff --git a/src/test/resources/expected_helm_output/bamboo-agent/output.yaml b/src/test/resources/expected_helm_output/bamboo-agent/output.yaml index df81bd82b..134de7dc0 100644 --- a/src/test/resources/expected_helm_output/bamboo-agent/output.yaml +++ b/src/test/resources/expected_helm_output/bamboo-agent/output.yaml @@ -46,7 +46,6 @@ spec: template: metadata: annotations: - checksum/config-jvm: 327e7ecaef5c146059f3d4f586c1be407403c2197a16c89686a9b3d4e430bdeb labels: app.kubernetes.io/name: bamboo-agent app.kubernetes.io/instance: unittest-bamboo-agent diff --git a/src/test/resources/expected_helm_output/bamboo/output.yaml b/src/test/resources/expected_helm_output/bamboo/output.yaml index b7824a3f6..5767830e6 100644 --- a/src/test/resources/expected_helm_output/bamboo/output.yaml +++ b/src/test/resources/expected_helm_output/bamboo/output.yaml @@ -147,7 +147,6 @@ spec: template: metadata: annotations: - checksum/config-jvm: 649cd0d664c2bd3a2a110a443ba424ee0c042453567313692c48e4eede6ee447 labels: app.kubernetes.io/name: bamboo app.kubernetes.io/instance: unittest-bamboo diff --git a/src/test/resources/expected_helm_output/bitbucket/output.yaml b/src/test/resources/expected_helm_output/bitbucket/output.yaml index 130e6ff4f..fe2254d72 100644 --- a/src/test/resources/expected_helm_output/bitbucket/output.yaml +++ b/src/test/resources/expected_helm_output/bitbucket/output.yaml @@ -228,7 +228,6 @@ spec: template: metadata: annotations: - checksum/config-jvm: db89b933a78ce40c704c28a43ad838c5cd69bc7f29eed430073ee69d43739dd2 labels: app.kubernetes.io/name: bitbucket-mesh app.kubernetes.io/instance: unittest-bitbucket @@ -351,7 +350,6 @@ spec: template: metadata: annotations: - checksum/config-jvm: 913bbd0e175be8c1070e165be31cd49b51de5090005b14472255801e07e37120 labels: app.kubernetes.io/name: bitbucket app.kubernetes.io/instance: unittest-bitbucket diff --git a/src/test/resources/expected_helm_output/confluence/output.yaml b/src/test/resources/expected_helm_output/confluence/output.yaml index a24fba7fb..cf970bc51 100644 --- a/src/test/resources/expected_helm_output/confluence/output.yaml +++ b/src/test/resources/expected_helm_output/confluence/output.yaml @@ -177,7 +177,6 @@ spec: template: metadata: annotations: - checksum/config-jvm: 5c7e4f3183d49bd4e8c82a29b06246e551e4120042495652f1f9b27a0599a882 labels: app.kubernetes.io/name: confluence-synchrony app.kubernetes.io/instance: unittest-confluence @@ -259,7 +258,6 @@ spec: template: metadata: annotations: - checksum/config-jvm: 45fb5597dbc00f885f1724566f712a443ce1edde897b800d9b6ce2ee4859de73 labels: app.kubernetes.io/name: confluence app.kubernetes.io/instance: unittest-confluence diff --git a/src/test/resources/expected_helm_output/crowd/output.yaml b/src/test/resources/expected_helm_output/crowd/output.yaml index abe0eb62a..38f17bf73 100644 --- a/src/test/resources/expected_helm_output/crowd/output.yaml +++ b/src/test/resources/expected_helm_output/crowd/output.yaml @@ -126,7 +126,6 @@ spec: template: metadata: annotations: - checksum/config-jvm: 89722e2178661d279149ad3b81efe91cec67dd01a539a66f08b9fd4c28f0a5b0 labels: app.kubernetes.io/name: crowd app.kubernetes.io/instance: unittest-crowd diff --git a/src/test/resources/expected_helm_output/jira/output.yaml b/src/test/resources/expected_helm_output/jira/output.yaml index 8d4f3ffdc..e9eb4272a 100644 --- a/src/test/resources/expected_helm_output/jira/output.yaml +++ b/src/test/resources/expected_helm_output/jira/output.yaml @@ -124,7 +124,6 @@ spec: template: metadata: annotations: - checksum/config-jvm: 3c107375d0ec9bbf3a6fa539dd59078b3983f14de8d394140528260debdd6caa labels: app.kubernetes.io/name: jira app.kubernetes.io/instance: unittest-jira diff --git a/src/test/scripts/kind/deploy_app.sh b/src/test/scripts/kind/deploy_app.sh index 0a5238e78..754af5b20 100755 --- a/src/test/scripts/kind/deploy_app.sh +++ b/src/test/scripts/kind/deploy_app.sh @@ -30,6 +30,9 @@ create_secrets() { kubectl create secret generic ${DC_APP}-app-license \ --from-literal=license=${LICENSE} \ -n atlassian + + openssl req -x509 -newkey rsa:4096 -keyout key.pem -out mycert.crt -days 365 -nodes -subj '/CN=localhost' + kubectl create secret generic certificate --from-file=mycert.crt=mycert.crt -n atlassian } deploy_app() { From e961e5c8c1f4cfa0734b0464f163b41be0c8e315 Mon Sep 17 00:00:00 2001 From: Yevhen Ivantsov Date: Sun, 17 Sep 2023 16:18:11 +1000 Subject: [PATCH 2/2] Add docs --- docs/docs/userguide/CONFIGURATION.md | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/docs/docs/userguide/CONFIGURATION.md b/docs/docs/userguide/CONFIGURATION.md index acf98b4a5..c78060fd4 100644 --- a/docs/docs/userguide/CONFIGURATION.md +++ b/docs/docs/userguide/CONFIGURATION.md @@ -519,3 +519,53 @@ readinessProbe: !!!warning "`startupProbe` and `livenessProbe`" Both `startupProbe` and `livenessProbe` are disabled by default. Make sure you go through the [Kubernetes documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/){.external} before enabling such probes. Misconfiguration can result in unwanted container restarts and failed "cold" starts. + +## :material-certificate: Self Signed Certificates + +Accessing applications or websites that are encrypted with SSL using certificates not signed by a public authority will result in a connection error. The stacktrace will contain the following: + +```shell +caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed:sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target +``` + +To fix the issue, self signed certificates need to be added to Java truststore. First off, you need to create a [Kubernetes secret](https://kubernetes.io/docs/concepts/configuration/secret/){.external} containing base64-encoded certificate(s). Here's an example [kubectl command](https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/#use-source-files){.external} to create a secret from 2 local files: + +```shell +kubectl create secret generic dev-certificates \ + --from-file=stg.crt=./stg.crt \ + --from-file=dev.crt=./dev.crt -n $namespace +``` + +The resulting secret will have the following data: + +```yaml +data: + stg.crt: base64encodedstgcrt + dev.crt: base64encodeddevcrt +``` + +!!!info "You can have as many keys (certificates) in the secret as required. All keys will be mounted as files to `/tmp/crt` in the container and imported into Java truststore. In the example above, certificates will be mounted as `/tmp/crt/stg.crt` and `/tmp/crt/dev.crt`. File extension in the secret keys does not matter as long as the file is a valid certificate." + +Once done, provide the secret name in Helm values: + +```yaml +jira: + additionalCertificates: + secretName: dev-certificates +``` + +Helm chart will add additional `volumeMounts` and `volumes` to the pod(s), as well as an extra init container that will: + +* copy the default Java cacerts to a runtime volume shared between the init container and the main container at `/var/ssl` +* run [keytool -import](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html){.external} to import all certificates in `/tmp/crt` mounted from `dev-certificates` secret to `/var/ssl/cacerts` + +`-Djavax.net.ssl.trustStore=/var/ssl/cacerts` system property will be automatically added to `JVM_SUPPORT_RECOMMENDED_ARGS` environment variable. + +If necessary, it is possible to override the default `keytool -import` command: + +```yaml +jira: + additionalCertificates: + secretName: dev-certificates + customCmd: keytool -import ... +``` \ No newline at end of file