how to enable CORS with Jenkins; or add CORS to Jenkins?

8,579 views
Skip to first unread message

Alexis Gallagher

unread,
Apr 3, 2013, 12:52:29 PM4/3/13
to jenkin...@googlegroups.com
Hi,

I'm wondering if anyone has had any success setting up Jenkins with cross origin resource sharing (aka, CORS, as described at http://enable-cors.org or more formally at http://www.w3.org/TR/cors/ ). Enabling CORS would let you call the Jenkins REST API from javascript, so you could setup a simplified webpage for triggering builds, setting parameters for triggering builds, observing the status of builds, etc..

One way to enable CORS would be to run Jenkins under Apache or some other server, and setup the CORS configurations on that server. So this is my first question: has that worked for anyone?

An alternative way, which I would ideally prefer, is to do it all within Jenkins using Jenkins's embedded server, since this would make it easier to administer. (Is that nuts?) The embedded Winstone servlet container does not offer configurations for CORS. However, I've found two open source java servlet filters that implement CORS (the one in jetty, and another one here ( http://software.dzhuvinov.com/cors-filter.html ). So I could adapt one of those servlet filters and then provide CORS either via a Jenkins plugin or via a patch on Jenkins itself.

So this leads to my other questions: Is this the sort of functionality that could be implemented through a plugin? One subtlety of CORS is that it's not just a mechanism for filtering requests and decorating responses. It also requires generating responses for http OPTIONS requests, for the CORS "preflight" requests. I haven't seen any existing Jenkins plugins that do anything similar, so I'm not sure if the plugin system would allow it. If this functionality could not be enabled via a plugin, would a patch for it be useful to others and welcome?

Alexis

 

Kohsuke Kawaguchi

unread,
Apr 3, 2013, 1:51:46 PM4/3/13
to jenkin...@googlegroups.com, Alexis Gallagher

Yes, you can serve an HTTP header from plugin, so this is possible as a
plugin. See PageDecorator. And while I'm not too familiar with this,
this might make sense in the core.

Plugins responding to OPTIONS call on and responding according to what's
actually sitting at the endpoint is more tricky from a plugin. So this
might better fit the core after all.

I'm also not too sure how this would interact with security. Do we only
do this when we allow anonymous read access? Wouldn't it still make it
vulnerable to CSRF if Jenkins is running inside the firewall (which is
probably the majority?)
> --
> You received this message because you are subscribed to the Google
> Groups "Jenkins Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to jenkinsci-de...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>


--
Kohsuke Kawaguchi http://kohsuke.org/

Alexis Gallagher

unread,
Apr 3, 2013, 5:46:42 PM4/3/13
to jenkin...@googlegroups.com, Alexis Gallagher
Hi Kohsuke,

Thanks for the reply.

On Wednesday, April 3, 2013 6:51:46 PM UTC+1, Kohsuke Kawaguchi wrote:

Yes, you can serve an HTTP header from plugin, so this is possible as a
plugin. See PageDecorator. And while I'm not too familiar with this,
this might make sense in the core.

(I presume you meant "this might make sense in a plugin.) 
 

Plugins responding to OPTIONS call on and responding according to what's
actually sitting at the endpoint is more tricky from a plugin. So this
might better fit the core after all.
 

Yes, I did some research and was looking at PageDecorator, and in particular at the XFrame plugin ( https://wiki.jenkins-ci.org/display/JENKINS/XFrame+Filter+Plugin )  and the HSTS plugin ( https://wiki.jenkins-ci.org/display/JENKINS/HSTS+Filter+Plugin ). However, looking at how they hooked into the plugin architecture, and at the PageDecorator javadoc, I get the impression there is no way to handle CORS preflight requests in a similar way.

This is because both these plugins seemed to decorate normal Jenkins response traffic, rather than intercepting the request object, which would be needed to detect the OPTIONS method, and generate an immediate response rather than forwarding the request to Jenkins for further processing.

Is there another kind of plugin that affects how Jenkins handles http request objects?
 
I'm also not too sure how this would interact with security. Do we only
do this when we allow anonymous read access? Wouldn't it still make it
vulnerable to CSRF if Jenkins is running inside the firewall (which is
probably the majority?)

The purpose of CORS is to let a server tell browsers that they should not block XmlHttpRequests (XHR) from javascript delivered from a particular, whitelisted domain. So CORS would be disabled by default, but could be deliberately enabled in order to allow such traffic.

For instance, in my situation, there is a jenkins server that lives at something like http://jenkins.company.com. And I would like to be able to manipulate it from JS hosted at http://configurationapp.company.com. Both domains would be within a VPN. So CORS on the Jenkins server would be configured to explicitly allow XHR requests only from configurationapp.company.com.

The main security risk, I believe, is only if someone incorrectly configured CORS on a secured domain (such as within a VPN, on http://jenkins.company.com) to allow traffic from a non-secure domain (such as one outside a VPN, at http://www.dangerouswebsite.com). Then the unsecured domain's JS could initiate http requests to the secured domain. However, if a resource on the resource was protected by credentials (like Jenkins's login), then the unsecured domain still would not be able to access it unless CORS had additionally been configured to permit credentials. Ann van Kestern (the CORS spec author) has a quite clear blog post on this: http://annevankesteren.nl/2012/12/cors-101

The bottom line is that if CORS is enabled by default, the it doesn't reduce the default security at all. But if you misconfigure it, then yes you could create new security vulnerabilities. But that is sort of its purpose.

It's also worth remembering that CORS does not create new vulnerabilities on the server, as such. It is only a mechanism for telling browsers they have permission to make certain requests. Anyone with a copy of curl could still make those requests by hand.

Alexis

Alexis Gallagher

unread,
Apr 3, 2013, 5:50:28 PM4/3/13
to jenkin...@googlegroups.com, Alexis Gallagher
Oops, correction:

I meant, "The bottom line is that if CORS is DISabled by default, the it doesn't reduce the default security at all."

A

evernat

unread,
Apr 4, 2013, 5:47:21 AM4/4/13
to jenkin...@googlegroups.com, Alexis Gallagher
In a servlet filter and so in a Jenkins plugin, it is possible to intercept http requests and to generate an immediate response.

For example :
public class MyFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest httpRequest = (HttpServletRequest) request;
        final HttpServletResponse httpResponse = (HttpServletResponse) response;
        if ("OPTIONS".equals(httpRequest.getMethod())) {
            httpResponse.getWriter().write("hello");
        } else {
            chain.doFilter(request, response);
        }
    }
    ...
}

Emeric

Alexis Gallagher

unread,
Apr 4, 2013, 5:58:25 AM4/4/13
to jenkin...@googlegroups.com, Alexis Gallagher
Thanks this is very helpful! If it can be done with a plugin, I presume that would be much easier.

Is the idea that one could define a plugin which subclasses which implemented hudson.util.ServletFilter ( http://javadoc.jenkins-ci.org/?hudson/util/PluginServletFilter.html ) ?

Are you aware of any existing plugins that do this successfully?

Alexis

evernat

unread,
Apr 4, 2013, 7:16:47 AM4/4/13
to jenkin...@googlegroups.com, Alexis Gallagher
No, you should probably not extend hudson.util.PluginServletFilter. You only need to implement the javax.servlet.Filter interface.

Then in a Jenkins plugin, you can call the following in the start() method of the PluginImpl class of your plugin:
   hudson.util.PluginServletFilter.addFilter(new MyFilter());

I know one plugin which do this and the source (mirrored) is here:
https://github.com/jenkinsci/monitoring-plugin

Emeric

Jesse Glick

unread,
Apr 4, 2013, 10:23:12 AM4/4/13
to jenkin...@googlegroups.com
On 04/04/2013 07:16 AM, evernat wrote:
> you can call the following in the start() method of the PluginImpl class of your plugin

Or use @Initializer.

Alexis Gallagher

unread,
Apr 9, 2013, 11:28:50 AM4/9/13
to jenkin...@googlegroups.com
Hi Jesse,

This is tantalizing and confusing. How would I use @Initializer here instead of addFilter ?

Alexis

Jesse Glick

unread,
Apr 9, 2013, 12:01:44 PM4/9/13
to jenkin...@googlegroups.com
On 04/09/2013 11:28 AM, Alexis Gallagher wrote:
> This is tantalizing and confusing. How would I use @Initializer here instead of addFilter ?

No, @Initializer instead of PluginImpl.

Alexis Gallagher

unread,
Apr 10, 2013, 6:06:56 AM4/10/13
to jenkin...@googlegroups.com
Hi,

Following advice here, I have been working on implementing a Jenkins plugin that enables CORS on Jenkins: https://github.com/algal/cors .

It's almost working -- the servlet filter which implements CORS (based on a fixed version of the one in jetty), the integration of the servlet filter into Jenkins, the UI for configuring the plugin, the help for configuring the plugin.

But....

I'm still confused about how to bind the plugin's fields to the jelly files correctly. In particular, I understand that I should be making calls to save() in my plugin's configure() method, and a call to load() in the plugin's constructor. But, for instance, if I uncomment save() in line 177 of


then I get an OutOfMemoryError a few seconds after I hit "Apply" on the Jenkins UI for system configuration. And if I uncomment the load() call in lines 78-83, then I need to handle exceptions that none of the other plugin examples seem to handle.

I'd really appreciate any advice.

Part of the problem, perhaps, is that I need to extend hudson.Plugin in order to make use of the setServletContext() method, but I can't find any other plugins that actually work by extending hudson.Plugin, so I may be missing something about how that class should be implemented differently.

Thanks, A

Anurag Garg

unread,
Sep 2, 2013, 9:42:56 AM9/2/13
to jenkin...@googlegroups.com
Hi Alexis

Is your filter now setup to allow CORS properly? I need it to access jenkins apis through my JS sitting on my localhost.
If yes, can you point to the setup instructions.
If no, can you help with an easy and quick alternative to do POST on jenkins APIs. 

Thanks
Anurag

Alexis Gallagher

unread,
Sep 5, 2013, 5:24:17 AM9/5/13
to jenkin...@googlegroups.com
Hi Anurag,

Yes, I did finally get the Jenkins CORS filter working, but I was never 100% confident in it. If I recall, this was largely because I couldn't find anything in Jenkins's plugin architecture that guaranteed the order of servlet filters. I suspect it wouldn't take long for someone who knew there way around Jenkins to ensure the plugin is correct, or fix any remaining issues. It's all still at the repo https://github.com/algal/cors .

In the end, for my problem, I learned someone was already running an nginx proxy server in front of my prod Jenkins installation. This made it easier to just implement CORS in the proxy server. You can find the nginx configurations I developed for CORS in this gist: https://gist.github.com/algal/5480916 .
Alexis

Anurag Garg

unread,
Sep 5, 2013, 12:12:28 PM9/5/13
to jenkin...@googlegroups.com
Thanks. Will look at it.

I have meanwhile shifted from calling the Jenkins rest Apis directly from my HTML web page. I now have a HTML client page which talks to my node js server which in turn is able to do POST and GET to the Jenkins Apis.

Thanks
Anurag
--
You received this message because you are subscribed to a topic in the Google Groups "Jenkins Developers" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jenkinsci-dev/Qy-zKX9hlpw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jenkinsci-de...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages