Nginx Proxy to HTTP and HTTPS

E

ehansen

Guest
I recently purchased some KVMs from a good provider of mine and decided to migrate from VPS to KVM. In doing this, I set up some LXC containers, installed Apache on them and copied over my data. That, was the easy part. Now I had to find a way to let the world view my websites.

From a previous time not too long ago (which I'll write an article up for here), I proxied web requests via Nginx to the containers' web servers. I could do this easily with HTTP, but as I also needed HTTPS this posed an issue. What better way to learn though than diving right into it in a production environment, right?! Lets go!

Okay, so out of the scope of this article, I will assume you:
  • Have LXC or some other operating system virtualization set up done (possibly can be done with OpenVZ too as they are basically the same)
  • Your firewall is configured properly to masquerade data in NAT's POSTROUTING chain
  • You have a valid SSL certificate (though this can be used with a self-signed cert as well)
  • You have Nginx installed and compiled with SSL support
  • Your container has Apache installed and listening on HTTPS (I used the same cert for both Nginx and Apache)
So, first get the IP address of the container with Apache installed. We'll assume the containers reside on a private network of 10.0.3.0/24, and your public IP is 1.3.3.7. ;) The IP of the box Nginx is on isn't really of importance. For argument's sake, Apache's IP will be on 10.0.3.42.


Create a new config file for the Apache server that proxies HTTP requests to 10.0.3.42:80 by doing this:

Code:
server {[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]listen 80;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]server_name domain.com;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]location / {[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]proxy_set_header X-Real-IP $remote_addr;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]proxy_set_header Host $host;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]proxy_pass http://10.0.3.42:80;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]}[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]}

Reload Nginx and try to access 1.3.3.7:80 (i.e.: http://1.3.3.7), does it work? If so, then continue on.

Before we set up the HTTPS side of things, there's something that should be stated about the SSL cert that Nginx uses. Unfortunately Nginx doesn't offer a way to use a Certificate Chain like Apache does. Instead what you need to combine the chain file with the actual certificate:


Code:
cat domain_cert.crt certificate_chain.crt > domain_chain.crt[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]

But, this tends to cause another issue. In your Nginx error log you'll probably notice something like this later on if you don't fix it now:


Code:
PEM routines:PEM_read_bio:bad end line error[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]

Annoyingly, this is because Nginx is picky in how it parses certificate files. You will need to manually go into the domain_chain.cert file and find something similar to this:

Code:
----END CERTIFICATE--------BEGIN CERTIFICATE-----[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]That's 5 "-" on both sides. After 5 "-" from "END CERTIFICATE" create a new line so it looks like this:[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]-----END CERTIFICATE-----[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]-----BEGIN CERTIFICATE-----

Alright, so now we're onto configuring Nginx to proxy SSL requests to Apache. From what I read before having this work is that this is not possible, and you need something like HAproxy or Stunnel. Apparently, either I got really lucky or that advise is a little too old now.

Now, another assumption, the certificate files will be in /etc/nginx/sites-available/. If not, change the addition to the above Nginx config file:

Code:
server {[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]    listen 443;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]    ssl on;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]    server_name domain.com;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]    ssl_certificate /etc/nginx/sites-available/domain_chain.crt;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]    ssl_certificate_key /etc/nginx/sites-available/domain.key;[/COLOR][/COLOR][/COLOR]
 
[COLOR=#000000][COLOR=#000000][COLOR=#000000]    location / {[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]        proxy_set_header X-Forwarded-Proto https;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]        proxy_set_header X-Real-IP $remote_addr;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]        proxy_set_header Host $host;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]        proxy_pass https://10.0.3.42:443;[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]    }[/COLOR][/COLOR][/COLOR]
[COLOR=#000000][COLOR=#000000][COLOR=#000000]}

Add that to the bottom of the first Nginx config you made, and restart Nginx again. You should now be able to access Apache both through HTTP and HTTPS.

Pretty cool, huh?

This really does open up a whole world of other possibilities as Nginx is a quite useful, small footprint proxy, even if it was originally made as a web server. Think of it as the Gentoo or Arch Linux of web servers, while Apache is like Ubuntu.
 

Members online


Latest posts

Top