ops4j / org.ops4j.pax.web

OSGi R7 Http Service, Whiteboard and Web Applications (OSGi CMPN Release chapters 102, 140 and 128) implementation using Jetty 9, Tomcat 9 or Undertow 2.
https://ops4j1.jira.com/wiki/display/paxweb/Pax+Web
Other
146 stars 184 forks source link

[8.0.x] Pax-web (pax-web-jetty-keycloak) 8.0.20 does not work with Keycloak #1893

Closed GuliyevFarid closed 1 year ago

GuliyevFarid commented 1 year ago

Hello,

I use keycloak authentication with apache karaf almost 4 years. Previously I was using keycloak with undertow, but currently we upgraded the karaf version and now I see that there is a pax-web-jetty-keycloak feature with looks like support keycloak integration with jetty 9.4. I have installed the pax-web-jetty-keycloak and also created a keycloak.json file. Additionally I have created a org.ops4j.pax.web.context-services.cfg file with the following content:

bundle.symbolicName = org.apache.cxf.cxf-rt-transports-http
context.id = default
login.config.authMethod = KEYCLOAK

security.services.common.url = /services/*
security.services.common.roles = admin, user

NOTE: previously I also used the following parameter which I removed this time, as this property looks that is not available any more.

context.param.keycloak.config.resolver = org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver

But, unfortunately I receive the following error during the service call:

ERROR: keycloak deployment isn't configured return false

is is an issue there? or do I miss some configurations?

BR, Farid

grgrzybek commented 1 year ago

@GuliyevFarid please see some background information here: #1716

grgrzybek commented 1 year ago

For a WAR example, please see https://github.com/ops4j/org.ops4j.pax.web/tree/web-8.0.20/samples/samples-war/war-keycloak - it is a WAR with /WEB-INF/keycloak.json embedded and related realm configuration is available in src/main/config/PaxWeb-realm-export.json.

grgrzybek commented 1 year ago

And before I see your setup (or find some time this week to check it myself on pure Karaf 4.4.x), here's a documented example using Red Hat Fuse 7.12 (which is based on Karaf 4.4.x): https://github.com/jboss-fuse/karaf-quickstarts/tree/7.x.redhat-7-x/security/keycloak/keycloak-cxf org.ops4j.pax.web.context-cxf.cfg contains the factory PID with necessary configuration.

Please check it and if this still doesn't work, let me know - I'll try to help here.

GuliyevFarid commented 1 year ago

Thanks a lot for your reply.

Just would like to mention that, I use keycloak with undertow container almost 4 years and it works very well. I have configured the security with context URLs and ROLEs using whiteboard. As karaf version is upgraded, so I had to upgrade my servers as well. Now I use karaf 4.4.3 and pax-web 8.0.20.

This time, I tried a lot, to integrate keycloak adapter with jetty container. But it did not work at all.

Next I tried to install undertow container using cxf-http-undertow. After disabling the SSL in pax-web configuration, finally I was able to run undertow container and my services.

Now, I try again install keycloak adapter and configure my security via context. For this, I tried to install keycloak-osgi-adapter (to use HierarchicalPathBasedKeycloakConfigResolver), but unfortunately that bundle requires http-whiteboard till version 8. But in karaf 4.4.3, the version is 8.0.20. So, I cannot use that configresolver for the keycloak.

After that, I tried to install the pax-web-undertow-keycloak18. The installation of the bundle was successful. But as soon as I add pax-web configuration for my contexts, the services return HTTP 404, even /services context.

I checked the logs and saw that karaf stops undertow context "/" and does not start that again, so, I receive 404. ERROR in karaf: Stopping Undertow context "/"

Now, the first problem is that, I want to use hierarchical path based config resolver, but due to version cannot use that. How can I solve that issue? The second problem is that as soon as there is a pax-web configuration, undertow container stops. how can I fix that issue? The third, if there is still a possibility to use keycloak with jetty container, using whiteboard context security configuration?

NOTE: I have read all the above links that you mentioned. As I mentioned, my purpose is to set up a general security layer via whiteboard, so, all deployed services under predefined contexts will require authentication and authorization.

grgrzybek commented 1 year ago

For this, I tried to install keycloak-osgi-adapter (to use HierarchicalPathBasedKeycloakConfigResolver), but unfortunately that bundle requires http-whiteboard till version 8.

keycloak-osgi-adapter version 18 has this import:

Import-Package: org.ops4j.pax.web.service;version="[3.0,8)"

This is special commit for Keycloak 18 where I export relevant package at the version expected by Keycloak (the interface used by Keycloak didn't change): 596577b106a811cbcb2864e9ce4d7475c62eeb45 and it's available since pax-web 8.0.16:

$ git tag --contains 596577b106a811cbcb2864e9ce4d7475c62eeb45
web-8.0.16
web-8.0.17
web-8.0.18
web-8.0.19
web-8.0.20
web-8.0.21
web-8.0.22

as soon as I add pax-web configuration for my contexts, the services return HTTP 404, even /services context.

I'd have to see more logs to check what has happened. With https://github.com/jboss-fuse/karaf-quickstarts/tree/7.x.redhat-7-x/security/keycloak/keycloak-cxf I checked Keycloak 18 and Pax Web 8 and it worked, so I'll be happy to help here.

GuliyevFarid commented 1 year ago

The commit you have mentioned is related to Jetty+Keycloak18 integration.

When I try to do that integration, I do the following steps:

I check the current installed jetty bundles:

image

Next I start to install the keycloak features for version 18.

feature:repo-add mvn:org.keycloak/keycloak-osgi-features/18.0.2/xml/features

image

Next I install pax-web-jetty-keycloak18

feature:install pax-web-jetty-keycloak18

And it is successfully installed.

Next I add new file into etc folder: org.ops4j.pax.web.context-services.cfg with the following content:

bundle.symbolicName = org.apache.cxf.cxf-rt-transports-http
context.id = default

context.param.keycloak.config.resolver = org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver
login.config.authMethod = KEYCLOAK

security.services.common.url = /services/*
security.services.common.roles = admin, user

In the log I see the following issue with Keycloak:

2023-09-19T17:47:01,640 | INFO  | paxweb-context-7-thread-1 | extProcessing$HttpContextTracker  408 | 318 - org.ops4j.pax.web.pax-web-runtime - 8.0.20 | Registering login configuration in OsgiContextModel{HS,id=OCM-58,name='default',path='/',bundle=org.apache.cxf.cxf-rt-transports-http,context=DefaultHttpContext{bundle=org.apache.cxf.cxf-rt-transports-http_3.5.6 [182],contextId='default'}}: method=KEYCLOAK, realm=default
2023-09-19T17:47:01,641 | INFO  | paxweb-context-7-thread-1 | extProcessing$HttpContextTracker  427 | 318 - org.ops4j.pax.web.pax-web-runtime - 8.0.20 | Registering security mappings in OsgiContextModel{HS,id=OCM-58,name='default',path='/',bundle=org.apache.cxf.cxf-rt-transports-http,context=DefaultHttpContext{bundle=org.apache.cxf.cxf-rt-transports-http_3.5.6 [182],contextId='default'}}
2023-09-19T17:47:01,643 | INFO  | paxweb-config-1-thread-1 | y.internal.JettyServerController  162 | 317 - org.ops4j.pax.web.pax-web-jetty - 8.0.20 | Receiving Batch{"Processing context "default" for bundle org.apache.cxf.cxf-rt-transports-http", size=4}
2023-09-19T17:47:01,643 | INFO  | paxweb-config-1-thread-1 | etty.internal.JettyServerWrapper 2078 | 317 - org.ops4j.pax.web.pax-web-jetty - 8.0.20 | Stopping Jetty context "/"
2023-09-19T17:47:01,644 | INFO  | paxweb-config-1-thread-1 | etty.internal.JettyServerWrapper 2091 | 317 - org.ops4j.pax.web.pax-web-jetty - 8.0.20 | Adding init parameters to OsgiContextModel{HS,id=OCM-58,name='default',path='/',bundle=org.apache.cxf.cxf-rt-transports-http,context=DefaultHttpContext{bundle=org.apache.cxf.cxf-rt-transports-http_3.5.6 [182],contextId='default'}}: {keycloak.config.resolver=org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver}
2023-09-19T17:47:01,644 | INFO  | paxweb-config-1-thread-1 | etty.internal.JettyServerWrapper 2109 | 317 - org.ops4j.pax.web.pax-web-jetty - 8.0.20 | Adding security configuration to OsgiContextModel{HS,id=OCM-58,name='default',path='/',bundle=org.apache.cxf.cxf-rt-transports-http,context=DefaultHttpContext{bundle=org.apache.cxf.cxf-rt-transports-http_3.5.6 [182],contextId='default'}}
2023-09-19T17:47:01,645 | INFO  | paxweb-config-1-thread-1 | etty.internal.JettyServerWrapper 2173 | 317 - org.ops4j.pax.web.pax-web-jetty - 8.0.20 | Starting Jetty context "/" with default Osgi Context OsgiContextModel{HS,id=OCM-51,name='default',path='/',bundle=org.apache.karaf.webconsole.console,context=DefaultHttpContext{bundle=org.apache.karaf.webconsole.console_4.4.3.20230726 [241],contextId='default'}}
2023-09-19T17:47:01,655 | INFO  | paxweb-config-1-thread-1 | stractKeycloakJettyAuthenticator  240 | 580 - org.keycloak.keycloak-jetty-core - 18.0.2 | The specified resolver org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver
2023-09-19T17:47:01,662 | INFO  | paxweb-config-1-thread-1 | nternal.web.JettyResourceServlet   75 | 317 - org.ops4j.pax.web.pax-web-jetty - 8.0.20 | Initialized Jetty Resource Servlet for base="res" with cache maxSize=4096kB, maxEntrySize=2048kB, maxEntries=2048
2023-09-19T17:47:16,646 | INFO  | fileinstall-/opt/TalendRuntime-8.0.1-R2023-08-RT/runtime/etc | install.internal.Util$OsgiLogger  205 | 18 - org.apache.felix.fileinstall - 3.7.4 | Removing  READ_ONLY attribute from configuration {org.ops4j.pax.web.context~services} from /opt/TalendRuntime-8.0.1-R2023-08-RT/runtime/etc/org.ops4j.pax.web.context-services.cfg

ERROR: The specified resolver org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver

this check is within pax-web-jetty-keycloak

    if (this.configResolver == null && theServletContext != null) {
      String configResolverClass = theServletContext.getInitParameter("keycloak.config.resolver");
      if (configResolverClass != null)
        try {
          this.configResolver = (KeycloakConfigResolver)ContextHandler.getCurrentContext().getClassLoader().loadClass(configResolverClass).newInstance();
          log.infov("Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
        } catch (Exception ex) {
          log.infov("The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[] { configResolverClass, ex.getMessage() });
        }  
    } 

As it cannot find required resolver, (which is part of keycloak-osgi-adapter) so I try to install that specific bundle.

feature:install keycloak-osgi-adapter

And it fails.

image

As my current http-whiteboard is version 8.0.20.

image

As you said that it works for you the Jetty+Keycloak integration, so, do I miss something there?

GuliyevFarid commented 1 year ago

When I try to do the Undertow+Keycloak integration, based on the link that you provided: https://github.com/jboss-fuse/karaf-quickstarts/tree/7.x.redhat-7-x/security/keycloak/keycloak-cxf

I do the following steps:

I install required undertow features

image

Next I install keycloak as mentioned in the documentation that you sent.

 feature:repo-add mvn:org.keycloak/keycloak-osgi-features/18.0.2/xml/features
 feature:install -v keycloak-pax-http-undertow

In this case, installation of keycloak-pax-http-undertow stuck and does not continue. In the logs I found the following conflict

2023-09-19T18:12:21,635 | DEBUG | features-2-thread-1 | ternal.resolver.Slf4jResolverLog   45 | 19 - org.apache.karaf.features.core - 4.4.3 | Candidate permutation failed due to a conflict between imports; will try another if possible. (Uses constraint violation. Unable to resolve resource org.ops4j.pax.web.pax-web-undertow [org.ops4j.pax.web.pax-web-undertow/8.0.20] because it is exposed to package 'io.undertow.connector' from resources io.undertow.core [io.undertow.core/2.2.24.Final] and io.undertow.core [io.undertow.core/1.4.28.Final] via two dependency chains.

Chain 1:
  org.ops4j.pax.web.pax-web-undertow [org.ops4j.pax.web.pax-web-undertow/8.0.20]
    import: (&(osgi.wiring.package=io.undertow.connector)(version>=2.2.0)(!(version>=3.0.0)))
     |
    export: osgi.wiring.package: io.undertow.connector
  io.undertow.core [io.undertow.core/2.2.24.Final]

Chain 2:
  org.ops4j.pax.web.pax-web-undertow [org.ops4j.pax.web.pax-web-undertow/8.0.20]
    import: (&(osgi.wiring.package=org.keycloak.adapters.undertow)(version>=18.0.2))
     |
    export: osgi.wiring.package=org.keycloak.adapters.undertow; uses:=io.undertow.security.api
  org.keycloak.keycloak-undertow-adapter [org.keycloak.keycloak-undertow-adapter/18.0.2]
    import: (&(osgi.wiring.package=io.undertow.security.api)(version>=1.4.0)(!(version>=3.0.0)))
     |
    export: osgi.wiring.package: io.undertow.security.api; uses:=io.undertow.connector
    export: osgi.wiring.package=io.undertow.connector
  io.undertow.core [io.undertow.core/1.4.28.Final])

I do not know why there is a conflict. could you tell me which part I do incorrect, please?

grgrzybek commented 1 year ago

Thanks for the details - I already see you have ancient Undertow 1.4.28 bundle installed, but I'll check more (and maybe update the quickstarts) tomorrow, or later this week...

GuliyevFarid commented 1 year ago

Thanks for the details - I already see you have ancient Undertow 1.4.28 bundle installed, but I'll check more (and maybe update the quickstarts) tomorrow, or later this week...

any news on that topic?

grgrzybek commented 1 year ago

I'm sorry @GuliyevFarid but I have a lot of work recently. I'd have to setup full example and Keycloak server to check it on Jetty/Tomcat/Undertow. I will do it, but not immediately.

In the meantime - can you check where did you get Undertow 1.4.28 from?

grgrzybek commented 1 year ago

Checking with Karaf 4.4.4, Pax Web 8.0.22 and CXF 3.5.5

karaf@root()> repo-add mvn:org.apache.karaf.examples/karaf-rest-example-features/4.4.4/xml                                                                                                                                                     
Adding feature url mvn:org.apache.karaf.examples/karaf-rest-example-features/4.4.4/xml

karaf@root()> feature:install karaf-rest-example-blueprint 

karaf@root()> la -l | grep pax
  4 │ Active   │   5 │ 2.6.14             │ mvn:org.ops4j.pax.url/pax-url-aether/2.6.14
  5 │ Active   │   8 │ 2.2.3              │ mvn:org.ops4j.pax.logging/pax-logging-log4j2/2.2.3
  7 │ Active   │   8 │ 2.2.3              │ mvn:org.ops4j.pax.logging/pax-logging-api/2.2.3
 51 │ Active   │  10 │ 2.6.14             │ mvn:org.ops4j.pax.url/pax-url-wrap/2.6.14/jar/uber
113 │ Active   │  30 │ 8.0.22             │ mvn:org.ops4j.pax.web/pax-web-api/8.0.22
114 │ Resolved │  30 │ 8.0.22             │ mvn:org.ops4j.pax.web/pax-web-compatibility-servlet31/8.0.22
115 │ Active   │  30 │ 8.0.22             │ mvn:org.ops4j.pax.web/pax-web-jetty/8.0.22
116 │ Active   │  30 │ 8.0.22             │ mvn:org.ops4j.pax.web/pax-web-runtime/8.0.22
117 │ Active   │  30 │ 8.0.22             │ mvn:org.ops4j.pax.web/pax-web-spi/8.0.22
118 │ Active   │  30 │ 8.0.22             │ mvn:org.ops4j.pax.web/pax-web-tomcat-common/8.0.22

karaf@root()> feature:install pax-web-karaf 

karaf@root()> web:servlet-list 
Bundle ID │ Name                       │ Class                                                │ Context Path(s) │ URLs   │ Type
──────────┼────────────────────────────┼──────────────────────────────────────────────────────┼─────────────────┼────────┼────────────
78        │ cxf-osgi-transport-servlet │ org.apache.cxf.transport.servlet.CXFNonSpringServlet │ /               │ /cxf/* │ HttpService

karaf@root()> web:context-list 
Bundle ID │ Symbolic Name                         │ Context Path │ Context Name │ Rank │ Service ID │ Type        │ Scope   │ Registration Properties
──────────┼───────────────────────────────────────┼──────────────┼──────────────┼──────┼────────────┼─────────────┼─────────┼─────────────────────────────────────────────────
78        │ org.apache.cxf.cxf-rt-transports-http │ /            │ default      │ MAX  │ 0          │ HttpService │ static* │ httpContext.id=default
          │                                       │              │              │      │            │             │         │ httpContext.path=/
          │                                       │              │              │      │            │             │         │ osgi.http.whiteboard.context.httpservice=default
          │                                       │              │              │      │            │             │         │ osgi.http.whiteboard.context.path=/

*) This context is using ServletContextHelper/HttpContext without resolving an org.osgi.framework.ServiceReference.

As you can see, after installing karaf-rest-example-blueprint feature, I see pax-web-http-jetty feature installed (with its relevant bundles).

Does it work?

$ curl -XPOST -H'Content-Type: application/json' --data '{"id":"42","customer":"pax-web","flight":"0000-0001"}' -i http://localhost:8181/cxf/booking
HTTP/1.1 204 No Content
Date: Tue, 26 Sep 2023 14:13:42 GMT

$ curl -i http://localhost:8181/cxf/booking/42
HTTP/1.1 200 OK
Date: Tue, 26 Sep 2023 14:13:48 GMT
Content-Type: application/json
Transfer-Encoding: chunked

{"id":42,"customer":"pax-web","flight":"0000-0001"}[ggrzybek@everfree] /data/servers/apache-karaf-4.4.4 

That's the preparation. See more comments soon.

grgrzybek commented 1 year ago

@GuliyevFarid btw, have you seen https://github.com/ops4j/org.ops4j.pax.web/blob/pax-web-8.0.x/pax-web-keycloak/readme.adoc ?

grgrzybek commented 1 year ago

I started fresh Keycloak 22.0.3 server:

$ bin/kc.sh start-dev --http-port 8180

And imported https://github.com/ops4j/org.ops4j.pax.web/blob/pax-web-8.0.x/pax-web-keycloak/realm-export.json as paxweb realm.

I added cxf client with "Direct Access Grants" only.

Installed pax-web-jetty-keycloak feature

karaf@root()> feature:install pax-web-jetty-keycloak                                                                                       

After adding this to etc/org.ops4j.pax.web.context-cxf.cfg:

bundle.symbolicName = org.apache.cxf.cxf-rt-transports-http
context.id = default

context.param.keycloak.config.resolver = org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver
login.config.authMethod = KEYCLOAK
security.cxf.url = /cxf/*
security.cxf.roles = paxweb-admin, paxweb-viewer

And this to etc/keycloak.json:

{
  "realm": "paxweb",
  "auth-server-url": "http://localhost:8180/auth",
  "ssl-required": "external",
  "resource": "cxf",
  "use-resource-role-mappings": true,
  "confidential-port": 0,
  "principal-attribute": "preferred_username",
  "bearer-only": true
}

I'm seeing this in logs:

2023-09-26T16:40:44,579 | INFO  | paxweb-config-1-thread-1 | JettyServerWrapper               | 115 - org.ops4j.pax.web.pax-web-jetty - 8.0.22 | Adding init parameters to OsgiContextModel{HS,id=OCM-16,name='default',path='/',bundle=org.apache.cxf.cxf-rt-transports-http,context=DefaultHttpContext{bundle=org.apache.cxf.cxf-rt-transports-http [78],contextId='default'}}: {keycloak.config.resolver=org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver}
2023-09-26T16:40:44,579 | INFO  | paxweb-config-1-thread-1 | JettyServerWrapper               | 115 - org.ops4j.pax.web.pax-web-jetty - 8.0.22 | Adding security configuration to OsgiContextModel{HS,id=OCM-16,name='default',path='/',bundle=org.apache.cxf.cxf-rt-transports-http,context=DefaultHttpContext{bundle=org.apache.cxf.cxf-rt-transports-http [78],contextId='default'}}
2023-09-26T16:40:44,580 | INFO  | paxweb-config-1-thread-1 | JettyServerWrapper               | 115 - org.ops4j.pax.web.pax-web-jetty - 8.0.22 | Starting Jetty context "/" with default Osgi Context OsgiContextModel{HS,id=OCM-16,name='default',path='/',bundle=org.apache.cxf.cxf-rt-transports-http,context=DefaultHttpContext{bundle=org.apache.cxf.cxf-rt-transports-http [78],contextId='default'}}
2023-09-26T16:40:44,590 | INFO  | paxweb-config-1-thread-1 | AbstractKeycloakJettyAuthenticator | 115 - org.ops4j.pax.web.pax-web-jetty - 8.0.22 | The specified resolver org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver

And the reason is that ClassLoader for / ServletContext sees only 2 bundles:

result = {org.ops4j.pax.web.service.spi.servlet.OsgiServletContextClassLoader@11715} 
 assertionLock: java.lang.Object  = {org.ops4j.pax.web.service.spi.servlet.OsgiServletContextClassLoader@11715} 
 bundles: java.util.List  = {java.util.Collections$UnmodifiableRandomAccessList@11723}  size = 2
  0 = {org.apache.felix.framework.BundleImpl@11739} "org.apache.cxf.cxf-rt-transports-http [78]"
  1 = {org.apache.felix.framework.BundleImpl@11740} "org.ops4j.pax.web.pax-web-jetty [115]"

In Karaf 4.3 and earlier and in Pax Web 7 the classloader management was a bit broken (when talking about compliance with Whiteboard specification) and I don't remember exactly how it worked with Jetty.

grgrzybek commented 1 year ago

Same problem with pax-web-http-undertow:

result = {org.ops4j.pax.web.service.spi.servlet.OsgiServletContextClassLoader@14008} 
 assertionLock: java.lang.Object  = {org.ops4j.pax.web.service.spi.servlet.OsgiServletContextClassLoader@14008} 
 bundles: java.util.List  = {java.util.Collections$UnmodifiableRandomAccessList@14009}  size = 2
  0 = {org.apache.felix.framework.BundleImpl@14027} "org.apache.cxf.cxf-rt-transports-http [161]"
  1 = {org.apache.felix.framework.BundleImpl@14028} "org.ops4j.pax.web.pax-web-undertow [135]"

To install pax-web-http-undertow, you have to blacklist pax-web-http-jetty feature first, by adding this to etc/org.apache.karaf.features.xml:

    <blacklistedFeatures>
        <feature>pax-web-http-jetty</feature>
    </blacklistedFeatures>

The reason is this chain of feature dependencies: cxf-jaxrs → cxf-httphttppax-web-httppax-web-http-jetty

And finally the same for pax-web-http-tomcat:

result = {org.apache.catalina.loader.ParallelWebappClassLoader@17539} "ParallelWebappClassLoader\r\n  context: ROOT ...
...
 java.lang.ClassLoader.parent: java.lang.ClassLoader  = {org.ops4j.pax.web.service.spi.servlet.OsgiServletContextClassLoader@17553} 
  assertionLock: java.lang.Object  = {org.ops4j.pax.web.service.spi.servlet.OsgiServletContextClassLoader@17553} 
  bundles: java.util.List  = {java.util.Collections$UnmodifiableRandomAccessList@17580}  size = 2
   0 = {org.apache.felix.framework.BundleImpl@17590} "org.apache.cxf.cxf-rt-transports-http [161]"
   1 = {org.apache.felix.framework.BundleImpl@17591} "org.ops4j.pax.web.pax-web-tomcat [205]"
grgrzybek commented 1 year ago

Tomorrow I'll check with Keycloak 18.

grgrzybek commented 1 year ago

Here's an answer why it works with Keycloak 18:

bundles: java.util.List  = {java.util.Collections$UnmodifiableRandomAccessList@16534}  size = 5
 0 = {org.apache.felix.framework.BundleImpl@16448} "org.apache.cxf.cxf-rt-transports-http [122]"
 1 = {org.apache.felix.framework.BundleImpl@16465} "org.ops4j.pax.web.pax-web-undertow [238]"
  ...
  m_revisions: java.util.List  = {java.util.ArrayList@16601}  size = 1
   0 = {org.apache.felix.framework.BundleRevisionImpl@16602} "org.ops4j.pax.web.pax-web-undertow [238](R 238.0)"
    ...
    m_wiring: org.apache.felix.framework.BundleWiringImpl  = {org.apache.felix.framework.BundleWiringImpl@16467} "org.ops4j.pax.web.pax-web-undertow [238]"
     ...
     m_fragments: java.util.List  = {java.util.ArrayList@16631}  size = 1
      0 = {org.apache.felix.framework.BundleRevisionImpl@16800} "org.keycloak.keycloak-pax-web-undertow [256](R 256.0)"
 2 = {org.apache.felix.framework.BundleImpl@16591} "org.ops4j.pax.web.pax-web-jsp [233]"
 3 = {org.apache.felix.framework.BundleImpl@16592} "io.undertow.websockets-jsr [51]"
 4 = {org.apache.felix.framework.BundleImpl@16593} "org.ops4j.pax.web.pax-web-undertow-websocket [239]"

org.keycloak.keycloak-pax-web-undertow is a fragment attached to org.ops4j.pax.web.pax-web-undertow bundle:

karaf@root()> la | grep Undertow
...
238 │ Active   │  30 │ 8.0.22                     │ OPS4J Pax Web - Undertow, Fragments: 256
...
256 │ Resolved │  80 │ 18.0.6.redhat-00001        │ Keycloak Fuse 7.0 Adapter - Undertow, Hosts: 238

So org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver is loaded via the fragment content.

I'll do two things:

grgrzybek commented 1 year ago

Rest example feature repo:

karaf@root()> repo-add mvn:org.apache.karaf.examples/karaf-rest-example-features/4.4.4/xml                                                                                                                                                     
Adding feature url mvn:org.apache.karaf.examples/karaf-rest-example-features/4.4.4/xml

Jetty:

karaf@root()> feature:install pax-web-http-jetty pax-web-karaf 

The example feature:

karaf@root()> feature:install karaf-rest-example-blueprint 

karaf@root()> web:servlet-list 
Bundle ID │ Name                       │ Class                                                │ Context Path(s) │ URLs   │ Type
──────────┼────────────────────────────┼──────────────────────────────────────────────────────┼─────────────────┼────────┼────────────
99        │ cxf-osgi-transport-servlet │ org.apache.cxf.transport.servlet.CXFNonSpringServlet │ /               │ /cxf/* │ HttpService

Keycloak 18 features:

karaf@root()> feature:repo-add mvn:org.keycloak/keycloak-osgi-features/18.0.2/xml/features                                                                                                                                                     
Adding feature url mvn:org.keycloak/keycloak-osgi-features/18.0.2/xml/features

karaf@root()> feature:install -v keycloak-adapter-core                                                                                                                                                                                         
Adding features: keycloak-adapter-core/[18.0.2,18.0.2]
Changes to perform:
  Region: root
    Bundles to install:
      mvn:org.apache.httpcomponents/httpclient-osgi/4.5.2
      mvn:org.apache.httpcomponents/httpcore-osgi/4.4.4
      mvn:org.keycloak/keycloak-adapter-core/18.0.2
      mvn:org.keycloak/keycloak-adapter-spi/18.0.2
      mvn:org.keycloak/keycloak-authz-client/18.0.2
      mvn:org.keycloak/keycloak-common/18.0.2
      mvn:org.keycloak/keycloak-core/18.0.2
...

etc/keycloak.json:

{
  "realm": "paxweb",
  "auth-server-url": "http://localhost:8180/auth",
  "ssl-required": "external",
  "resource": "cxf",
  "use-resource-role-mappings": true,
  "confidential-port": 0,
  "principal-attribute": "preferred_username",
  "bearer-only": true
}

Jetty bundles (because there's no proper Keycloak feature related to Jetty):

karaf@root()> install mvn:org.keycloak/keycloak-osgi-adapter/18.0.2                                                                                                                                                                            
Bundle ID: 130
karaf@root()> install mvn:org.keycloak/keycloak-jetty-adapter-spi/18.0.2                                                                                                                                                                       
Bundle ID: 131
karaf@root()> install mvn:org.keycloak/keycloak-jetty-core/18.0.2                                                                                                                                                                              
Bundle ID: 132
karaf@root()> install mvn:org.keycloak/keycloak-jetty94-adapter/18.0.2                                                                                                                                                                         
Bundle ID: 133
karaf@root()> install mvn:org.keycloak/keycloak-pax-web-jetty94/18.0.2                                                                                                                                                                         
Bundle ID: 134

etc/org.ops4j.pax.web.context-cxf.cfg:

bundle.symbolicName = org.apache.cxf.cxf-rt-transports-http
context.id = default

context.param.keycloak.config.resolver = org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver
login.config.authMethod = KEYCLOAK
security.cxf.url = /cxf/*
security.cxf.roles = paxweb-admin, paxweb-viewer

However I see the same problem again:

2023-09-27T10:56:54,624 | INFO  | paxweb-config-1-thread-1 | AbstractKeycloakJettyAuthenticator | 132 - org.keycloak.keycloak-jetty-core - 18.0.2 | The specified resolver org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver

The reason is that org.keycloak.keycloak-pax-web-jetty94 fragment attached to pax-web-jetty bundle doesn't have org.keycloak.adapters.osgi import:

m_fragments: java.util.List  = {java.util.ArrayList@10911}  size = 1
 0 = {org.apache.felix.framework.BundleRevisionImpl@10926} "org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)"
  m_activationExcludes: java.util.List  = null
  m_activationIncludes: java.util.List  = null
  m_bundle: org.apache.felix.framework.BundleImpl  = {org.apache.felix.framework.BundleImpl@10935} "org.keycloak.keycloak-pax-web-jetty94 [134]"
  m_content: org.apache.felix.framework.cache.Content  = {org.apache.felix.framework.cache.JarContent@10936} "JAR /data/servers/apache-karaf-4.4.4/data/cache/bundle134/version0.0/bundle.jar"
  m_contentPath: java.util.List  = null
  m_declaredActivationPolicy: int  = 0
  m_declaredCaps: java.util.List  = {java.util.ArrayList@10933}  size = 2
  m_declaredNativeLibs: java.util.List  = null
  m_declaredReqs: java.util.List  = {java.util.ArrayList@10934}  size = 10
   0 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10945} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.host; (&(osgi.wiring.host=org.ops4j.pax.web.pax-web-jetty)(bundle-version>=0.0.0))"
   1 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10946} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.package; (&(osgi.wiring.package=org.eclipse.jetty.security)(version>=9.4.0)(!(version>=10.0.0)))"
   2 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10947} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.package; (&(osgi.wiring.package=org.eclipse.jetty.util.security)(version>=9.4.0)(!(version>=10.0.0)))"
   3 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10948} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.package; (&(osgi.wiring.package=org.jboss.logging)(version>=3.4.0)(!(version>=4.0.0)))"
   4 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10949} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.package; (&(osgi.wiring.package=org.keycloak.adapters.jetty)(version>=18.0.2))"
   5 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10950} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.package; (&(osgi.wiring.package=org.ops4j.pax.web.service)(version>=7.1.0)(!(version>=9.0.0)))"
   6 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10951} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.package; (&(osgi.wiring.package=org.ops4j.pax.web.service.spi.model)(version>=7.1.0)(!(version>=9.0.0)))"
   7 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10952} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.package; (&(osgi.wiring.package=org.osgi.framework)(version>=1.5.0)(!(version>=2.0.0)))"
   8 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10953} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.package; (&(osgi.wiring.package=org.osgi.service.http)(version>=1.2.0)(!(version>=2.0.0)))"
   9 = {org.apache.felix.framework.wiring.BundleRequirementImpl@10954} "[org.keycloak.keycloak-pax-web-jetty94 [134](R 134.0)] osgi.wiring.package; (&(osgi.wiring.package=org.osgi.util.tracker)(version>=1.4.0)(!(version>=2.0.0)))"

I tried to fix it with https://issues.redhat.com/browse/KEYCLOAK-19939 and didn't review it when implementing it in Pax Web itself.

However if you create a fragment attached to org.ops4j.pax.web.pax-web-jetty bundle with additional:

Import-Package: org.keycloak.adapters.osgi

it should work.

grgrzybek commented 1 year ago

Checking with Tomcat now.

Add this to etc/org.apache.karaf.features.xml:

<blacklistedFeatures>
    <feature>pax-web-jetty</feature>
    <feature>pax-web-http-jetty</feature>
</blacklistedFeatures>

Switch from Jetty to Tomcat (based on setup from previous comment):

karaf@root()> uninstall org.keycloak.keycloak-jetty-adapter-spi
karaf@root()> uninstall org.keycloak.keycloak-jetty-core
karaf@root()> uninstall org.keycloak.keycloak-jetty94-adapter
karaf@root()> uninstall org.keycloak.keycloak-pax-web-jetty94

karaf@root()> feature:refresh
karaf@root()> feature:install pax-web-tomcat-keycloak18

4 previous Jetty related bundles from Keycloak are now replaced with 1 Pax Web related pax-web-tomcat-keycloak18 feature to fix OSGi problems on Keycloak side.

However the problem is still there:

2023-09-27T11:06:39,808 | ERROR | paxweb-config-1-thread-1 | AbstractKeycloakAuthenticatorValve | 136 - org.ops4j.pax.web.pax-web-tomcat - 8.0.22 | The specified resolver org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver

However I could easily fix this on Pax Web side and get:

2023-09-27T11:15:00,929 | DEBUG | paxweb-config-1-thread-1 | AbstractKeycloakAuthenticatorValve | 136 - org.ops4j.pax.web.pax-web-tomcat - 8.0.22 | Using org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver to resolve Keycloak configuration on a per-request basis.

With my little fix, I could get proper Keycloak response:

$ curl -XPOST -H'Content-Type: application/json' --data '{"id":"42","customer":"pax-web","flight":"0000-0001"}' -i http://localhost:8181/cxf/booking
HTTP/1.1 401 
WWW-Authenticate: Bearer realm="paxweb"
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 686
Date: Wed, 27 Sep 2023 09:16:27 GMT
grgrzybek commented 1 year ago

Checking with Undertow.

karaf@root()> feature:uninstall pax-web-tomcat-keycloak18 
karaf@root()> feature:uninstall pax-web-http-tomcat 
karaf@root()> feature:install pax-web-http-undertow
karaf@root()> feature:install pax-web-undertow-keycloak18 

And it should work out of the box. Logs:

2023-09-27T11:24:13,176 | INFO  | paxweb-config-1-thread-1 | KeycloakServletExtension         | 153 - org.keycloak.keycloak-undertow-adapter - 18.0.2 | Using org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver to resolve Keycloak configuration on a per-request basis.

Request:

$ curl -XPOST -H'Content-Type: application/json' --data '{"id":"42","customer":"pax-web","flight":"0000-0001"}' -i http://localhost:8181/cxf/booking
HTTP/1.1 401 Unauthorized
Expires: 0
Connection: keep-alive
WWW-Authenticate: Bearer realm="paxweb"
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: text/html;charset=UTF-8
Content-Length: 71
Date: Wed, 27 Sep 2023 09:24:45 GMT

<html><head><title>Error</title></head><body>Unauthorized</body></html>
grgrzybek commented 1 year ago

So I agree:

Without using org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver, everything should work - see:

samples/samples-whiteboard/whiteboard-security sample has proper:

<Import-Package>
...
    <!-- Needed to use org.keycloak.adapters.osgi.PathBasedKeycloakConfigResolver -->
    org.keycloak.adapters.osgi;version="[18,22)";resolution:=optional
</Import-Package>

So the problems with CXF are not occurring.

GuliyevFarid commented 1 year ago

@grgrzybek thanks a lot. I will check and proceed with the instructions you mentioned for Pax Web + Undertow + Keycloak 18.

GuliyevFarid commented 1 year ago

@grgrzybek I have checked Pax Web + Undertow + Keycloak 18 integration using your instructions, and it worked for me. I was missing the following command to install the bundle, which was crucial for Configresolver.

karaf@root()> install mvn:org.keycloak/keycloak-osgi-adapter/18.0.2

Thanks a lot. this solved my problem.

It would also be great to have Pax Web + Jetty + Keycloak 18 integration.

grgrzybek commented 1 year ago

Thanks a lot. this solved my problem.

Great to hear!

It would also be great to have Pax Web + Jetty + Keycloak 18 integration.

I'm afraid it'll be much easier to simply fix Pax Web + Jetty/Tomcat/Undertow on latest Keycloak version (which dropped Pax Web support) than to fix it for Keycloak 18 - but I'll see what can I do.

grgrzybek commented 1 year ago

Another instruction for pax-web-jetty after new fixes:

Start with fresh Karaf 4.4.4.

Rest example feature repo:

karaf@root()> repo-add mvn:org.apache.karaf.examples/karaf-rest-example-features/4.4.4/xml                                                                                                                                                     
Adding feature url mvn:org.apache.karaf.examples/karaf-rest-example-features/4.4.4/xml

Jetty:

karaf@root()> feature:install pax-web-http-jetty pax-web-karaf 

The example feature:

karaf@root()> feature:install karaf-rest-example-blueprint 

karaf@root()> web:servlet-list 
Bundle ID │ Name                       │ Class                                                │ Context Path(s) │ URLs   │ Type
──────────┼────────────────────────────┼──────────────────────────────────────────────────────┼─────────────────┼────────┼────────────
99        │ cxf-osgi-transport-servlet │ org.apache.cxf.transport.servlet.CXFNonSpringServlet │ /               │ /cxf/* │ HttpService

Keycloak 18 features:

karaf@root()> feature:repo-add mvn:org.keycloak/keycloak-osgi-features/18.0.2/xml/features                                                                                                                                                     
Adding feature url mvn:org.keycloak/keycloak-osgi-features/18.0.2/xml/features

Now new pax-web-jetty-keycloak18 feature:

karaf@root()> feature:install -v -t pax-web-jetty-keycloak18
Adding features: pax-web-jetty-keycloak18/[8.0.23.SNAPSHOT,8.0.23.SNAPSHOT]
Changes to perform:
  Region: root
    Bundles to update:
      org.eclipse.jetty.servlet/9.4.52.v20230823 with mvn:org.eclipse.jetty/jetty-servlet/9.4.53.v20231009
      org.eclipse.jetty.servlets/9.4.52.v20230823 with mvn:org.eclipse.jetty/jetty-servlets/9.4.53.v20231009
      org.eclipse.jetty.io/9.4.52.v20230823 with mvn:org.eclipse.jetty/jetty-io/9.4.53.v20231009
...
    Bundles to install:
      mvn:org.apache.httpcomponents/httpclient-osgi/4.5.14
      mvn:org.apache.httpcomponents/httpcore-osgi/4.4.16
      mvn:org.keycloak/keycloak-adapter-core/18.0.2
      mvn:org.keycloak/keycloak-adapter-spi/18.0.2
      mvn:org.keycloak/keycloak-authz-client/18.0.2
      mvn:org.keycloak/keycloak-common/18.0.2
      mvn:org.keycloak/keycloak-core/18.0.2
      mvn:org.keycloak/keycloak-osgi-adapter/18.0.2
      mvn:org.ops4j.pax.web/pax-web-jetty-keycloak18/8.0.23-SNAPSHOT

This is a new feature which should be installed instead of separate Keycloak bundles. It includes only one bundle now: pax-web-jetty-keycloak18 which repackages Keycloak 18 bundles and adds required import to find org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver.

etc/keycloak.json:

{
  "realm": "paxweb",
  "auth-server-url": "http://localhost:8180/auth",
  "ssl-required": "external",
  "resource": "cxf",
  "use-resource-role-mappings": true,
  "confidential-port": 0,
  "principal-attribute": "preferred_username",
  "bearer-only": true
}

etc/org.ops4j.pax.web.context-cxf.cfg:

bundle.symbolicName = org.apache.cxf.cxf-rt-transports-http
context.id = default

context.param.keycloak.config.resolver = org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver
login.config.authMethod = KEYCLOAK
security.cxf.url = /cxf/*
security.cxf.roles = paxweb-admin, paxweb-viewer

Now I see:

2023-10-11T15:14:52,745 | INFO  | paxweb-config-1-thread-1 | JettyServerWrapper               | 69 - org.ops4j.pax.web.pax-web-jetty - 8.0.22 | Starting Jetty context "/" with default Osgi Context OsgiContextModel{HS,id=OCM-4,name='default',path='/',bundle=org.apache.cxf.cxf-rt-transports-http,context=DefaultHttpContext{bundle=org.apache.cxf.cxf-rt-transports-http [99],contextId='default'}}
2023-10-11T15:14:52,969 | INFO  | paxweb-config-1-thread-1 | AbstractKeycloakJettyAuthenticator | 69 - org.ops4j.pax.web.pax-web-jetty - 8.0.22 | Using org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver to resolve Keycloak configuration on a per-request basis.
2023-10-11T15:14:52,972 | INFO  | paxweb-config-1-thread-1 | ContextHandler                   | 63 - org.eclipse.jetty.util - 9.4.53.v20231009 | Started o.o.p.w.s.j.i.PaxWebServletContextHandler@7c2de19c{/,null,AVAILABLE}

And it seems to work:

$ curl -XPOST -H'Content-Type: application/json' --data '{"id":"42","customer":"pax-web","flight":"0000-0001"}' -i http://localhost:8181/cxf/booking
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="paxweb"
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1
Content-Length: 402

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 401 Unauthorized</title>
</head>
<body><h2>HTTP ERROR 401 Unauthorized</h2>
<table>
<tr><th>URI:</th><td>/cxf/booking</td></tr>
<tr><th>STATUS:</th><td>401</td></tr>
<tr><th>MESSAGE:</th><td>Unauthorized</td></tr>
<tr><th>SERVLET:</th><td>cxf-osgi-transport-servlet</td></tr>
</table>

</body>
</html>
grgrzybek commented 1 year ago

For Tomcat, after fixing pax-web-tomcat-keycloak18 bundle installed with pax-web-tomcat-keycloak18 feature, I also see correct configuration:

2023-10-11T15:23:39,031 | DEBUG | paxweb-config-1-thread-1 | AbstractKeycloakAuthenticatorValve | 133 - org.ops4j.pax.web.pax-web-tomcat - 8.0.23.SNAPSHOT | Using org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver to resolve Keycloak configuration on a per-request basis.
$ curl -XPOST -H'Content-Type: application/json' --data '{"id":"42","customer":"pax-web","flight":"0000-0001"}' -i http://localhost:8181/cxf/booking
HTTP/1.1 401 
WWW-Authenticate: Bearer realm="paxweb"
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 686
Date: Wed, 11 Oct 2023 13:24:45 GMT

<!doctype html><html lang="en"><head><title>HTTP Status 401 – Unauthorized</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 401 – Unauthorized</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The request has not been applied to the target resource because it lacks valid authentication credentials for that resource.</p><hr class="line" /><h3>Apache Tomcat/9.0.80</h3></body></html>
grgrzybek commented 1 year ago

(sorry, bad commit message for Upgrade to Tomcat 9.0.82 commit)

grgrzybek commented 1 year ago

Checking with 22.0.4:

I started fresh Keycloak 22.0.4 server (JDK 17 required):

$ bin/kc.sh start-dev --http-port 8180

Browse to http://localhost:8180/, create administrator user and browse to http://localhost:8180/admin/master/console/.

Import https://github.com/ops4j/org.ops4j.pax.web/blob/pax-web-8.0.x/pax-web-keycloak/realm-export.json as paxweb realm.

I added another client cxf with "Direct Access Grants" only and http://127.0.0.1:8181 root/home/admin URLs. This client has to have "Client authentication" enabled and on "Credentials" tab, "Client ID and secret" has to be selected.

Install pax-web-jetty-keycloak feature:

karaf@root()> feature:install pax-web-jetty-keycloak                                                                                       

Install rest example:

karaf@root()> repo-add mvn:org.apache.karaf.examples/karaf-rest-example-features/4.4.4/xml                                                                                                                                                     
Adding feature url mvn:org.apache.karaf.examples/karaf-rest-example-features/4.4.4/xml
karaf@root()> feature:install karaf-rest-example-blueprint

After adding this to etc/org.ops4j.pax.web.context-cxf.cfg:

bundle.symbolicName = org.apache.cxf.cxf-rt-transports-http
context.id = default

context.param.keycloak.config.resolver = org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver
login.config.authMethod = KEYCLOAK
security.cxf.url = /cxf/*
security.cxf.roles = paxweb-admin, paxweb-viewer

And this to etc/keycloak.json:

{
  "realm": "paxweb",
  "auth-server-url": "http://localhost:8180/",
  "ssl-required": "external",
  "resource": "cxf",
  "confidential-port": 0,
  "principal-attribute": "preferred_username",
  "bearer-only": true
}

I'm seeing this in logs:

2023-10-12T11:15:53,387 | INFO  | paxweb-config-1-thread-1 | AbstractKeycloakJettyAuthenticator | 80 - org.ops4j.pax.web.pax-web-jetty - 8.0.23.SNAPSHOT | Using org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver to resolve Keycloak configuration on a per-request basis.

And this with CURL I see authentication problem:

$ curl -XPOST -H'Content-Type: application/json' --data '{"id":"42","customer":"pax-web","flight":"0000-0001"}' -i http://localhost:8181/cxf/booking
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="paxweb"
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1
Content-Length: 402

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 401 Unauthorized</title>
</head>
<body><h2>HTTP ERROR 401 Unauthorized</h2>
<table>
<tr><th>URI:</th><td>/cxf/booking</td></tr>
<tr><th>STATUS:</th><td>401</td></tr>
<tr><th>MESSAGE:</th><td>Unauthorized</td></tr>
<tr><th>SERVLET:</th><td>cxf-osgi-transport-servlet</td></tr>
</table>

</body>
</html>

However I can log in using cxf client's secret (from Keycloak configuration of cxf client - here it's 0f7nPJ7iK93BUirCIR6LFJgRQ6XvcNgm):

$ curl -v -XPOST -u cxf:0f7nPJ7iK93BUirCIR6LFJgRQ6XvcNgm -H'Content-Type: application/x-www-form-urlencoded' -d 'grant_type=password&username=admin&password=passw0rd' http://127.0.0.1:8180/realms/paxweb/protocol/openid-connect/token
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1:8180...
* Connected to 127.0.0.1 (127.0.0.1) port 8180 (#0)
* Server auth using Basic with user 'cxf'
> POST /realms/paxweb/protocol/openid-connect/token HTTP/1.1
> Host: 127.0.0.1:8180
> Authorization: Basic Y3hmOjBmN25QSjdpSzkzQlVpckNJUjZMRkpnUlE2WHZjTmdt
> User-Agent: curl/8.0.1
> Accept: */*
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 52
> 
< HTTP/1.1 200 OK
< Referrer-Policy: no-referrer
< X-Frame-Options: SAMEORIGIN
< Strict-Transport-Security: max-age=31536000; includeSubDomains
< Cache-Control: no-store
< X-Content-Type-Options: nosniff
< Set-Cookie: KEYCLOAK_LOCALE=; Version=1; Comment=Expiring cookie; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/paxweb/; HttpOnly
< Set-Cookie: KC_RESTART=; Version=1; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/paxweb/; HttpOnly
< Pragma: no-cache
< X-XSS-Protection: 1; mode=block
< Content-Type: application/json
< content-length: 2192
< 
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJlSFJadVhvTW8tZ0NmcG5Xd19qNVJLX3RuQWphLWRHR25oWWJvWVNWMXI4In0.eyJleHAiOjE2OTcxMDUwODYsImlhdCI6MTY5NzEwNDc4NiwianRpIjoiODMzZGRhMGUtMDgzZC00NGM3LWJhMDItMzg1OTNmZThlODI3IiwiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo4MTgwL3JlYWxtcy9wYXh3ZWIiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiZGVkNjRmYmItMGFiYy00ZWIyLThiNDItNjczOTEyMmJhMTRlIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiY3hmIiwic2Vzc2lvbl9zdGF0ZSI6IjIzZmJkMTZlLWE4MTctNDM0Ny1hMTRhLTVjNDdkMzcxN2MyNiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovLzEyNy4wLjAuMTo4MTgxIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLXBheHdlYiIsIm9mZmxpbmVfYWNjZXNzIiwicGF4d2ViLWFkbWluIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiIyM2ZiZDE2ZS1hODE3LTQzNDctYTE0YS01YzQ3ZDM3MTdjMjYiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6ImFkbWluIiwiZ2l2ZW5fbmFtZSI6IiIsImZhbWlseV9uYW1lIjoiIn0.KvpSrPRqmvhm8v7G-LFUsG6V8Fu5cr5CHf7k7YEkEFBFNvzBIMGEaMGtNUfek8BfXmGGPQWbdg2cRA_C5wBfnv8c-dTcVONW8egTrUnRAfJP9P-ST6eWI-x4dR4fjVLIq4aneOzSX-L3025A1rlXg8VXlI8TarvlE1BzQu-ngLILnrPI0Tp7tjTN0cUe4UjdblMULdoSa0nM4jULW83IP7Kk-CPp_cLmS3lwl5FeMAYB9MAlzsRkffUIryxVooFqXKoEl5SU7rjAWJPmH1UMT7dt0p2FVuo1b9BHYAwyGiXtrNNqdoahQk8X1-xDFFQdYKxgUmNZqf1xdEofYug1HQ","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1YTcxMDE2OS0yMjQyLTRhNDUtYjc2OC02ZWVlZTFjOTQ5NGUifQ.eyJleHAiOjE2OTcxMDY1ODYsImlhdCI6MTY5NzEwNDc4NiwianRpIjoiM2Q2NTNlZGYtZTJhMi00ZmI1LThkZjQtOGFjNWMzMThkZTBlIiwiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo4MTgwL3JlYWxtcy9wYXh3ZWIiLCJhdWQiOiJodHRwOi8vMTI3LjAuMC4xOjgxODAvcmVhbG1zL3BheHdlYiIsInN1YiI6ImRlZDY0ZmJiLTBhYmMtNGViMi04YjQyLTY3MzkxMjJiYTE0ZSIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJjeGYiLCJzZXNzaW9uX3N0YXRlIjoiMjNmYmQxNmUtYTgxNy00MzQ3LWExNGEtNWM0N2QzNzE3YzI2Iiwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwic2lkIjoiMjNmYmQxNmUtYTgxNy00MzQ3LWExNGEtNWM0N2QzNzE3YzI2In0.uscsweXuT3vSqchmpp_EtTcO* Connection #0 to host 127.0.0.1 left intact
9t8XfSqYe5YnCVqesJU","token_type":"Bearer","not-before-policy":0,"session_state":"23fbd16e-a817-4347-a14a-5c47d3717c26","scope":"email profile"}

$ export T=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJlSFJadVhvTW8tZ0NmcG5Xd19qNVJLX3RuQWphLWRHR25oWWJvWVNWMXI4In0.eyJleHAiOjE2OTcxMDUwODYsImlhdCI6MTY5NzEwNDc4NiwianRpIjoiODMzZGRhMGUtMDgzZC00NGM3LWJhMDItMzg1OTNmZThlODI3IiwiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo4MTgwL3JlYWxtcy9wYXh3ZWIiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiZGVkNjRmYmItMGFiYy00ZWIyLThiNDItNjczOTEyMmJhMTRlIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiY3hmIiwic2Vzc2lvbl9zdGF0ZSI6IjIzZmJkMTZlLWE4MTctNDM0Ny1hMTRhLTVjNDdkMzcxN2MyNiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovLzEyNy4wLjAuMTo4MTgxIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLXBheHdlYiIsIm9mZmxpbmVfYWNjZXNzIiwicGF4d2ViLWFkbWluIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiIyM2ZiZDE2ZS1hODE3LTQzNDctYTE0YS01YzQ3ZDM3MTdjMjYiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6ImFkbWluIiwiZ2l2ZW5fbmFtZSI6IiIsImZhbWlseV9uYW1lIjoiIn0.KvpSrPRqmvhm8v7G-LFUsG6V8Fu5cr5CHf7k7YEkEFBFNvzBIMGEaMGtNUfek8BfXmGGPQWbdg2cRA_C5wBfnv8c-dTcVONW8egTrUnRAfJP9P-ST6eWI-x4dR4fjVLIq4aneOzSX-L3025A1rlXg8VXlI8TarvlE1BzQu-ngLILnrPI0Tp7tjTN0cUe4UjdblMULdoSa0nM4jULW83IP7Kk-CPp_cLmS3lwl5FeMAYB9MAlzsRkffUIryxVooFqXKoEl5SU7rjAWJPmH1UMT7dt0p2FVuo1b9BHYAwyGiXtrNNqdoahQk8X1-xDFFQdYKxgUmNZqf1xdEofYug1HQ

And now I can access CXF endpoint:

$ curl -XPOST -H"Authorization: Bearer $T" -H 'Content-Type: application/json' --data '{"id":"42","customer":"pax-web","flight":"0000-0001"}' -i http://127.0.0.1:8181/cxf/booking
HTTP/1.1 204 No Content
Date: Thu, 12 Oct 2023 10:00:05 GMT

$ curl -XGET -H"Authorization: Bearer $T" -i http://127.0.0.1:8181/cxf/booking/42
HTTP/1.1 200 OK
Date: Thu, 12 Oct 2023 10:00:26 GMT
Content-Type: application/json
Transfer-Encoding: chunked

{"id":42,"customer":"pax-web","flight":"0000-0001"}

And I can't access it without the token:

$ curl -XGET -i http://127.0.0.1:8181/cxf/booking/42
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="paxweb"
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1
Content-Length: 405

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 401 Unauthorized</title>
</head>
<body><h2>HTTP ERROR 401 Unauthorized</h2>
<table>
<tr><th>URI:</th><td>/cxf/booking/42</td></tr>
<tr><th>STATUS:</th><td>401</td></tr>
<tr><th>MESSAGE:</th><td>Unauthorized</td></tr>
<tr><th>SERVLET:</th><td>cxf-osgi-transport-servlet</td></tr>
</table>

</body>
</html>
grgrzybek commented 1 year ago

It works with Tomcat and Undertow as well.