Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLIP-1826: Add user provided certificates to the default Java truststore #663

Merged
merged 4 commits into from
Sep 18, 2023

Conversation

bianchi2
Copy link
Collaborator

@bianchi2 bianchi2 commented Sep 8, 2023

While it is possible to create a custom truststore and mount it as a secret using additionalFiles and add additionalJvmArguments, it takes a lot of steps. This PR makes adding custom certs a lot easier.

<product>:
  additionalCertificates:
     secretName

If secretName is defined (it must be the secret that has been pre-created outside helm), then:

  • an additional init container is added
  • this container shares a runtime volume with the main container at /var/ssl
  • the secret is mounted at /tmp/crt in the init container
  • keytool command imports all files from /tmp/crt into the default java truststore at $JAVA_HOME/lib/security/cacerts
  • $JAVA_HOME/lib/security/cacerts is copied to /var/ssl/cacerts which is available to the main container
  • -Djavax.net.ssl.trustStore=/var/ssl/cacerts sysprop is added to JVM args to instruct the server to use a custom truststore

The secret can contain multiple certificates, for example:

data:
  stg.crt: base64encodedcrt
  dev.crt: base64encodedcrt

Certs will be mounted as /tmp/crt/stg.crt, /tmp/crt/dev.srt. When certs are imported, filenames are used as aliases.

In addition to unit tests, this feature is enabled in KinD tests to make sure that the init containers don't fail and main containers can find the custom trust store. Here's KinD run.

Checklist

  • I have added unit tests
  • I have applied the change to all applicable products
  • The E2E test has passed (use e2e label)

Copy link
Member

@nanux nanux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great improvement @bianchi2! It is great to streamline the experience of setting up the certificates for all users.

Do you think it would be worth adding a paragraph or two to the user facing documentation?
Something along the lines:

How to use custom certificates

{{- 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when customer has or wants to define different passkey than the default changeit?

Copy link
Collaborator Author

@bianchi2 bianchi2 Sep 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We assume that the official image is used. In case of a custom truststore, it should be already mounted as a secret. So, if a user already mounts a custom truststore, importing new certs happens outside helm then.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right and if customer wants to do something specific in this command, they can always redefine the customCmd.

@@ -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 }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we are running this image because we want to run the keytool util? I am just wondering whether it is necessary to spin up the large product image to run a simple command.

Copy link
Collaborator Author

@bianchi2 bianchi2 Sep 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question :) In fact, it does not really matter if we use temurin image for keytool init container. This won't save us any time since product image needs to be pulled anyways. It's a matter of which phase takes longer - init or containers start. If we pull product image in the init phase, container runtime will have it available when the product container starts. No gains/benefits with either of the approaches, imho.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming this. I was thinking that because we pull the image anyway, it is faster on the pull time but for a very short period we take more resources than we need but as the execution is super quick, it is negligible.

Last thing, if this fails, does it stop container execution, and how will customers get to the error logs (are they sufficient to understand why it failed?)

@bianchi2
Copy link
Collaborator Author

bianchi2 commented Sep 16, 2023

@nanux yes, adding docs with examples is a good idea.

UPDATE. Docs added

{{- 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right and if customer wants to do something specific in this command, they can always redefine the customCmd.

@@ -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 }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming this. I was thinking that because we pull the image anyway, it is faster on the pull time but for a very short period we take more resources than we need but as the execution is super quick, it is negligible.

Last thing, if this fails, does it stop container execution, and how will customers get to the error logs (are they sufficient to understand why it failed?)

@bianchi2
Copy link
Collaborator Author

We do set -e, and if the init container fails other containers do not start. It will be possible to get logs of the failed init container to troubleshoot the error. And yes, customCmd is for use cases when the default cmd fails and it needs to be overridden.

@bianchi2 bianchi2 merged commit 44684ee into main Sep 18, 2023
3 checks passed
@bianchi2 bianchi2 deleted the add-crt-to-keystore branch September 18, 2023 00:38
Comment on lines +522 to +571

## :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 ...
```
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bianchi2 Im just wondering if it makes more sense for this to go under the TROUBLESHOOTING section? Maybe not, but as a compromise a section in the troubleshooter page talking to PKIX exceptions with a link to the fix might be good?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good idea! I'll raise another PR with docs changes as this one has been merged.

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 link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Helm chart will add additional `volumeMounts` and `volumes` to the pod(s), as well as an extra init container that will:
The product Helm chart will add additional `volumeMounts` and `volumes` to the pod(s), as well as an extra init container that will:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants