Spring Boot & reactor-netty https multiple domains - spring-boot

I'm trying to configure a spring cloud gateway, (on top of project Reactor) to serve HTTPS across two domains.
However it appears that it is unable to use SNI to choose the correct certificate to show to the client.
My configuration is as follows:
I've generated two different private key/ certs and both of those are stored in keystore.jks
both of them have different CommonNames and they also have Subject Alternative Names that match the expected domains.
I've entered the common name into my hosts files. To fool browser/curl into thinking that its two seperate domains.
I've configured application.yml as follows
server:
ssl:
enabled: true
# The entire purpose of this project is so that client authentication is needed
client-auth: need
---
spring:
profiles: development
server:
ssl:
key-store: config/keystore.jks
trust-store: config/truststore.jks
The purpose of this is to enable 2 way ssl authentication with two different clients both of whom issue us with their own certificates.
I know netty which is the foundation for spring cloud gateway supports SNI. Is there any way to configure SNI for spring cloud gateway?

It looks like there is an outstanding issue to Support SNI.
https://github.com/spring-cloud/spring-cloud-gateway/issues/1525 which though closed is closed by the issue creator. Not by the framework creators.
This issue links to another issue in reactor.
https://github.com/reactor/reactor-netty/issues/573
Which at the time of writing is not closed.

Related

Spring cloud gateway does not forward HTTPS correctly

I've recently migrated a microservices backend to Spring boot v2.6.1 + spring cloud v2021.0.0 (old version was v2.2.1.RELEASE).
Previously, the setup included a discovery server (Eureka), a gateway (Zuul) and various microservices accessible externally via the gateway. All of these were TLS/secure port enabled so all requests (discovery, registration, gateway forwarding etc) required SSL trust store configuration.
Since Zuul is no longer supported in this version, I am switching to Spring cloud gateway, with the following application.properties setup:
server.port = 8080
eureka.instance.securePortEnabled=false
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/
spring.application.name= gateway-service
spring.cloud.gateway.routes[0].id=first-service
spring.cloud.gateway.routes[0].uri=lb://FIRST-SERVICE
spring.cloud.gateway.routes[0].predicates[0].name=Path
spring.cloud.gateway.routes[0].predicates[0].args.pattern=/firstService/**
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1
security.require-ssl=true
server.ssl.key-store=keystore.p12
server.ssl.key-store-password=password
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=localhost
spring.cloud.gateway.httpclient.ssl.trustedX509Certificates[0]=cert.pem
Without ssl universally disabled, I can access a first service URL via that gateway without issue. With SSL enabled, I can access the first service directly and again no issue:
https://localhost:8100/test
{ "message": "Hello World!" }
However, the gateway is not correctly forwarding the requests either via the correct protocol or with the correct client certificate. I am getting the below error response instead of "Hello World" message:
https://localhost:8080/firstService/test
Bad Request This combination of host and port requires TLS.
(It's worth noting that the Eureka secure port is disabled, and registration/registry fetch is done via HTTP due to issues logged at Error setting custom trust store for Eureka Discovery client by overriding DiscoveryClient.DiscoveryClientOptionalArgs
Ok, this comes down to how the microservices register with Eureka.
Although FIRST-SERVICE was also setup with security.require-ssl=true for direct access it was also necessary to register with Eureka with SSL enabled.
Adding the settings below to application.properties fixed the issue without any changes needed for the gateway configuration:
eureka.client.tls.enabled=true
eureka.client.tls.key-store=file:<path-to-key-store>
eureka.client.tls.key-store-password=<password>
eureka.client.tls.keyStoreType=PKCS12
eureka.client.tls.keyPassword=<password>
eureka.client.tls.trust-store=file:<path-to-trust-store>
eureka.client.tls.trust-store-password=<password>

Disable TLS <1.2 with Spring Boot (embedded Tomcat Server) + Heroku

I'm trying to disable TLS <1.2 (so disable TLS 1.0 & 1.1) on my Spring boot (v. 1.4) app which is running on Heroku.
This app integrates with Salesforce so for security reasons the app (which is an outside website) should not accept TLS connections that are <1.2.
Since the Spring Boot version is 1.4, it really should be as simple as adding a few lines of code to the application.properties file OR the yaml. And, I have tried this in almost every combination.
For example, I currently have the following in my yaml:
Spring:
server:
ssl:
enabled: true
protocol: TLS
enabled-Protocols: [TLSv1.2]
The application builds fine on Heroku and is up and running, but it still accepts TLS 1.0 and 1.1 connections (confirmed via https://www.cdn77.com/tls-test), and I really do not understand why.
I'm trying to determine if this is something related to Heroku. They do manage certificates automatically via what they call ACM. And with ACM you are not able to disable certain versions of TLS. But, my understanding would be that it shouldn't matter, if I'm telling the embedded Tomcat (Spring Boot) server to only accept TLS 1.2 connections, then the Heroku server should actually come after that and hence follow the same rules.
Just to add all of the information I have: server.error.whitelabel.enabled = false is in the application.properties file. This is the only server setting in that file, but I'm just adding in case maybe applications.properties file could interfere with the yaml? (even though I have tried removing it and it did not fix the issue).
So, why is my app / website still accepting TLS <1.2 connections??
Any insight on this would be greatly appreciated!

SpringBoot - Reload SSL Cert using Spring Cloud Config

I'm learning about using SSL Certificates with Spring Boot. Specially using Let's Encrypt ones.
They have the limitation of being expired after 3 months, so they should be renewed and as far as I know, when renewing the certificate we need to restart the Spring Boot app in order to make it load the new one instead.
Some time ago, I was playing around with Eureka and Zuul Gateway, to develop microservices... And I recall I also set a git repo to be used as a Spring Cloud Config. I do not remember well, I think we can use Spring Cloud Config without using the microservice arch.
So my question is: Can we use this Spring Cloud Config mechanism that reload properties to reload the SSL Certificate? The idea would be to trigger the properties reloading mechanism, and as the ssl is configured via those properties, I think maybe it can be reloaded.
I'm planning on automating the process of getting and renewing the Let's Encrypt certificate and avoid the downtime on my app.
Best regards!
SSL certs are applied at the JVM level - neither Spring Boot nor Spring Cloud Config has any control over this, and so to apply a new cert would require a restart of the JVM instance your app runs in, because you've updated your keystore. Being able to dynamically add certs without shutting down the JVM would be a major security flaw.
In the AWS ecosystem, the idea is that if you ever shut down your VM, you lose that VM, and the contents on it are gone forever. With Spring Cloud (Config, Zuul, Eureka) you can spin up VMs that get registered with Eureka via Config, and Zuul uses the info in Eureka to do the load balancing. So, the way it should be done is you spin up another VM with your Spring Boot instance with the updated cert, and kill off the older VM which evaporates thanks to AWS, and Zuul takes care of the dirty work of being a "reverse web proxy", routing the requests to the new web server as required.
The can of worms you open going this route is that now you have to implement 4 servers and a VPN to support them, your Zuul server becomes the target of external web requests, and you might need to look into the "circuit breaker" pattern on how to handle HTTP request failures - Hystrix is the next thing to look into.
With Digital Ocean, I'm not sure what you might have to do differently, but a JVM restart is unavoidable.
Actually, it depends. Certificates are applied on SSLContext level and SSLContext can be refreshed during runtime. It is completely possible to update the certificate in KeyStore and refresh the SSLContext, moreover, Tomcat has a special helper function reloadSslHostConfigs that helps you to do that.
So what you ask is completely doable:
Spring Cloud triggers certificate update event notification or via polling
Your application loads updated certificate either from Spring-Cloud or from some shared storage
Your application issues reloadSslHostConfigs, so that Tomcat updates its SSLContext
For implementation details of the certificate reloading, you can take a look at the letsencrypt-helper library. It allows generating and keeping-up fresh your LetsEncrypt certificate without JVM restart.

securing spring boot app with mTLS - running on Swisscom App Cloud

I have a spring boot app deployed to Swisscom App Cloud that should to be secured with mTLS.
Obviously there's spring security... Specific to Swisscom App Cloud I read about securing traffic on https://docs.developer.swisscom.com/adminguide/securing-traffic.html.
It is unclear to me how the two play together...
If I enable mTLS via spring security, would that work as is or would I need additional configuration for the Swisscom App Cloud? (I came across HTTP routing which mentions passing client certificates for mTLS https://docs.developer.swisscom.com/concepts/http-routing.html)
Is the configuration of mTLS on Swisscom App Cloud a replacement for what I would otherwise enable with spring security or would I still need to configure something within my application?
Securing traffic mentions deployment manifest and BOSH manifest, is the latter (and maybe additional) configuration needed to enable mTLS on Swisscom App Cloud (i.e. would I need to have access to configs besides the deployment manifest) ?
Update
My use case that I have a REST API that will be consumed by a client outside of Swisscom App Cloud. It was decided that it shall be secured using mTLS.
The admin guide you're referring to is meant for platform operators (i.e. Swisscom), so it's not a resource that can be leveraged by end users.
What is your use case? If it's only a security requirement to check off a list, be aware that the platform itself will be using mTLS internally soon, so the whole path up until the app container is secured. That might be enough for your auditor.
If you really need to validate client certificates by yourself, CF's way of doing so is leveraging X-Forwarded-Client-Cert (https://docs.cloudfoundry.org/concepts/http-routing.html#-forward-client-certificate-to-applications).
However, we've currently not enabled this (there was no need for it up until now), but we can do so.
Update:
According to this explanation, insertion of X-Forwarded-Client-Cert is actually done transparently by the platform. So if you add the client application's certificate to the server application's truststore, it will verify the client certificate.
Update 2:
As you can see in the discussions below, it looks like there is currently conceptually no easy way to allow apps to do proper mTLS using X-Forwarded-Client-Cert. The only option currently is using tcp routes, which is something you can request with your Appcloud support team.

How to config SSL certificate for external servers in Spring Boot Tomcat?

I am creating a Spring Boot integration with Tomcat for Soap WebService application
We are calling other Soap WebServices inside it, which are running in https protocol.
Could you please help me how to config SSL for external servers?
First of all, if these external servers have their own legal trusted certificates, you don't need to do anything.
But if not and you trust these external servers, you can skip certificates verification for those servers as How to fix the “java.security.cert.CertificateException: No subject alternative names present” error?.
Or just skip specific host as SSLHandshakeException: No subject alternative names present.
Wish it could help.

Resources