Jolokia and CORS
Jolokia has configurable CORS support so that it plays nicely together with the Browser world when it comes to cross origin requests. However, Jolokia’s CORS support is not without gotchas. This post explains how Jolokias CORS supports works, what are the issues and how I plan to solve them.
tldr; Jolokia CORS support is configured via
jolokia-access.xml but has issues with authenticated requests which are tackled for the next release 1.3.0
In simple cases, it works more or less like this:
- Since the the origin of the script and target URL of the request differs, the browser adds some extract checking on the response of this request.
- The request to b.com contains a header
- The server at b.com answering the request has to decided upon this header whether it wants allow this request. The server decision is contained in the response header
- The value of this header can be either a literal URL (e.g. http://a.com) or a wildcard like in
Access-Control-Allow-Origin: *which allows access from any original location.
This is it for simple requests. A simple request has the following characteristics:
- HTTP method is either
- The request contains only the following headers
Content-Typewith the value
If this criteria are not match for a request (e.g. because it uses a different method or additional headers), a so called preflight request is sent to the server before the actual request is performed. The preflight is an HTTP request with method
OPTIONS and contains the headers
Origin (http://a.com in our case),
Access-Control-Request-Method for the HTTP method requested and
Access-Control-Request-Headers with a comma separated list of additional header names. The server in turn answers with the allowed request methods and headers, whether an authenticated request is allowed and how long the client might cache this answer. An important point is, that a preflight request must not be authenticated.
And in fact, browsers never sent an authentication header with the preflight request even when already authenticated against the target server. More on this later.
Jolokia CORS Support
By default, Jolokia allows any CORS request. For the preflight the agent answers with
Access-Control-Allow-Origin: http://a.com Access-Control-Allow-Headers: accept, authorization, content-type
The allowed headers returned are exactly the same headers as requested. For the real request with an origin header
Origin: http://a.com the answer is
Access-Control-Allow-Origin: http://a.com Access-Control-Allow-Credentials: true
For best computability Jolokia always answers with the provided
Origin: which is extracted from the request (except when the origin is
null in which case the wildcard
* is returned.
This behavior can be tuned by adapting the
jolokia-access.xml policy as described in the reference manual :
<cors> <allow-origin>http://www.jolokia.org</allow-origin> <allow-origin>*://*.jmx4perl.org</allow-origin> <strict-checking/> </cors>
<cors> section is present in
jolokia-access.xml then only those hosts declared in this sections are allowed. The Origin URLs to match against can be specified either literally or as pattern containing the wildcard
*. The optional declaration
<strict-checking/> is not really connected to CORS but helps in defending against Cross-Site-Request-Forgery (CSRF). If this option is given, then the given patterns are used for every request to compare it against the
Referer: header (not only for CORS requests).
CORS and Authentication
Authorization: is for CORS not a simple header, when authentication is used, preflight checking is always applied. However, there is often a catch 22:
- The preflight check using the
OPTIONSHTTP Method must not be authenticated as explained above, so browser doesn’t send the appropriate authentication headers when doing the preflight.
- The Jolokia agent is typically secured completely no matter which HTTP method is used.
- The preflight check fails, the request fails.
The only clean solution is to setup Jolokia Authentication that way that
OPTIONS request are not secured.
Let’s have a look at the individual Agents:
Since the JVM agent does all the security stuff on its own, it is not a big deal to introduce this specific behavior. Next one.
The WAR agent use authentication and authorization as defined in the Servlet Specification, i.e. the appropriate
<security-constraint> must be added manually to the web.xml (jolokia is a CLI tool which helps in this repackaging). Unfortunately there is no way to secure the same
<url-pattern> differently for different HTTP Methods (i.e. secured with an
POST, but accessible for everybody for
OPTIONS). I tried hard by providing multiple
<security-constraint> but failed miserably (if you know how to this, please let me know).
The only solution is to switch over to checking a given role on our own without relying on the declarative JEE security mechanism. Since we can check the role programmatically (
HttpServletRequest.isUserInRole()) this should not be that big deal. But it’s still some work ….
When using an OSGI HttpService adding this behavior should not be difficult since security is handled programmatically here as well (
Other Agent variants
This is the dark matter, because I don’t know where and how Jolokia is integrated directly into a bigger context. I know that ActiveMQ, Karaf and Spring Boot uses Jolokia internally. In order to support authenticated CORS access they probably needs to be changed to allow unauthorized
OPTIONS access for everybody. Since this is not under my control I have no idea when and even whether it ever will happen. Generic purpose console like hawt.io rely in some setups on CORS access so it would be real cool if we can get it out there. Help with this is highly appreciated ;-)
Since 1.2.2 is already finished and about to be published today, the stuff I can do as described above will go into a 1.3.0. Looking back at my release history this will probably be ready approx. end of august.