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

SslBundle - Allow loading PEM encoded trusted certificates from multiple files #38754

Open
bgK opened this issue Dec 12, 2023 · 8 comments
Open
Labels
status: pending-design-work Needs design work before any code can be developed theme: ssl Issues related to ssl support type: enhancement A general enhancement
Milestone

Comments

@bgK
Copy link

bgK commented Dec 12, 2023

At the moment, PEM SslBundles can be instantiated through the following properties:

spring:
  ssl:
    bundle:
      pem:
        client:
          truststore:
            certificate: "classpath:client.crt"

Where client.crt can contain multiple certificates.

In some situations, multiple very different certificates need to be trusted. For instance:

  • An external service changes the CA it uses to sign the certificate it exposes. Both the old one and the new one need to be trusted on the client side to avoid downtime.
  • When exposing a service with mutual authentication enabled, and when the corporate CA is re-used for multiple systems, it can be somewhat easy to obtain a keypair for a given FQDN. Trusting the CA is not an option, the trust store must contain all the allowed certificates.

While concatenating all the trusted certificates in the same file is an option, it makes it quite hard to see at a glance which certificates are trusted, as they are PEM-encoded.
It would be nice to be able to use file names to identify the certificates:

spring:
  ssl:
    bundle:
      pem:
        client:
          truststore:
            certificates:
            - "classpath:allowed-client1.crt"
            - "classpath:allowed-client2.crt"
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 12, 2023
@philwebb philwebb added the theme: ssl Issues related to ssl support label Dec 12, 2023
@philwebb
Copy link
Member

There might be some overlap here with #38242 where we are looking to support directory glob patterns. If the order of the certificates isn't important we might be able to support something like this:

spring:
  ssl:
    bundle:
      pem:
        client:
          truststore:
            certificate: "/my/certs/allowed-*.crt"
            select: all

@philwebb philwebb added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Dec 12, 2023
@philwebb philwebb added this to the 3.x milestone Dec 12, 2023
@philwebb philwebb added the for: team-meeting An issue we'd like to discuss as a team to make progress label Dec 12, 2023
@philwebb philwebb added the status: pending-design-work Needs design work before any code can be developed label Dec 12, 2023
@bgK
Copy link
Author

bgK commented Dec 13, 2023

I hadn't seen #38242. Indeed, it fills almost the same need. It's nice in that the properties are backwards compatible and makes it easy to have large numbers of certificates.

Here's a use case that I don't think is easily covered by #38242. A Spring Boot app deployed in Kubernetes. It calls an external service for which some instances are hosted in the same cluster, some instances are scaled out to an external provider. The internal instances are exposed using the Kubernetes CA, the other instances use some other CA. Kubernetes bind mounts the CA at a fixed location inside the pod /var/run/secrets/kubernetes.io/serviceaccount/ca.crt:

spring:
  ssl:
    bundle:
      pem:
        client:
          truststore:
            certificates:
            - "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
            - "classpath:other-ca.crt"

IMO, me both #38754 and this are good enhancements.

@philwebb philwebb modified the milestones: 3.x, 3.3.x Dec 13, 2023
@philwebb philwebb removed the for: team-meeting An issue we'd like to discuss as a team to make progress label Dec 13, 2023
@mhalbritter mhalbritter modified the milestones: 3.3.x, 3.x Mar 26, 2024
@dopsun
Copy link

dopsun commented May 30, 2024

Add to this ticket about a similar but not same use case:

to allow multiple certs from a folder for different hostnames. This may open the possibility to support one Spring Boot application serving traffic for multiple domains.

Underlying TomCat 8.5 already "allows multiple certificates with different names to be associated with a single TLS connector" (link).

@scottfrederick
Copy link
Contributor

scottfrederick commented May 30, 2024

@dopsun Spring Boot 3.3 added support for configuring SSL bundles for hostnames (SNI). Does this meet your requirement?

@dopsun
Copy link

dopsun commented May 30, 2024

@scottfrederick Thanks for sharing this, I have not been aware of this feature yet. A quick look at the link you shared, it seems what I'm waiting for. Will try it out ASAP.

@quarky42
Copy link

quarky42 commented Jul 1, 2024

I also need to be able to add multiple PEM format certificates to the truststore. Being able to add them in with wildcard or by name would be very helpful.

Having it be limited per hostname is not useful in my case and, my case would be covered by the other two options. I can see how mapping certs to specific hostnames is a good and useful config in other situations.

@chicobento
Copy link
Contributor

I hadn't seen #38242. Indeed, it fills almost the same need. It's nice in that the properties are backwards compatible and makes it easy to have large numbers of certificates.

Here's a use case that I don't think is easily covered by #38242. A Spring Boot app deployed in Kubernetes. It calls an external service for which some instances are hosted in the same cluster, some instances are scaled out to an external provider. The internal instances are exposed using the Kubernetes CA, the other instances use some other CA. Kubernetes bind mounts the CA at a fixed location inside the pod /var/run/secrets/kubernetes.io/serviceaccount/ca.crt:

spring:
  ssl:
    bundle:
      pem:
        client:
          truststore:
            certificates:
            - "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
            - "classpath:other-ca.crt"

IMO, me both #38754 and this are good enhancements.

That's a great idea. I'd also like to add a little bit of salt here.
Suppose that your application has 2 well-known potential client services, foo and bar and you want to support mTLS. Each client service has a specific intermediate CA.
Client services are dynamically installed/removed from the cluster. For improved resiliency/manageability, you dont want to change the deployment of your application whenever a new client service is installed in the cluster: the client CAs must be dynamically added/removed to your server truststore as their services installed/removed from the cluster.

With k8s you should be able to mount the volumes as optional:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: mypod
      image: myimage
      volumeMounts:
        - name: foo-ca
          mountPath: /var/run/secrets/foo/ca.crt
        - name: bar-ca
          mountPath: /var/run/secrets/bar/ca.crt
  volumes:
    - name: foo-ca
      secret:
        secretName: foo-ca
        optional: true
    - name: bar-ca
      secret:
        secretName: bar-ca
        optional: true

In this scenario, there should be a way to flag the SSLBundle API that the CA is optional, and when not available it should still attempt to load the other CAs and not prevent the service from starting.
As the client services are installed/uninstalled, the secrets and mounts will be updated and a new SSLBundle should be emitted via SslBundles.addBundleUpdateHandler callback so components supporting hot-reload will be able to reconfigure themselves. In the absence of the CAs (in case none of the services are installed), we should consider whatever the default behavior is, i.e: not trust any CA.

For supporting this feature we can extend your proposal with something like:

spring:
  ssl:
    bundle:
      pem:
        client:
          truststore:
            certificates:
            - "optional:/var/run/secrets/foo/ca.crt"
            - "optional:/var/run/secrets/bar/ca.crt"

@chicobento
Copy link
Contributor

Regarding my comment above, we currently have an in-house component for supporting this and we are willing to contribute if thats acceptable from spring team side. Please let me know your views on this so we can start.

elisabeteaprcd pushed a commit to elisabeteaprcd/spring-boot that referenced this issue Aug 19, 2024
**Problem Statement**

The challenge is the dynamic updating and management of certificates in an application that supports mTLS and interacts with client services that are frequently added and removed.

Suppose your application needs to support mTLS with two well-known client services, `foo` and `bar`, each having a specific intermediate CA (Certificate Authority). Client services are dynamically installed/removed from the cluster. For improved resiliency/manageability, you do not want to change the deployment of your application whenever a new client service is installed in the cluster: the client CAs must be dynamically added/removed to your server truststore as their services installed/removed from the cluster.

**Proposed Solution**

This PR aims to address this issue by allowing certificates to be marked as optional. With this feature, the application can start and continue running regardless of the availability of these certificates.

**Current Status**

This pull request is currently a draft/proof of concept. It does not yet include tests or documentation and is intended to gather feedback. The implementation includes several TODOs, such as:

- Extending optionality to the private key (only added to the certificate).
- Adding necessary validations.

An example of `application.yaml` with optionality would look like:

```
spring.ssl.bundle.pem.server.keystore.certificate=/var/run/secrets/foo/server.crt
spring.ssl.bundle.pem.server.keystore.private-key=/var/run/secrets/foo/server.key
spring.ssl.bundle.pem.server.keystore.private-key-password=123456
spring.ssl.bundle.pem.server.reload-on-update=true
spring.ssl.bundle.pem.server.truststore.certificate=optional:/var/run/secrets/foo/ca.crt

```

**Related Issue**

This PR is related to a comment is this issue [spring-projects#38754](spring-projects#38754 (comment))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: pending-design-work Needs design work before any code can be developed theme: ssl Issues related to ssl support type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

8 participants