Stuck
Stuck

Reputation: 12292

@Configuration not applied depending on names of classes

Please read the "Update" at the bottom. It seems to be a problem with the name of the controllers. Depending on the name the SSL-configuration is not applied.


The UserService of my spring application connects to an external server that uses a self-signed cert in development. I added the self-signed and valid cert to a java key store ./dev-truststore.jks and use this @Configuration:

@Profile("development")
@Configuration
class SSLConfigDev {
  @Suppress("unused")
  @PostConstruct
  private fun configureSSL() {
    System.setProperty("javax.net.ssl.trustStore", "./dev-truststore.jks")
    System.setProperty("javax.net.ssl.trustStorePassword", "secret")
  }
}

This is the reduced UserServiceImpl that still shows the error:

import org.keycloak.admin.client.Keycloak
// ...

@Service
class UserServiceFeatureImpl(
  private val userRepository: UserRepository,
  private val kc: Keycloak,
) : UserService {
  override suspend fun createUser(email: String, pw: String) {
    try {
      val realmResources = kc.realm("myRealm")
      val usersResource = realmResources.users()
      
      // The next line works fine until I define the new TaskService as 
      // described below. Then it fails with the SSL exception shown below.
      val existingUsers = usersResource.search(email, 0, 1)

      // ...
    } catch (e: Exception) {
      throw CreateAccountException("Could not create account", e)
    }
  }
}

And this is the route for creating users:

@RestController
class UserController(
  private val userService: UserService,
) {
  @PostMapping("/users/new")
  @ResponseStatus(HttpStatus.CREATED)
  suspend fun create(@RequestBody @Valid dto: CredentialsDTO) {
    userService.createUser(dto.email, dto.password)
  }
}

This is the bean creation for Keycloak:


@Configuration
@ConfigurationProperties("keycloak")
class KeycloakConfig{
  lateinit var serverUrl: String
  lateinit var realm: String
  lateinit var clientId: String
  lateinit var clientSecret: String
  lateinit var frontEndClientId: String

  @Bean
  fun init(): Keycloak {
    return KeycloakBuilder.builder()
      .serverUrl(serverUrl)
      .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
      .realm(realm)
      .clientId(clientId)
      .clientSecret(clientSecret)
      .resteasyClient(
        ResteasyClientBuilder().connectionPoolSize(10).build()
      )
      .build()
  }
}

This works fine.

Now, I was developing a new unrelated feature (controller/service/repository) and suddenly the create-user route stops working, because it cannot connect to the keycloak service anymore because it fails to perform the SSL handshake. Note that the create-user action is not related to anything in the new service/feature and has not been modified.

javax.ws.rs.ProcessingException: RESTEASY004655: Unable to invoke request: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

The full stack trace is:

2021-06-25 18:04:19.050 ERROR 7821 --- [or-http-epoll-3] a.w.r.e.AbstractErrorWebExceptionHandler : [755b0875-1]  500 Server Error for HTTP POST "/users/new"

com.example.backend.user.CreateAccountException: Could not create account
    at com.example.backend.user.UserServiceFeatureImpl.createUser$suspendImpl(UserServiceFeatureImpl.kt:278) ~[main/:na]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ Handler com.example.backend.user.UserController#create(CredentialsDTO, Continuation) [DispatcherHandler]
    |_ checkpoint ⇢ com.example.backend.core.ReactorContextLocaleWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ HTTP POST "/users/new" [ExceptionHandlingWebHandler]
Stack trace:
        at com.example.backend.user.UserServiceFeatureImpl.createUser$suspendImpl(UserServiceFeatureImpl.kt:278) ~[main/:na]
        at com.example.backend.user.UserServiceFeatureImpl.createUser(UserServiceFeatureImpl.kt) ~[main/:na]
        at com.example.backend.user.UserServiceFeatureImpl$$FastClassBySpringCGLIB$$ed9e17ac.invoke(<generated>) ~[main/:na]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.7.jar:5.3.7]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.3.7.jar:5.3.7]
        at com.example.backend.user.UserServiceFeatureImpl$$EnhancerBySpringCGLIB$$2ccf6d25.createUser(<generated>) ~[main/:na]
        at com.example.backend.user.UserServicePermissionsImpl.createUser$suspendImpl(UserServicePermissionsImpl.kt:47) ~[main/:na]
        at com.example.backend.user.UserServicePermissionsImpl.createUser(UserServicePermissionsImpl.kt) ~[main/:na]
        at com.example.backend.user.UserServicePermissionsImpl$$FastClassBySpringCGLIB$$d7f96f3a.invoke(<generated>) ~[main/:na]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.7.jar:5.3.7]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.3.7.jar:5.3.7]
        at com.example.backend.user.UserServicePermissionsImpl$$EnhancerBySpringCGLIB$$fb5d3241.createUser(<generated>) ~[main/:na]
        at com.example.backend.user.UserController.create$suspendImpl(UserController.kt:116) ~[main/:na]
        at com.example.backend.user.UserController.create(UserController.kt) ~[main/:na]
        at com.example.backend.user.UserController$$FastClassBySpringCGLIB$$d1552207.invoke(<generated>) ~[main/:na]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.7.jar:5.3.7]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.3.7.jar:5.3.7]
        at com.example.backend.user.UserController$$EnhancerBySpringCGLIB$$f0dbf2c8.create(<generated>) ~[main/:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97) ~[kotlin-reflect-1.5.0.jar:1.5.0-release-749 (1.5.0)]
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Instance.call(CallerImpl.kt:113) ~[kotlin-reflect-1.5.0.jar:1.5.0-release-749 (1.5.0)]
        at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108) ~[kotlin-reflect-1.5.0.jar:1.5.0-release-749 (1.5.0)]
        at kotlin.reflect.full.KCallables.callSuspend(KCallables.kt:55) ~[kotlin-reflect-1.5.0.jar:1.5.0-release-749 (1.5.0)]
        at org.springframework.core.CoroutinesUtils$invokeSuspendingFunction$mono$1.invokeSuspend(CoroutinesUtils.kt:64) ~[spring-core-5.3.7.jar:5.3.7]
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.5.0.jar:1.5.0-release-749 (1.5.0)]
        at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:377) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
        at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
        at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
        at kotlinx.coroutines.reactor.MonoKt.monoInternal$lambda-2(Mono.kt:90) ~[kotlinx-coroutines-reactor-1.5.0.jar:na]
        at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:57) ~[reactor-core-3.4.6.jar:3.4.6]
        // ...
        at reactor.netty.channel.FluxReceive.terminateReceiver(FluxReceive.java:469) ~[reactor-netty-core-1.0.7.jar:1.0.7]
        at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:261) ~[reactor-netty-core-1.0.7.jar:1.0.7]
        at reactor.netty.channel.FluxReceive.request(FluxReceive.java:130) ~[reactor-netty-core-1.0.7.jar:1.0.7]
        at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:162) ~[reactor-core-3.4.6.jar:3.4.6]
        at reactor.core.publisher.FluxPeek$PeekSubscriber.request(FluxPeek.java:137) ~[reactor-core-3.4.6.jar:3.4.6]
        at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:162) ~[reactor-core-3.4.6.jar:3.4.6]
        at reactor.core.publisher.MonoCollect$CollectSubscriber.onSubscribe(MonoCollect.java:103) ~[reactor-core-3.4.6.jar:3.4.6]
        at reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:92) ~[reactor-core-3.4.6.jar:3.4.6]
        at reactor.core.publisher.FluxPeek$PeekSubscriber.onSubscribe(FluxPeek.java:170) ~[reactor-core-3.4.6.jar:3.4.6]
        at reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:92) ~[reactor-core-3.4.6.jar:3.4.6]
        at reactor.netty.channel.FluxReceive.startReceiver(FluxReceive.java:168) ~[reactor-netty-core-1.0.7.jar:1.0.7]
        at reactor.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:147) ~[reactor-netty-core-1.0.7.jar:1.0.7]
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:384) ~[netty-transport-native-epoll-4.1.65.Final-linux-x86_64.jar:4.1.65.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
        at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
Caused by: javax.ws.rs.ProcessingException: RESTEASY004655: Unable to invoke request: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:328) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:443) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:149) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:112) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at com.sun.proxy.$Proxy131.grantToken(Unknown Source) ~[na:na]
    at org.keycloak.admin.client.token.TokenManager.grantToken(TokenManager.java:90) ~[keycloak-admin-client-13.0.1.jar:13.0.1]
    at org.keycloak.admin.client.token.TokenManager.getAccessToken(TokenManager.java:70) ~[keycloak-admin-client-13.0.1.jar:13.0.1]
    at org.keycloak.admin.client.token.TokenManager.getAccessTokenString(TokenManager.java:65) ~[keycloak-admin-client-13.0.1.jar:13.0.1]
    at org.keycloak.admin.client.resource.BearerAuthFilter.filter(BearerAuthFilter.java:52) ~[keycloak-admin-client-13.0.1.jar:13.0.1]
    at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.filterRequest(ClientInvocation.java:579) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:440) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:149) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:112) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    at com.sun.proxy.$Proxy173.search(Unknown Source) ~[na:na]
    at com.example.backend.user.UserServiceFeatureImpl.createUser$suspendImpl(UserServiceFeatureImpl.kt:247) ~[main/:na]
    at com.example.backend.user.UserServiceFeatureImpl.createUser(UserServiceFeatureImpl.kt) ~[main/:na]
    at com.example.backend.user.UserServiceFeatureImpl$$FastClassBySpringCGLIB$$ed9e17ac.invoke(<generated>) ~[main/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.7.jar:5.3.7]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.3.7.jar:5.3.7]
    at com.example.backend.user.UserServiceFeatureImpl$$EnhancerBySpringCGLIB$$2ccf6d25.createUser(<generated>) ~[main/:na]
    at com.example.backend.user.UserServicePermissionsImpl.createUser$suspendImpl(UserServicePermissionsImpl.kt:47) ~[main/:na]
    at com.example.backend.user.UserServicePermissionsImpl.createUser(UserServicePermissionsImpl.kt) ~[main/:na]
    at com.example.backend.user.UserServicePermissionsImpl$$FastClassBySpringCGLIB$$d7f96f3a.invoke(<generated>) ~[main/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.7.jar:5.3.7]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.3.7.jar:5.3.7]
    at com.example.backend.user.UserServicePermissionsImpl$$EnhancerBySpringCGLIB$$fb5d3241.createUser(<generated>) ~[main/:na]
    at com.example.backend.user.UserController.create$suspendImpl(UserController.kt:116) ~[main/:na]
    at com.example.backend.user.UserController.create(UserController.kt) ~[main/:na]
    at com.example.backend.user.UserController$$FastClassBySpringCGLIB$$d1552207.invoke(<generated>) ~[main/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.7.jar:5.3.7]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.3.7.jar:5.3.7]
    at com.example.backend.user.UserController$$EnhancerBySpringCGLIB$$f0dbf2c8.create(<generated>) ~[main/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
    at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97) ~[kotlin-reflect-1.5.0.jar:1.5.0-release-749 (1.5.0)]
    at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Instance.call(CallerImpl.kt:113) ~[kotlin-reflect-1.5.0.jar:1.5.0-release-749 (1.5.0)]
    at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108) ~[kotlin-reflect-1.5.0.jar:1.5.0-release-749 (1.5.0)]
    at kotlin.reflect.full.KCallables.callSuspend(KCallables.kt:55) ~[kotlin-reflect-1.5.0.jar:1.5.0-release-749 (1.5.0)]
    at org.springframework.core.CoroutinesUtils$invokeSuspendingFunction$mono$1.invokeSuspend(CoroutinesUtils.kt:64) ~[spring-core-5.3.7.jar:5.3.7]
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.5.0.jar:1.5.0-release-749 (1.5.0)]
    at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:377) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
    at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
    at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) ~[kotlinx-coroutines-core-jvm-1.5.0.jar:na]
    at kotlinx.coroutines.reactor.MonoKt.monoInternal$lambda-2(Mono.kt:90) ~[kotlinx-coroutines-reactor-1.5.0.jar:na]
    // ...
    at reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:92) ~[reactor-core-3.4.6.jar:3.4.6]
    at reactor.netty.channel.FluxReceive.startReceiver(FluxReceive.java:168) ~[reactor-netty-core-1.0.7.jar:1.0.7]
    at reactor.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:147) ~[reactor-netty-core-1.0.7.jar:1.0.7]
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
    at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:384) ~[netty-transport-native-epoll-4.1.65.Final-linux-x86_64.jar:4.1.65.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.65.Final.jar:4.1.65.Final]
    at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) ~[na:na]
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:325) ~[na:na]
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:268) ~[na:na]
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:263) ~[na:na]
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1340) ~[na:na]
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1215) ~[na:na]
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1158) ~[na:na]
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396) ~[na:na]
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:445) ~[na:na]
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:423) ~[na:na]
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:182) ~[na:na]
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:171) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1475) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1381) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:441) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:412) ~[na:na]
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:436) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:384) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) ~[httpclient-4.5.13.jar:4.5.13]
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56) ~[httpclient-4.5.13.jar:4.5.13]
    at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:323) ~[resteasy-client-3.13.2.Final.jar:3.13.2.Final]
    ... 98 common frames omitted
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:439) ~[na:na]
    at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306) ~[na:na]
    at java.base/sun.security.validator.Validator.validate(Validator.java:264) ~[na:na]
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231) ~[na:na]
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132) ~[na:na]
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1324) ~[na:na]
    ... 122 common frames omitted
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141) ~[na:na]
    at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126) ~[na:na]
    at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297) ~[na:na]
    at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:434) ~[na:na]
    ... 127 common frames omitted

2021-06-25 18:04:19.072  INFO 7821 --- [or-http-epoll-3] reactor.netty.http.server.AccessLog      : 127.0.0.1:59138 - - [25/Jun/2021:18:04:18 +0200] "POST /users/new HTTP/1.1" 500 23140 306 ms

I tracked the issue down by removing all methods from the new service (so it is an empty interface + the implementation in a @Service annotated class):

interface TaskService {}

and

@Service
class TaskServiceImpl(
  // next line implies the error.
  private val userService: UserService,
) : TaskService {}

When I remove the private val userService: UserService dependency from the TaskServiceImpl the userService is able to connect to keycloak and the create-user route does not fail, but if I add it again, the said SSL handshake error occurs on the create-user route.

Other services, that also depend on the UserService as well, do not imply the same error. The new service is completely empty, so I am very confused why the other services do not imply the same error?

For example, this service also uses the UserService and it does not imply the user creation route to fail:

@Service
class FeedServiceImpl(
  private val userService: UserService,
  // other dependencies
) : FeedService {
   /// ...
}

The TaskService is used in two controllers, I have removed all routes and all other dependencies:

@RestController
class TaskReviewController(
  // adding or removing this dependency does not change anything
  private val taskService: TaskService,
) {
}
@RestController
class ActionsController(
  // adding or removing this dependency implies the error
  private val taskService: TaskService,
) {
}

When I remove the taskService dependency from both, the error does not occur. When I only add it to the ActionsController the error does occur - but if I only add it to the TaskReviewController the error does not occur.

I am lost how I can track it further down. I thought that this might indicate a dependency cycle but there isn't a cycle, as UserService does not depend on anything of the new TaskService and has not been modified when adding the new TaskService. And the UserService also has no dependency on any of the controllers.

Any ideas?

--

PS: I also verified that configureSSL is still executed and I am running the right development profile.

UPDATE

I found out that the error depends on the package and name of the controller. If all controllers that use the TaskService are alphabetically ordered after the SSLConfigDev configuration than the error does not occur:

So this controller in the same package as the 'SSLConfigDev` implies the error:

@RestController
class SSLConfigDeuController(
  private val taskService: TaskReviewService,
) {
}

but this is fine:

@RestController
class SSLConfigDewController(
  private val taskService: TaskReviewService,
) {
}

Upvotes: 1

Views: 666

Answers (1)

Stuck
Stuck

Reputation: 12292

After further investigation I think I understand the problem.

Depending on the name of the controller the user service is instantiated before or after the SSLConfigDev configuration.

The UserService depends on Keycloak. The Keycloak class creates a resteasy client in the constructor:

The relevant code of the library is:

Keycloak(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, String grantType, Client resteasyClient, String authtoken) {
        config = new Config(serverUrl, realm, username, password, clientId, clientSecret, grantType);
        client = resteasyClient != null ? resteasyClient : newRestEasyClient(null, null, false);
        authToken = authtoken;
        tokenManager = authtoken == null ? new TokenManager(config, client) : null;

        target = (ResteasyWebTarget) client.target(config.getServerUrl());
        target.register(newAuthFilter());
    }

This code is run when the UserService is instantiated because it triggers creating the Keycloak bean.

Depending on whether this happens before or after the SSLConfigDev is run the system properties are already set or not.

As often with the hard-to-spot bugs, solving the problem is easy: We just need to ensure that the Keycloak bean is created after the system properties have been set. And because the correct configuration should not depend on the order of bean creation, the implementation should be bound to the specific bean.

For example, we can remove the SSLConfigDev and instead create a dev-profile version of the Keycloak.init() method that sets the system properties just before creating the bean instance. Or we can just check the environment when creating the bean as follows:

@Configuration
@ConfigurationProperties("keycloak")
class KeycloakConfig(
  private val env: Environment,
){
  lateinit var serverUrl: String
  lateinit var realm: String
  lateinit var clientId: String
  lateinit var clientSecret: String
  lateinit var frontEndClientId: String

  @Bean
  fun init(): Keycloak {
    if (env.activeProfiles.contains("development")) {
      System.setProperty("javax.net.ssl.trustStore", "./dev-truststore.jks")
      System.setProperty("javax.net.ssl.trustStorePassword", "keycloak")
    }
    return KeycloakBuilder.builder()
      .serverUrl(serverUrl)
      .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
      .realm(realm)
      .clientId(clientId)
      .clientSecret(clientSecret)
      .resteasyClient(
        ResteasyClientBuilder().connectionPoolSize(10).build()
      )
      .build()
  }
}

Upvotes: 1

Related Questions