6

I'm now trying to setup nginx as a reverse proxy for Hudson so it can be access on

http://build.example.com
directly. I've already installed Hudson on Tomcat and verified that I can access Hudson on
http://127.0.0.1:8080/hudson

It was pretty straight forward to get to where it's possible to access hudson on http://build.example.com/hudson. But when I tried to setup the proxy to remap the /hudson directory, things starts to go wrong. None of the css or image files are being loaded.

A quick look at the nginx access log tells me that lots of requests are being made to resources at /hudson or /hudson/static. These requests are not being forwarded (or rewritten) to Tomcat correctly. But after digging around for several hours, I'm at a lost at how to fix this simple problem.

The nginx settings I'm using looks like

server {
    listen          80;
    server_name     build.example.com;
    location / {
        proxy_pass        http://127.0.0.1:8080/hudson;
        proxy_set_header  Host             $http_host;
        proxy_set_header  X-Real-IP        $remote_addr;
        proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
    }
}

Can somebody shed some light on this? It's gotta be really simple to setup, but I can't seem to find the solution anywhere on the net.

Update

My sincere thanks to all those who took the time to answer this question. After trying various solutions, I think the most straight forward way to deal with this particular problem is either to...

A) Leave it alone As @VonC suggested, let it stay in a sub-directory. It's straight forward and we'll be certain that nothing will break. Or...

B) Deploy the app to the ROOT of tomcat webapp directory Perhaps this is not the best way of doing things if you want to host multiple webapps with a tomcat installation. But an admin might want to setup a separate tomcat install just to run Hudson/Jenkins anyway. Tomcat isn't capable of running different hosted apps as different users and that anyone setting up Hudson/Jenkins for CI will likely want to run it as a specific user ("build" for example). In this case, deploying in ROOT makes the most sense, which can be mapped to build.example.com very simply.

mt3
  • 310
  • 1
  • 3
  • 12
odie
  • 63
  • 1
  • 4

3 Answers3

3

Here is my nginx configuration file, even though I access Hudson through https (port 443):

The trick with reverse proxy (Apache or Nginx) is to always keep the same path:
If you want to redirect to a_long_and_complex_address/hudson, keep the /hudson part: myserver/hudson.
Don't try to redirect myserver to a_long_and_complex_address/hudson: all sorts of scripts and pictures, with absolute paths ('/') will broke.
Redirect myserver/hudson to a_long_and_complex_address/hudson.

Plus, you can redirect myserver/other_services to a_long_and_complex_address/other_services as well ;)

worker_processes  1;

error_log  logs/error.log  debug;
pid        logs/nginx.pid;

events {
    worker_connections  250;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout  150;
    #gzip  on;

    port_in_redirect   on;
    proxy_redirect     off;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

    server {
        listen       443;
        server_name  (some alias);
        # default max client body size was 1m! => Error code is 413
        # here: max 10Go
        client_max_body_size 10000m;

        ssl                  on;
        ssl_certificate      /home/xxx/.ssl/my.crt;
        ssl_certificate_key  /home/xxx/.ssl/my.key;
        ssl_session_timeout  5m;
        #ssl_protocols SSLv2 SSLv3 TLSv1; # NO: SSLv2 prohibited
        ssl_protocols  SSLv3 TLSv1;
        ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
        ssl_prefer_server_ciphers   on;

        rewrite ^/hudson$ https://myserver/hudson/ redirect;
        location /hudson/ {
          proxy_pass https://complex_address:8xx3/hudson/;
        }
    }
}
VonC
  • 2,653
  • 5
  • 29
  • 48
  • "The trick with reverse proxy (Apache or Nginx) is to always keep the same path." If that really is the case, then what I'm trying to do is exactly what the reverse proxy **can't** do. – odie Mar 29 '11 at 07:17
  • @odie: yup, I spent many hours trying to change to root path. Not a good idea. – VonC Mar 29 '11 at 07:38
  • Although it's not the answer that I wanted to find, at least now I won't be spending more time messing with this. Thanks! =) – odie Mar 29 '11 at 09:05
  • @odie: that is wise ;) Even if you did find a solution in your case, you have no way to know if something else will break later down the road. Keeping the same root path is really the safest path. – VonC Mar 29 '11 at 09:08
2

From the experiences I've had with nginx, the way you specify urls to proxy is a bit strange (because you have to specify the full url rather than just a directory). I believe your configuration could go something like this:

upstream 127.0.0.1:8080 {
  ip_hash;
  server 127.0.0.1:8080;
}

location ~* ^/(.*) {
  proxy_pass http://127.0.0.1:8080/hudson/$1;
  proxy_set_header  Host             $http_host;
  proxy_set_header  X-Real-IP        $remote_addr;
  proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
}

Let me know if it works for you.

Edit: To work around absolute requests you'll have to add in a second location definition that does

location ~* ^/hudson/(.*) {
  proxy_pass http://127.0.0.1:8080/hudson/$1;
  proxy_set_header  Host             $http_host;
  proxy_set_header  X-Real-IP        $remote_addr;
  proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
}

or add in the redirect that Alaz has recommended:

location /hudson {
  rewrite ^/hudson(.*)$ $1 redirect;
}

Unfortunately, unless you rewrite the source for hudson to retrieve resource files by relative path this is your only workaround, unless nginx has an http stream modifying plugin that I don't know of like mod_proxy_html for apache...

photoionized
  • 454
  • 2
  • 6
  • Thanks for the solution. It seemed like a reasonable thing to try so I went ahead. Every resource actually retrieves **something** now. But each resource file now gets the html content of the hudson dashboard page. I think the problem here is that even though the rewrite rule hides the "hudson" directory upstream, it doesn't remap the various resource file absolute paths such as "/hudson/style.css". – odie Mar 29 '11 at 09:53
  • Hmm! I guess that just means I need to have a few more directives in there to deal with those absolute paths! A working solution is just around the corner, it seems! – odie Mar 29 '11 at 09:55
  • Hmm... do you mean that hits to /hudson/style.css are giving you /hudson instead? The regex in the location declaration should catch the target url in the $1 variable which is then appended in the line that actually sets the proxy_pass. – photoionized Mar 29 '11 at 15:16
  • Just realized what you meant... the /hudson/style.css is an absolute path reference within the html page itself, so it's looking for /hudson/style.css on the main site, which is then proxying to 127.0.0.1:8080/hudson/hudson/style.css instead of 127.0.0.1:8080/hudson/style.css... that would be an issue, I edited my initial answer to include a workaround for that. – photoionized Mar 29 '11 at 15:20
1

Most probably Hudson returns an HTML page which refers to resources using absolute paths. And it calculates these paths using Web app's "contextPath" (equal to "/hudson" in your case).

Nginx just passes HTML to a browser, it does not alter page content. Hence your browser requests "/hudson/..." URLs.

Try to add one more location to your Nginx configuration (I didn't test it, you should catch the idea):

location /hudson {
  rewrite ^/hudson(.*)$ $1 break;
}

If you don't want your users see "/hudson" prefix, issue 301/302 redirect on such requests:

location /hudson {
  rewrite ^/hudson(.*)$ $1 redirect;
}

redirect results in 302; permanent results in 301

Alexander Azarov
  • 3,510
  • 20
  • 19
  • That's definitely the right direction. I tried some similar directives. `location ~* /jenkins/(.*) { proxy_pass http://127.0.0.1:8080/jenkins/$1; }` That lets users load the resource files correctly. there is still one last item though. When the user navigates to an absolute location such as /hudson/people, although the web browser is directed to the right page, the /hudson/ part of the url is still visible. Is there a way to change the url displayed? – odie Mar 29 '11 at 10:51
  • Issue an HTTP redirect. I've updated the answer – Alexander Azarov Mar 29 '11 at 13:11