I have spring boot with keycloak integration on kuberntes cluster. On production I certificate for SSL that is trusted for domain like "example.com". I configured my spring boot web application and keycloak to be under the same domain routing with ingress and providing SSL to reuse my certificate for the both servers.
https://example.com -> Spring boot https://example.com/auth -> Keycloak
This works fine but when I try to obtain a token from my web application through the keycloak spring adapter, I receive the following error in the logs:
message: "failed to turn code into token"
stack_trace: "s.s.p.c.SunCertPathBuilderException: unable to find valid certification path to requested target
at s.s.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at s.s.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
... 95 common frames omitted
Wrapped by: s.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
at sun.security.validator.Validator.validate(Validator.java:262)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:330)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:237)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1621)
... 89 common frames omitted
Wrapped 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
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
at o.apache.http.conn.ssl.SSLSocketFactory.createLayeredSocket(SSLSocketFactory.java:570)
at o.keycloak.adapters.SniSSLSocketFactory.createLayeredSocket(SniSSLSocketFactory.java:114)
at o.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:554)
at o.keycloak.adapters.SniSSLSocketFactory.connectSocket(SniSSLSocketFactory.java:109)
at o.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:415)
at o.a.h.i.c.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
at o.a.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:144)
at o.a.h.i.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:134)
at o.a.h.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:605)
at o.a.h.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:440)
at o.a.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)
at o.a.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at o.a.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at o.a.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
at org.keycloak.adapters.ServerRequest.invokeAccessCodeToToken(ServerRequest.java:111)
at o.k.adapters.OAuthRequestAuthenticator.resolveCode(OAuthRequestAuthenticator.java:335)
at o.k.adapters.OAuthRequestAuthenticator.authenticate(OAuthRequestAuthenticator.java:280)
at o.keycloak.adapters.RequestAuthenticator.authenticate(RequestAuthenticator.java:139)
at o.k.a.s.f.KeycloakAuthenticationProcessingFilter.attemptAuthentication(KeycloakAuthenticationProcessingFilter.java:150)
at o.s.s.w.a.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
at o.s.s.w.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at o.s.s.w.a.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at o.s.s.w.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at o.k.a.s.f.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:86)
at o.s.s.w.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at o.s.s.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
at o.s.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at o.s.s.w.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at o.s.s.w.c.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at o.s.s.w.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at o.s.s.w.c.r.a.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at o.s.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at o.s.s.w.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at o.s.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
at o.s.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
at o.s.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
at o.s.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
at o.a.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at o.a.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at o.s.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at o.s.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at o.a.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at o.a.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at o.s.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
at o.s.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at o.a.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at o.a.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at o.s.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
... 34 frames truncated
"
This error is because the trustore used to validate the TLS connection in your jvm doesn't trust the certificate you use for the keycloak server. Thus the request to turn the code into a token is never completed.
You need to add the CA certificate in your jvm trustore
As you are in a kubernetes environment, a easier way may be to use the facilities given by keycloak adapters (https://www.keycloak.org/docs/latest/securing_apps/index.html#_java_adapter_config):