domzyoung
By:
domzyoung

How to setup nginx reverse proxy for sub domain

March 8, 2017 1.1k views
Nginx DigitalOcean Ubuntu 16.04

Hello, I'm trying to setup Nginx so I can have sub domains like

www.MySite.com -> Main website (Works correctly)
jenkins.MySite.com -> sub domain for Jenkins
gitlab.MySite.com -> sub domain for Gitlab

I've tried following various tutorials and I seem to have included everything required to make this work, but still to no avail.

I've followed this: https://www.digitalocean.com/community/tutorials/how-to-configure-nginx-with-ssl-as-a-reverse-proxy-for-jenkins
and various other sources online.

[Nginx Server Block]
I've edited my nginx.conf file, I've created a new nginx/sites-available conf file for Jenkins and symlinked it to sites-enabled.

This is my default/jenkins JENKINS_ARGS

JENKINS_ARGS="--webroot=/var/cache/jenkins/war --httpListenAddress=127.0.0.1 --httpPort=$HTTP_PORT -ajp13Port=$AJP_PORT"

This is an example of my jenkins server block in nginx

server {
    listen 80;
    return 301 https://$host$request_uri;
}

server {

    listen 443;
    server_name jenkins.MySite.com;

    #ssl_certificate           /etc/nginx/cert.crt;
    #ssl_certificate_key       /etc/nginx/cert.key;

    #ssl on;
    #ssl_session_cache  builtin:1000  shared:SSL:10m;
    #ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    #ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    #ssl_prefer_server_ciphers on;

    access_log            /var/log/nginx/jenkins/access.log;

    location / {

      proxy_set_header        Host $host;
      proxy_set_header        X-Real-IP $remote_addr;
      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header        X-Forwarded-Proto $scheme;

      # Fix the “It appears that your reverse proxy set up is broken" error.
      proxy_pass          http://127.0.0.1:8080;
      proxy_read_timeout  90;

      proxy_redirect      http://127.0.0.1:8080 https://jenkins.MySite.com;
    }
  }

I've also created an A record in DigitalOcean - Network
and also a CNAME

Much help would be appreciated.

Thanks

5 Answers

@domzyoung

The issue with the SSL portion of your NGINX server block is that SSL won't work without at least a self-signed or valid SSL Certificate. When you configure a server block to listen on port 443, a valid SSL certificate is required -- without it, the connection will fail.

In Chrome, you'll see something such as:

This site can’t provide a secure connection
ERRSSLPROTOCOL_ERROR

If you do have a valid SSL Certificate installed, whether via LetsEncrypt, self-signed, or through a SSL provider, please post the output of:

tail -20 /var/log/nginx/error.log

...

What you have right now, with the commented our SSL portion of your server block essentially gets read like this (as if the commented portions don't exist):

server {
    listen 80;
    server_name jenkins.MySite.com;

    return 301 https://$host$request_uri;
}

server {
    listen 443;
    server_name jenkins.MySite.com;

    location / {
        ...
        ...
        ...
    }
}

One note about your first server block (listening on port 80), just a quick fix, it should look like:

server {
    listen 80;
    server_name jenkins.MySite.com;

    return 301 https://$host$request_uri;
}

i.e. it's missing the server_name.

@jtittle Thanks for your reply.

Do I need the SSL stuff setup for this to work? at the moment I haven't bothered for now with any of the SSL stuff for my droplet setup, that's not to say I won't, but I was just aiming to get it working for now.

I've just made the below change, with MySite obviously changed to my domain.

server {
    listen 80;
    server_name jenkins.MySite.com;

    return 301 https://$host$request_uri;
}

However it's still saying "This site can’t be reached". Again www.MySite.com actually works fine, but jenkins.MySite.com still doesn't.

Is there any other logs or conf files I can show you which may help you verify if I've done everything correct?

  • @domzyoung

    The first two server blocks that you posted will work, only if you have a valid SSL Certificate. If you do not, they won't. What those two server blocks you posted do is:

    1). Accept a request on Port 80 and redirect it (using a 301 Redirect) to Port 443.
    2). Accepts Requests on Port 443.

    Essentially, those two server blocks are required if you want to use SSL (i.e. HTTPS). If you don't need/want SSL, you can use a modified version of your second server block to accept requests on the standard port 80 (i.e HTTP).

    I've modified your second server block to work on Port 80. This will work for standard requests but won't handle SSL requests. If you need SSL, then you'd need to fall back to the first two you were using and create an SSL Certificate and fill in that information where required.

    server {
        listen 80;
        server_name                 jenkins.MySite.com;
    
        access_log                  /var/log/nginx/jenkins/access.log;
        error_log                   /var/log/nginx/jenkins/error.log;
    
        location / {
            proxy_set_header        Host $host;
            proxy_set_header        X-Real-IP $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header        X-Forwarded-Proto $scheme;
            proxy_pass              http://127.0.0.1:8080;
            proxy_read_timeout      90;
            proxy_redirect          http://127.0.0.1:8080 http://jenkins.MySite.com;
        }
    }
    

    If you don't need SSL or want it, the above should be the only thing that's in your config file. You will then need to restart NGINX.

    • @jtittle

      Thanks, I've adjusted my jenkins server block file. Unfortunately It's still not working, I'm not entirely sure why...

      So I've added an A record for jenkins.MySite.com
      CNAME etc

      Added this to the Jenkins config

      JENKINS_ARGS="--webroot=/var/cache/jenkins/war --httpListenAddress=127.0.0.1 --httpPort=$HTTP_PORT -ajp13Port=$AJP_PORT"
      

      Added this to my nginx.conf file. It does usually work with just sites-enabled and sym link, but for testing I've just added them directly for now.

      include /etc/nginx/conf.d/*.conf;
          include /etc/nginx/sites-available/dominiczenyoung.conf;
          include /etc/nginx/sites-available/jenkins.conf;
          #include /etc/nginx/sites-available/gitlab.conf;
          include /etc/nginx/sites-enabled/*;
      

@domzyoung

What does the output of:

tail -20 /var/log/nginx/error.log

Show?

  • @jtittle

    Weirdly, it's just empty. That goes for the logs for jenkins too, running the above command.

    • @domzyoung

      When it comes to DNS, you mention that you've setup an A and CNAME, would that be:

      A           jenkins          DROPLET_IP
      CNAME       www.jenkins      jenkins.domain.com
      

      or something different? I ask as your server block is only setup for jenkins.domain.com, so if you're using the www variant (i.e CNAME), it most likely won't work properly.

      To remedy that, you can use:

      servername jenkins.domain.com www.jenkins.domain.com;
      

      That said, what's the output of nginx -t? This tests your configuration files to make sure they are indeed valid. This should eliminate potential issues with NGINX since no errors are being logged.

@jtittle

A                 jenkins.domain.com directs to          directs to  XXX.XX.XXX.XXX   3600
CNAME      *.jenkins.dominiczenyoung.com    is an alias of domain.com      4320
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
  • @domzyoung

    If that's the domain that Jenkins is supposed to be working on, it appears to be working on my end. I can access the site and I'm presented with a login box asking me to login to proceed any further.

    • @jtittle

      You're right.... chrome and my mac both I guess changed it to https.

      But just tried on internet explorer and it worked but on http.

      I guess I need to sort SSL out for https right?

      Thanks so much @jtittle

      • You should add SSL - it's free with Let's Encrypt.
        And the reason why Chrome still went to the https-site was because it caches the redirect until you restart the browser.

@domzyoung

No problem and yes, I would setup SSL since LetsEncrypt is indeed free and valid (i.e you won't get errors like you would with a self-signed SSL cert).

Setting up valid, working SSL that is actually secure does require a little more work though. The first thing you'd really want to do is create your dhparam file, which can take a while and it is resource intensive.

..

To start, install LetsEncrypt:

sudo apt-get -y letsencrypt

Once done, shutdown NGINX using service nginx stop. We can now run LetsEncrypt to generate your SSL Certificates.

letsencrypt certonly -d jenkins.dominiczenyoung.com -d www.jenkins.dominiczenyoung.com

LetsEncrypt does not support WildCard SSL Certificates, so you can't use:

*.dominiczenyoung.com
*.www.jenkins.dominiczenyoung.com
etc...

You'll have to use the ones above in the command above. Once you've entered your e-mail and accepted the TOS, it should output a path to your certificate, key, etc. Make a note of that path, you'll need it shortly.

...

Now we need to generate your dhparam file. To do that, we can create an SSL directory in /etc/nginx using:

mkdir -p /etc/nginx/ssl

Then generate the file using the command below. This can take anywhere from 5-10 minutes up to 20-30 depending on the size of your Droplet. Be patient and let it finish; you need this for your SSL configuration.

openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096

Once that file has been created, the minimum configuration you really want to run with would be:

add_header                      X-Frame-Options SAMEORIGIN;
add_header                      X-Content-Type-Options nosniff;
add_header                      X-XSS-Protection "1; mode=block";

ssl_certificate                 /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key             /etc/letsencrypt/live/yourdomain.com/privkey.pem;

ssl_prefer_server_ciphers       on;
ssl_session_cache               shared:SSL:50m;
ssl_session_timeout             5m;
ssl_dhparam                     /etc/nginx/ssl/dhparam.pem;

ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

In the above, you'd change the paths to ssl_certificate and ssl_certificate_key to match those that were created by LetsEncrypt. You should only need to change yourdomain.com to:

jenkins.dominiczenyoung.com

Thus resulting in:

/etc/letsencrypt/live/jenkins.dominiczenyoung.com/

The fullchain.pem and privkey.pem won't need to be changed as those are the same for all.

So what you'd end up with is an NGINX configuration file that looks something like this one:

server {
    listen 80;
    server_name                     jenkins.dominiczenyoung.com www.jenkins.dominiczenyoung.com;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name                     jenkins.dominiczenyoung.com www.jenkins.dominiczenyoung.com;

    add_header                      X-Frame-Options SAMEORIGIN;
    add_header                      X-Content-Type-Options nosniff;
    add_header                      X-XSS-Protection "1; mode=block";

    ssl_certificate                 /etc/letsencrypt/live/jenkins.dominiczenyoung.com/fullchain.pem;
    ssl_certificate_key             /etc/letsencrypt/live/jenkins.dominiczenyoung.com/privkey.pem;

    ssl_prefer_server_ciphers       on;
    ssl_session_cache               shared:SSL:50m;
    ssl_session_timeout             5m;
    ssl_dhparam                     /etc/nginx/ssl/dhparam.pem;

    ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

    access_log                      /var/log/nginx/jenkins/access.log;
    error_log                       /var/log/nginx/jenkins/error.log;

    location / {
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;
        proxy_pass              http://127.0.0.1:8080;
        proxy_read_timeout      90;
        proxy_redirect          http://127.0.0.1:8080 https://jenkins.dominiczenyoung.com;
    }
}

I've already configured the above server block for you, so once LetsEncrypt is ran, it should be copy, paste, restart and it should work without any issues.

  • @jtittle Thanks man you've explained things well, I've just gone through the above steps but since changing my jenkins.conf file over to the new one it's not working again.

    Is there anything else I would need to do?

Have another answer? Share your knowledge.