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

Reload TLS Certificate from memory #43152

Open
amusarra opened this issue Sep 9, 2024 · 2 comments
Open

Reload TLS Certificate from memory #43152

amusarra opened this issue Sep 9, 2024 · 2 comments
Labels
area/security kind/enhancement New feature or request

Comments

@amusarra
Copy link
Contributor

amusarra commented Sep 9, 2024

Description

At the moment, reloading certificates can only be done from the file system, for example, by configuring the property quarkus.tls.https.trust-store.pem.certs=certs/ca_cert.pem. The certificate file must exist, if it does not exist, the application will not be able to start.

Following the documentation 7. Reloading certificates I wrote this piece of code where I used the available tlsConfiguration.getTrustStore().setCertificateEntry API to set the CA certificates coming from an external source (network endpoint, NFS Filesystem, etc.).

@ApplicationScoped
public class GovCertificateUpdater {

  @ConfigProperty(name = "gov.trust.certs.url")
  String certsUrl;

  @Inject
  Logger log;

  @Inject
  GovCertParser certParser;

  @Inject
  Event<CertificateUpdatedEvent> certificateUpdatedEvent;

  @Inject
  TlsConfigurationRegistry tlsConfigurationRegistry;

  public void reload() {
    TlsConfiguration tlsConfiguration = tlsConfigurationRegistry.get("https").orElseThrow();

    try {
      // Path to the PEM file containing all CA certificates
      String pathGovBundlePemTrustStore = certParser.getOutputPathPemBundle() + File.separator +
                                          certParser.getOutputPemBundleFileName();

      Path caCertsPath = Path.of(pathGovBundlePemTrustStore);

      // Load the CA certificates
      CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
      try (var certStream = Files.newInputStream(caCertsPath)) {
        var certs = certFactory.generateCertificates(certStream);
        for (var cert : certs) {
          X509Certificate caCert = (X509Certificate) cert;

          tlsConfiguration.getTrustStore()
              .setCertificateEntry(caCert.getSubjectX500Principal().getName(), caCert);
          
          log.info("Loaded CA certificate: " + caCert.getSubjectX500Principal().getName());
        }
      }

      // Reload the TLS configuration to apply the changes
      if (tlsConfiguration.reload()) {
        log.info("TLS configuration reloaded to apply new CA certificates");

        certificateUpdatedEvent.fire(new CertificateUpdatedEvent("https", tlsConfiguration));
      } else {
        log.error("Failed to reload TLS configuration");
      }

    } catch (Exception e) {
      log.error("Failed to load additional CA certificates", e);
    }
  }

  @Scheduled(every = "2h")
  public void updateCertificates() {
    // Download bundle PEM certs
    ....

    // reload the certificates via TLS configuration to apply the changes
    reload();
  }
}

In this way I am not forced to configure (on application.properties) the location on the file system of the certificates but I can take them dynamically from external sources.

I hope I was clear in my explanation. Please refer to the issue #43135

Implementation ideas

No response

@geoand
Copy link
Contributor

geoand commented Sep 10, 2024

cc @cescoffier

@cescoffier
Copy link
Member

First, I don't believe keeping the certificate in memory is a good idea. It would not survive restart and would not work when there are multiple instances of the application (like in Kubernetes). The code is not working because the call to reload erases all the changes and reloads from the file system.

You cannot use the VertxCertificateHolder (implementation of the runtime TLSConfiguration to do this). However, you can implement and register your own TLSConfiguration. While application code can do it, I recommend using an extension to implement such a feature.

Basically, the extension will configure and maintain its certificates, register it in the TLS registry, and do whatever it wants for the reload.

Now, I wonder if using an empty p12 file at startup and adding the entries like you do before calling reload would work. I guess it will (I don't remember checking if a p12 file is empty if you do not set an alias). The advantage of a P12 file is that you can add and remove multiple certs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/security kind/enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants