Reverse Proxy from Scratch - NGINX on CentOS 7 (With SSL and LDAP Authentication!)

Reverse Proxy from Scratch - NGINX on CentOS 7 (With SSL and LDAP Authentication!)

· by CodyDe · Read in about 10 min · (2057 words) ·

Introduction

Like most, I get the majority of my lab work done in my home at 2am. I’ve got 1 external IP address and multiple services that I want to be able to hit externally. I hate doing random ports for everything and trying to remember what port I’ve mapped to what server. Then I need to Swiss cheese my pfSense firewall and port forward to all kinds of destinations. I’m just not into it.

Fortunately, nginx makes this pretty easy to remedy from a web server perspective. I know pfSense has various packages, for example Squid, that can do proxy functionality, but not everyone has the desire to build out a router (which if you haven’t, you should. Different blog post for another time…)

I see questions around how to reverse proxy using nginx pretty frequently. It’s talked about so often, and there are so many questions that come out on how to set it up. I’ll admit that I thought it was going to be a lot more complicated than it actually turned out to be! Once I sat down and actually worked on it - it took me less than an hour to figure out. Now I can rebuild my reverse proxy configuration in 15-20 minutes.

Without further adieu…

Lets get started!

Our initial system is named nginxext01.thehumblelab.com (clever name right?). We’ll start our work with this box. We’ve placed this box on an isolated VLAN on our environment since it’s going to be internet facing. We’ll build fine-grained firewall’s around this system to prevent any sort of malicious activity hitting our network. To be safe, we’ll do the same for the systems below it - which will live on a different VLAN.

Lets do some generic initial prep work -

sed -i /etc/selinux/config -r -e 's/^SELINUX=.*/SELINUX=disabled/g'   # Disabled SELinux
yum -y install epel-release
yum -y install git wget openssl-devel pcre-devel openldap-devel.x86_64
yum -y groupinstall "Development Tools"
yum -y update && systemctl reboot

After reboot, we’re going to use our newly installed GIT to clone down the LDAP authentication module for nginx. SSH back into your server.

cd /tmp/
git clone https://github.com/kvspb/nginx-auth-ldap.git

Normally we could just install nginx and be good to go. However, since we’re going to leverage LDAP authentication, we need compile the nginx installation ourselves. Fortunately this is easy enough!

wget http://nginx.org/download/nginx-1.9.9.tar.gz
tar -xvf nginx-1.9.9.tar.gz && cd nginx-1.9.9
useradd --shell /sbin/nologin nginx
./configure --user=nginx --group=nginx --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --with-http_stub_status_module --with-http_ssl_module --with-pcre --with-file-aio --with-http_realip_module --add-module=/tmp/nginx-auth-ldap --with-ipv6 --with-debug
make
make install

Lets take a look at the nginx.conf that’s been created -

vi /etc/nginx/nginx.conf

Couple of small changes to be made here - you can paste this in -

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log;

pid  /run/nginx.pid;

events {
    worker_connections  1024;
}

http {
  include  /etc/nginx/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  /var/log/nginx/access.log  main;

  sendfile  on;

  keepalive_timeout  65;

  include /etc/nginx/conf.d/*.conf;

  index  index.html index.htm;
}

Before we can start nginx we’ll want to create our nginx.service file so we can start it using systemctl. Go ahead and do the following…

vi /lib/systemd/system/nginx.service

Paste the content below to create the nginx.service file

[Unit]
Description=The nginx HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Next step, we’ll configure our proxy.conf. You can see in our nginx.conf file we tell nginx to include all .conf files in the conf.d directory. Once we have this proxy conf in place, nginx will load it along with everything else. This file is going to allow us to specify the host names to reverse proxy. For now we’re going to setup a basic one just to get the service “up” - we’ll add the proxy locations afterwards.

Security Break - The Importance of SSL

The initial iteration of this post had us creating the http server first, and then going back and creating it with SSL later. While the idea of “building foundational knowledge and layer on” makes sense in most situations, lets teach a valuable lesson here. That lesson is “Always SSL if possible, and ALWAYS SSL when you are handling authentication”. It’s simple, takes an extra 15 minutes to create a self signed certificate, and you wont be communicating credentials “in the clear”. You can buy a cert if you want, and add it in - but for this tutorial, we’re going to create a self signed cert. We’ll use a wildcard certificate so we can reuse this certificate on other servers if we wish. We already have OpenSSL installed, lets get started!

mkdir /etc/nginx/ssl
openssl genrsa -out /etc/nginx/ssl/rpwildcard.key 2048
openssl req -new -key /etc/nginx/ssl/rpwildcard.key \
            -out /etc/nginx/ssl/rpwildcard.csr

This will create our encryption key, and then start the CSR generation process. We’ll be prompted to fill in a bit of information - I’ve provided my examples below -

Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:California
Locality Name (eg, city) []:Sacramento
Organization Name (eg, company) [Internet Widgits Pty Ltd]:TheHumbleLab
Organizational Unit Name (eg, section) []:Homelab
Common Name (e.g. server FQDN or YOUR name) []:*.thehumblelab.com
Email Address []:codyde@thehumblelab.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:holytestpassB@tm@n
An optional company name []:TheHumbleLab

This will give us our CSR now. We’ll combine our CSR with the our key, sign the request using openssl, and that’ll give us our certificate!

openssl x509 -req -days 365 -in /etc/nginx/ssl/rpwildcard.csr \
             -signkey /etc/nginx/ssl/rpwildcard.key \
             -out /etc/nginx/ssl/rpwildcard.crt

So now we have our self signed .crt (certificate) file and our private key. We can return to our regularly scheduled programming where we will create our reverse proxy configuration, using port 443 (ssl) to encrypt our traffic.

Back to the Proxy

mkdir /etc/nginx/conf.d/
vi /etc/nginx/conf.d/proxy.conf 

Paste the content below to your new proxy.conf file

server {
       listen         80;
       server_name    testing.thehumblelab.com, 10.0.2.10;
       return         301 https://$server_name$request_uri;
}
server  {
  listen  443;
  server_name  testing.thehumblelab.com, 10.0.2.10;
  ssl on;
  ssl_certificate     /etc/nginx/ssl/rpwildcard.crt;
  ssl_certificate_key /etc/nginx/ssl/rpwildcard.key;
  location / {
    proxy_pass http://10.0.11.12:8080/guacamole/;</pre>
<pre>    } 
} ```

We also need to add the nginx user as a "nologin" user, and make them the owner of the conf.d directory

```bash
useradd --shell /sbin/nologin nginx
chown nginx /etc/nginx/conf.d

We’ve done a few things within the proxy.conf file. We’ve defined the listeners for both our http and https communication. We’ve also told nginx that if a request comes in over 80, to redirect it to https so we’re using secure communication. Security is good (see the “Importance of SSL” speech above).

Lets to ahead and start nginx to make sure nothing has gone wrong so far…

systemctl enable nginx.service
systemctl start nginx.service

And finally, since CentOS 7 enables firewalld by default, we need to add the ports to enable incoming http and https traffic (port 80/443)

sudo firewall-cmd --permanent --zone=public --add-service=http 
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload

If all things are well, we should be able to launch a browser and hit http://10.0.2.10 OR hit http://testing.thehumblelab.com (provided you’ve set up DNS). At this point it’ll just be the default “Welcome to nginx page”, but that’s great!

Next, we need something to reverse proxy “to”. I’m going to use a guacamole server for example, since that’s going to be a later blog post anyways.

Let’s reverse proxy now…

Fast forward a few steps. We now have a second server we’re working with. This will work for relatively any web server. I chose Guacamole (a very awesome HTML5 Based Remote Access client - go read about it) since it was something I want to throw up in a blog in the future.

We’re going to edit the proxy.conf file we created earlier, so lets do a…

vi /etc/nginx/conf.d/proxy.conf

And we’re going to add the following sub domain for functionality below the exiting server entry. You can keep the exiting one there - just make sure you change to a new sub domain.

server  {
  listen  80;
  server_name  testing.thehumblelab.com, 10.0.2.10;
  location / {
    proxy_pass http://10.0.11.12:8080/guacamole/;
  }
}

Then we’ll restart nginx again to consume the new proxy.conf file

systemctl restart nginx.service

And that should do it! Test out your new subdomain and make sure connectivity is successful!

Now, we secure it with LDAP

Oh yeah! That LDAP Security stuff!

We’ve built a solid foundation at this point. We’ve configured our nginx server so it’s listening over port 80. We’ve setup our reverse proxy and pointed it to our guacamole server. We’ve successfully verified that everything is working from a connectivity standpoint. Let’s get our security hats on and lock it down!

First we’ll want to edit our nginx.conf file to include the bits to enable LDAP. Note: I’ve obscured my personal details in this configuration just for safety’s sake.

Much of this nginx.conf will look similar to the first one we setup. This one has the added LDAP configurations in it, so pay close attention!

vi /etc/nginx/nginx.conf

Paste the content below to your nginx.conf file, replacing the previous content

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log;

pid  /run/nginx.pid;

events {
    worker_connections  1024;
}

http {
  include  /etc/nginx/mime.types;
  default_type  application/octet-stream;

      auth_ldap_cache_enabled on;
      auth_ldap_cache_expiration_time 10000;
      auth_ldap_cache_size 1000;
      ldap_server LDAP1 {
          url "ldaps://dc01.thehumblelab.com/dc=thehumblelab,dc=com?sAMAccountName?sub?";
          binddn "thehumblelab\\ldapbind";
          binddn_passwd "bindpass1122!!";
          connect_timeout 5s;
          bind_timeout 5s;
          request_timeout 5s;
          satisfy any;
          group_attribute member;
          group_attribute_is_dn on;
          require group "CN=Domain Admins,CN=Users,DC=thehumblelab,DC=com";
      }


  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  /var/log/nginx/access.log  main;

  sendfile  on;

  keepalive_timeout  65;

  include /etc/nginx/conf.d/*.conf;

  index  index.html index.htm;
}

You can see the new bits we added are located between the http { } section. Note that using Domain Admins is bad. I used it for examples sake, but would never use it in real practice.

Now that we have the LDAP module and configuration in place to reference our LDAP server within our nginx configuration, we need to add the directives for enabling the authentication to our reverse proxy.

vi /etc/nginx/conf.d/proxy.conf

Replace your existing proxy.conf’s 443 section with the content below, which will enable our LDAP authentication

server  {
  listen  443;
  ssl on;
  server_name  testing.thehumblelab.com, 10.0.22.10;
  ssl_certificate     /etc/nginx/ssl/rpwildcard.crt;
  ssl_certificate_key /etc/nginx/ssl/rpwildcard.key;
  auth_ldap "Thou shalt not pass";
  auth_ldap_servers LDAP1;
  location / {
    proxy_pass http://10.0.11.12:8080/guacamole/;
  }
}

And now, finally…

systemctl restart nginx.service

Open up your browser, fire up incognito mode to get rid of any pesky leftover caching, and hit your URL that you setup. You should be prompted for credentials. Test invalid ones first - then use your real ones. You wont need to add on your domain; in fact it just won’t work if you add it on. For example - do not use codytest@thehumblelab.com or thehumblelab\codytest. Use just codytest.

Add an Additional Subdomain

With this configuration you have a subdomain setup to point to your guacamole server when you hit the “testing.thehumblelab.com” URL. But what do you need to do if you want to add another reverse proxy destination and URL? Easy!

vi /etc/nginx/conf.d/proxy.conf

We’re going to add a new https server listner and associated content - you can paste this into the bottom of your proxy.conf and customize as needed. Note - we keep the SSL certificates the same when were adding in the new entry since we created a wildcard certificate earlier. If you have a new specific certificate, you would want to change the path for that as well.

server  {
  listen  443;
  ssl on;
  server_name  securestuff.thehumblelab.com, 10.0.2.10;
  ssl_certificate     /etc/nginx/ssl/rpwildcard.crt;
  ssl_certificate_key /etc/nginx/ssl/rpwildcard.key;
  auth_ldap "Thou shalt not pass";
  auth_ldap_servers LDAP1;
  location / {
    proxy_pass http://10.0.11.88/;
  }
}

Easy as that! Save the conf file, and restart nginx again…

systemctl restart nginx.service

And fire away! Make sure you have your DNS configured for the new location, so that it actually resolves something!

Your Next steps

  • Stand up a second node and load balance them
  • When you have your load balancing setup, configure your session persistence
  • For pfSense users - keep your reverse proxy in an isolated subnet and configure explicit firewall rule to the specific destinations.

Great Resources

https://github.com/kvspb/nginx-auth-ldap - The GitHub for the nginx-auth-ldap module https://deviantengineer.com/2015/05/nginx-reverseproxy-centos7/ - An amazing blogger who’s answered dozens of questions I’ve had around various technologies (NGINX and Guacamole included!) http://deezx.github.io/blog/2015/04/24/how-to-configure-nginx-with-ldap-authentication/ - Awesome Guide for enabling LDAP Authentication in NGINX. One of the first I found/used when I did my initial configuration https://serversforhackers.com/self-signed-ssl-certificates - Great guide for configuring self signed certificates

Ping me in the comments with any questions!