Full Reverse Proxy Instructions with Dynamic IP and HTTPS Encryption

June 2018 – Unfortunately these instructions no longer work on the latest version of Raspbian. I have found an updated set of instructions at the link below, so rather than reinventing the wheel, please follow these instructions instead:

Building a reverse proxy server with Nginx, Certbot, Raspbian Stretch Lite and Raspberry Pi 3

The Goal

  • Secure reverse proxy running on a Raspberry Pi at home (using the latest NGINX web-server), so that you don’t have to open multiple ports to the internet on your router.
  • Dynamic DNS URL for various web applications at home, e.g RPi Web Cam Interface – http://elinux.org/RPi-Cam-Web-Interface
  • Official HTTPS certification from letsencrypt.org for free
    • So that you don’t have to login with your password being sent in the clear!
    • Required because self-signed certs don’t work when trying to view recorded RPi Cam video on an iPhone/iPad
  • Push notifications sent to your phone when events occur such as motion detection
  • One Raspberry Pi for the proxy and separate Pis for multiple RPi Web Cam Interface cameras

To summarise how this will work once built, you will have a single external access point into your home network via the secure HTTPS port 443 using something like https://mydomain.com/mycamera/.

This method is called a Reverse Proxy and runs on a Raspberry Pi using the NGINX (Engine X) web server. This web server software is used on many official websites on the internet, and is therefore a secure barrier between the internet and your home network.

A file on the proxy Pi called /etc/nginx/services.conf is used to redirect custom URL sub-paths (such as “mycamera” in the example above) to another internal web server on whatever port you need, such as a Pi running the video surveillance dvr software RPi Web Cam Interface – http://elinux.org/RPi-Cam-Web-Interface

Pre-requisites

You will need to know how to do the following:

  • Write a Jessie image to an SD card
  • Configure a static IP (a link is included below)
  • Edit text files from the command line
  • Using an SSH client from your computer such as Putty to connect to the Pi
  • Know how to configure your router for port forwarding

Set up the Pi and Install NGINX:

  1. Create a new Jessie image on an SD card (use whichever method is required for your computer).
  2. Once booted into the GUI, goto Preferences -> Raspberry Pi Configuration
    1. Change Boot to CLI
    2. Untick Auto Login
    3. Give it a new Hostname (eg proxypi)
    4. Set your timezone
    5. Enable SSH
    6. Press OK and reboot
  3. Log into the Pi using default user/pass (pi/raspberry)
  4. Determine assigned IP by running “hostname -I”. (The proxypi really should be connected via ethernet rather than wifi, and DHCP should have assigned you an IP)
  5. SSH to the Pi using this IP address from your computer (use Putty on Windows)
  6. Change the default password
  7. passwd
  8. Set a static IP on the Pi using these instructions: https://www.modmypi.com/blog/how-to-give-your-raspberry-pi-a-static-ip-address-update
  9. Reboot and log back in using the Static IP
  10. Update the Pi
  11. sudo apt-get update
    sudo apt-get upgrade
    reboot
  12. Install the latest NGINX. Edit the sources.list and add the stretch branch of raspbian
    1. Edit the sources.list file
    2. sudo nano /etc/apt/sources.list
    3. Add the following to the last line:
    4. deb http://mirrordirector.raspbian.org/raspbian/ stretch main contrib non-free rpi
  13. Prevent all packages using stretch unless specified:
    1. Create a new file:
      sudo nano /etc/apt/preferences
    2. Add the following lines:
    3. Package: *
      Pin: release n=jessie
      Pin-Priority: 600
  14. Update:
    sudo apt-get update
  15. Install NGINX:
    sudo apt-get install -t stretch nginx

Set up a Dynamic DNS Hostname

I’m using and paying for Dyn’s Dynamic DNS server due to it’s reliability: http://dyn.com/remote-access/
There are plenty of free options out there though so follow their instructions to set up a hostname and updater client if you prefer.
The instructions below explain how to install a client on the proxypi for automatic IP updates to Dyn. This assumes you’ve already created your dynamic hostname within “My Services -> DynDNS Pro”. (ddclient):

  1. Install ddclient
    sudo apt-get install ddclient
  2. Answer the questions when prompted
    – Other DNS Provider
    – Dynamic DNS Server: members.dyndns.org
    – Protocol: dyndns2
    – Username/password
    – Network: web
    – URL name.
  3. Generate your conf file from the following link: https://account.dyn.com/tools/clientconfig.html
    1. Within the webpage, select your host and client (ddclient)
    2. Compare the generated conf file with what was configured during setup (/etc/ddclient.conf) and update accordingly
      daemon=600
      protocol=dyndns2
      use=web, web=checkip.dyndns.com, web-skip='IP Address'
      server=members.dyndns.org
      login=your_username
      password='your_password'
      your_hostname
    3. Test by running
      sudo ddclient -daemon=0 -verbose

 

Configuring HTTPS with https://letsencrypt.org/

  1. You should already have a webpage that you can browse to on your local network:
  2. http:///

    This should give you the welcome nginx welcome page.

  3. Configure your router to forward port 80 and 443 to your proxy raspberry Pi IP
  4. Add the Jessie backports to your sources.list by editing
  5. sudo nano /etc/apt/sources.list
  6. Then add the following line
  7. deb ftp://ftp.uk.debian.org/debian jessie-backports main
  8. Update:
    sudo apt-get update

    (ignore any GPG errors)

  9. Install Certbot
    sudo apt-get install certbot -t jessie-backports
  10. Run certbot
    sudo certbot certonly --webroot -w /var/www/html -d your_domainname

    It will ask you for your email address as part of the setup.

  11. You should see a Congratulations! message
  12. Now follow the next section “Configuring NGINX

 

Configuring NGINX Proxy:

  1. Create a new nginx configuration for reverse proxy by editing the following file:
    sudo nano /etc/nginx/sites-available/main
  2. Copy the text below into the “main”text file and edit the entries as required.
  3. access_log off;
    add_header Cache-Control public;
    server_tokens off;
    # HTTP 80
    server {
    	listen 80;
    	server_name _;
    	return 301 https://$request_uri?;
    }
    # HTTPS 443
    server  {
    	listen 443 ssl;
    	keepalive_timeout 70;
    	server_name ;
    	include /etc/nginx/ssl.conf;
    	include /etc/nginx/services.conf;
    }
  4. Create an ssl.conf file
  5. sudo nano /etc/nginx/ssl.conf
  6. Copy the following text into it
  7. ssl on;
    ssl_certificate /etc/letsencrypt/live//fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live//privkey.pem;
    
    ssl_session_cache shared:SSL:20m;
    ssl_session_timeout 180m;
    
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
    
    ssl_dhparam /etc/nginx/cert/dhparam.pem;
  8. Create the services.conf file.
  9. sudo nano /etc/nginx/services.conf
  10. Add as many entries as you need.
  11. location /picam {
    proxy_pass http://internal-rpi-cam-ip2/html;
    include /etc/nginx/proxy.conf;
    }
    location /picam2 {
    proxy_pass http://internal-rpi-cam-ip2/html;
    include /etc/nginx/proxy.conf;
    }
    location /couchpotato {
    proxy_pass http://internal-cp-ip:4001/couchpotato;
    include /etc/nginx/proxy.conf;
    }
    location /sonarr {
    proxy_pass http://internal-sonarr-ip:8989/sonarr;
    include /etc/nginx/proxy.conf;
    }
  12. Create a proxy.conf file:
  13. sudo nano /etc/nginx/proxy.conf
  14. Copy the following text into this file
  15. 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;
    client_max_body_size 10m;
    client_body_buffer_size 128k;
    proxy_connect_timeout 90;
    proxy_send_timeout 90;
    proxy_read_timeout 90;
    proxy_buffers 32 4k;
  16. Create a symbolic link to the main site
  17. sudo ln -s /etc/nginx/sites-available/main /etc/nginx/sites-enabled/main
  18. (Optional) Strengthen HTTPS key exchange by running the commands below (it takes around 5 mins to generate on a RPi 3 – go make dinner if on a Pi1 – Original Instructions found here: https://bjornjohansen.no/optimizing-https-nginx)
  19. sudo mkdir /etc/nginx/cert
    sudo openssl dhparam 2048 -out /etc/nginx/cert/dhparam.pem
  20. Test the config:
  21. sudo nginx -t
    nginx: [warn] conflicting server name "_" on 0.0.0.0:80, ignored
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
  22. Redirect Port 443 on your router to the proxypi
  23. Restart the nginx service
  24. sudo service nginx restart
  25. Test your site security from here: https://www.ssllabs.com/ssltest/index.html
    You should get an A rating! (if step 6 was also followed)
  26. Test that certificate renewal will work
  27. sudo certbot renew --dry-run
  28. Set up a cronjob to automate cert renewal. Start by opening the crontab file
  29. sudo crontab -e
  30. Enter the following line at the bottom of the file
  31. * 1 * * 1 sudo /usr/bin/certbot renew

That’s it!!

If you have installed RPi Cam on another Pi and the internal URL/IP matches what you configured in /etc/nginx/services.conf, then you should be able to browse using this URL:

https://your-domainname/picam/

PLEASE NOTE – You must end the URL with a trailing forward slash “/”, otherwise the redirect won’t work properly

I built a new proxy pi as I wrote these instructions, so it should all work… I’ll be happy to answer any questions you have though.

Optional Extras for RPi Cam

Please see my other blog posts to add extra features to your setup:

9 thoughts on “Full Reverse Proxy Instructions with Dynamic IP and HTTPS Encryption

    1. It should hopefully work ok – the nginx install used stretch anyway so that should work. I would ignore step 11 “Prevent all packages using stretch unless specified:”.

      Letsencrypt step 4 and 6 will need to see if jessie-backports can be changed to see if stretch-backports would work.

      The rest should be ok though. Let me know how it goes if you get chance please.

      Like

  1. Hi, thanks for the guide! I have followed all the steps (including the optional one to strengthen the HTTPS key exchange), but I’m getting an error when testing the config and attempting to restart nginx.

    When I run the nginx config check command, I receive the following error:
    [emerg] PEM_read_bio_DHparams(“/etc/nginx/cert/dhparam.pem”) failed (SSL: error:0906D06C:PEM routines:PEM_read_bio:no start line:Expecting: DH PARAMETERS)

    When attempting to restart nginx, I recieve the following error:
    Job for nginx.service failed because the control process exited with error code.

    When running the Strengthen HTTPS key exchange command, it executes, runs and completes and generates the DH PARAMETERS. I copied from —–BEGIN DH PARAMETERS—– to —–END DH PARAMETERS—– into /etc/nginx/cert/dhparam.pem ensuring that there was no white space and all characters were copied correctly.

    I have gone through the Configuring NGINX Proxy section multiple times to ensure the configuration is correct and all files, settings and symbolic links exist.

    I have a valid certificate from letsenrypt.

    I have tried removing the /etc/nginx/cert/dhparam.pem and rerun the nginx config check command, but receive the same error.

    Any help would be greatly appreciated as I am sure it’s something really simple!

    Like

Leave a comment