Spring Boot and Kubernetes Mutual SSL

5/30/2020

I have a spring boot application that requires an X509 certificate for authentication.

This is already configured and working properly in the local dev environment when running from eclipse using docker desktop and fabric8 maven docker plugin to build the image.

Now I would like to deploy this to our k8s test cluster where I already succeeded to deploy a simple spring boot application with 8080 as a port. and I can reach it in the browser with https.

https://testcluster.exmaple.com/namespace/myapp-test/

As you see in the url the kuberntes cluster is already forwarding HTTP ports such as 8080 to a secure connection, Because I belive Kubernetes with ambassador will leverage that for you; you just need to provide a targetPort in your service.yaml (we are using helm):

kind: Service
apiVersion: v1
metadata:
  name: {{ include "component.web.fullname" . }}
  {{- include "helper.labels" . | nindent 2 }}
spec:
  selector:
    app: "{{ .Values.application.name }}"
    component: "{{ .Values.component.web.name }}"
    instance: "{{ .Values.instance }}"
  type: ClusterIP
  ports:
  - name: https
    port: 80
    targetPort: 8080

The question is; if take my current local working config which is for spring boot :

#development only.
server.port=8443
server.ssl.key-store=/dev/server.jks
server.ssl.key-store-password=123123
server.ssl.key-alias=localhost
server.ssl.key-password=123123
server.ssl.enabled=true
server.ssl.trust-store=/dev/clients.jks
server.ssl.trust-store-password=123123
server.ssl.client-auth=need

With the following spring security config :

@Override
protected void configure(HttpSecurity http) throws Exception {

        http.requiresChannel()
                .anyRequest()
                .requiresSecure()
                .and().csrf().disable()
                // .exceptionHandling()
                // .accessDeniedPage("/403")
                // .and()
                .x509()
                .userDetailsService(userDetailsService)
                .and()
                .authorizeRequests()
                .antMatchers("/**").access("hasAuthority('SYSADMIN')");

    }

But before deploying that,I tried with simple spring boot app with a hello world controller and no spring security config, just the server certificate only with a config that looks like this :

server.port=8443
server.ssl.key-store=/dev/server.jks
server.ssl.key-store-password=123123
server.ssl.key-alias=localhost
server.ssl.key-password=123123
server.ssl.enabled=true

And deployed to Kubernetes and updated the deployment.yaml and service.yaml to use port 8443 of the application and I got this :

Bad Request
This combination of host and port requires TLS.

So, If I can deploy a spring boot app that doesn't require client cert without any SSL config and I still can reach the application with SSL connection, how should I do for an optimal configuration to run mutual SSL applications, because as far as I know, I cannot configure mutual SSL in spring boot with only the trustore config only (be aware of the commented lines):

  server.port=8443
  #server.ssl.key-store=/dev/server.jks
  #server.ssl.key-store-password=123123
  #server.ssl.key-alias=localhost
  #server.ssl.key-password=123123
  server.ssl.enabled=true
  server.ssl.trust-store=/dev/clients.jks
  server.ssl.trust-store-password=123123
  server.ssl.client-auth=need

this will result in spring boot complaining with the stack below :

org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Could not load key store 'null'
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:156) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:544) ~[spring-context-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at com.sample.Application.main(Application.java:19) [classes/:na]
Caused by: org.springframework.boot.web.server.WebServerException: Could not load key store 'null'
    at org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer.configureSslKeyStore(SslConnectorCustomizer.java:128) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer.configureSsl(SslConnectorCustomizer.java:88) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer.customize(SslConnectorCustomizer.java:57) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.customizeSsl(TomcatServletWebServerFactory.java:339) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.customizeConnector(TomcatServletWebServerFactory.java:317) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:183) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:180) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:153) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    ... 8 common frames omitted
Caused by: java.lang.IllegalArgumentException: Resource location must not be null
    at org.springframework.util.Assert.notNull(Assert.java:198) ~[spring-core-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.util.ResourceUtils.getURL(ResourceUtils.java:130) ~[spring-core-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer.configureSslKeyStore(SslConnectorCustomizer.java:125) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    ... 15 common frames omitted
-- Jalal Sordo
docker
java
kubernetes
spring-boot
x509certificate

0 Answers