Search code examples
jenkinsdevopskeycloakjenkins-plugins

infinite loop while using keycloak plugin in jenkins


so I have jenkins installed on a virtual machine and right now, we are connecting to it via jenikns user's database. and I want to pass by keycloak to have a single sign on for all of our applications. I followed the tuto on jenkins website and the videos i found on youtube, unfortunately none of them work.

The problem is, I am using keycloak Authentication plugin on jenkins. I configured the realm and the client on keycloak and I gave the installation json code provided by keycloak to the keycloak JSON on jenkins. when i log out and want to log in again, we enter an infinite loop.

I checked the logs to see where the problem is comming from: NO AUDIENCE IN THE TOKEN

2024-04-24 14:21:09.679+0000 [id=366]   SEVERE  o.j.p.KeycloakSecurityRealm#doFinishLogin: Authentication Exception
org.keycloak.common.VerificationException: No audience in the token
        at org.keycloak.TokenVerifier$AudienceCheck.test(TokenVerifier.java:153)
        at org.keycloak.TokenVerifier.verify(TokenVerifier.java:476)
        at org.keycloak.adapters.rotation.AdapterTokenVerifier.verifyToken(AdapterTokenVerifier.java:54)
        at org.jenkinsci.plugins.KeycloakSecurityRealm.doFinishLogin(KeycloakSecurityRealm.java:262)
        at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:710)
        at org.kohsuke.stapler.Function$MethodFunction.invoke(Function.java:397)
        at org.kohsuke.stapler.Function$InstanceFunction.invoke(Function.java:409)
        at org.kohsuke.stapler.Function.bindAndInvoke(Function.java:207)
        at org.kohsuke.stapler.Function.bindAndInvokeAndServeResponse(Function.java:140)
        at org.kohsuke.stapler.MetaClass$11.doDispatch(MetaClass.java:558)
        at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:59)
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:762)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:894)
        at org.kohsuke.stapler.MetaClass$2.doDispatch(MetaClass.java:224)
        at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:59)
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:762)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:894)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:690)
        at org.kohsuke.stapler.Stapler.service(Stapler.java:240)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
        at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:157)
        at org.jenkinsci.plugins.RefreshFilter.doFilter(RefreshFilter.java:96)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
        at org.jenkinsci.plugins.ssegateway.Endpoint$SSEListenChannelFilter.doFilter(Endpoint.java:248)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
        at jenkins.telemetry.impl.UserLanguages$AcceptLanguageFilter.doFilter(UserLanguages.java:129)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
        at jenkins.security.ResourceDomainFilter.doFilter(ResourceDomainFilter.java:81)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
        at io.jenkins.blueocean.ResourceCacheControl.doFilter(ResourceCacheControl.java:134)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
        at io.jenkins.blueocean.auth.jwt.impl.JwtAuthenticationFilter.doFilter(JwtAuthenticationFilter.java:60)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
        at jenkins.metrics.impl.MetricsFilter.doFilter(MetricsFilter.java:125)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
        at hudson.util.PluginServletFilter.doFilter(PluginServletFilter.java:160)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at hudson.security.csrf.CrumbFilter.doFilter(CrumbFilter.java:160)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:94)
        at jenkins.security.AcegiSecurityExceptionFilter.doFilter(AcegiSecurityExceptionFilter.java:52)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
        at hudson.security.UnwrapSecurityExceptionFilter.doFilter(UnwrapSecurityExceptionFilter.java:54)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
        at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:141)
        at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:97)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:223)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
        at jenkins.security.BasicHeaderProcessor.doFilter(BasicHeaderProcessor.java:97)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:112)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82)
        at hudson.security.HttpSessionContextIntegrationFilter2.doFilter(HttpSessionContextIntegrationFilter2.java:63)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
        at hudson.security.ChainedServletFilter.doFilter(ChainedServletFilter.java:111)
        at hudson.security.HudsonFilter.doFilter(HudsonFilter.java:172)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.kohsuke.stapler.compression.CompressionFilter.doFilter(CompressionFilter.java:53)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at hudson.util.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:86)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.kohsuke.stapler.DiagnosticThreadNameFilter.doFilter(DiagnosticThreadNameFilter.java:30)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at jenkins.security.SuspiciousRequestFilter.doFilter(SuspiciousRequestFilter.java:38)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:549)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1383)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1544)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1305)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
        at org.eclipse.jetty.server.Server.handle(Server.java:563)
        at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505)
        at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
        at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:139)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:933)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1077)
        at java.base/java.lang.Thread.run(Thread.java:829)

and this is the json code i give to jenkins:

{
  "realm": "Prod",
  "auth-server-url": "https://login.****.org/auth/",
  "ssl-required": "none",
  "resource": "Jenkins",
  "verify-token-audience": true,
  "credentials": {
    "secret": "*******"
  },
  "use-resource-role-mappings": true,
  "confidential-port": 0
}

So if anyone have any ideas how to fix this or what is causing it, it would be appreciated.


Solution

  • I am the main maintainer for the keycloak jenkins plugin. A quick workaround is to remove "verify-token-audience": true, from your json and it should work (I tested it locally and was able to workaround it). If you're unable to get in to your jenkins because of the loop you can open up the config.xml file for jenkins, remove that line from the securityRealm element, and then restart the service.

    I'm looking in to why this is happening though and I created a ticket for the bug - https://issues.jenkins.io/browse/JENKINS-73072 . Please follow that for any updates. There's another bug fix that I am hoping to add to the next plugin release so once I have tested a fix for this I will release a new version.

    Thanks!

    EDIT: Sorry, I haven't used audience before in my tokens. A full(er) workaround is to get the audience added to your token from your keycloak. A simple way I was able to do this locally was to:

    1. Go to my client in the keycloak admin UI console
    2. Click on 'client scopes' tab
    3. Click on the dedicated client scopes (my client is called jenkins so the scope I clicked on was 'jenkins-dedicated').
    4. Click "Add Mapper" -> By Configuration
    5. Select the 'Audience' Mapper
    6. Fill out the info, I named mine 'audience' and in the 'Included Client Audience' area I clicked on "jenkins" from the drop-down.
    7. Click 'save', and re-log in to Jenkins. (You may need to clear browser cache or open up a new session).