Securing your Raspberry PI with SSL and Simple Authentication

Many of the home automation projects that I’ll be talking about on this blog will be controllable with a web browser. Most of the projects will, in fact, be controlled with simple HTTP GET requests. I do this to make it as easy as possible to control with as many methods as possible. In future postings, you will see that I will be able to control things with actions as simple as visiting a book mark, executing a Tasker task, accessing an Android Applications, or even using a nice Ajax’y web dashboard. The problem with using this internet accessible approach is that is is inherently insecure. In our first project, we connected the garage door to the internet. It would be unfortunate if someone were to happen upon the proper GET URL and open your garage door. I’m sure you could imagine the havoc that a bad guy could cause with that kind of power. Now, let’s learn about some steps you can take to secure this and future projects.

Prerequisites

These instructions assume that you have a Raspberry Pi with the Raspbian, Nginx, Flask, and uWSGI installed and working. If you don’t already have that, be sure to follow the step-by-step instructions in the Preparing your Raspberry Pi Envoronment post. That posting walks you through the setup process from scratch.

Internal network

You were advised to only do the garage door opener project with the Raspberry Pi on an internal secure network. This provides a minimal amount of security but wireless networks are insecure and anyone that is on your internal network could theoretically access your home controller. Making it easy and accessible with HTTP GET commands also makes it easy for accidents and attacks to happen.

Considering all of that, and if you want to access your controls from external networks, you should take some additional steps.

Using SSL

SSL, in the context that we will be using it, is a method of securing communication between your client (cellphone) and the server (Home Automation Controller). This is important because we don’t want anyone that may be watching your internet traffic to see the exchange that we have with the controller. See the video below for a more thorough explanation of SSL. If someone can see the traffic, they would know how to replicate the GET request. Encrypting your traffic is an important first step to securing your Home Automation Controller.

Normally, I would say that we could use a self-signed SSL certificate to accomplish this. After all, we are only trying to encrypt our traffic. Unfortunately, it has been my experience that cell phones and, specifically, Tasker do not cooperate with self signed certificates so we have to get a full fledged SSL certificate. Fortunately, I’ve found a free way that will suit this project just fine.

Get a domain name and a Dynamic DNS Service.

I already pay for my own domain names and DNS services so I did not use a free service but after a short search I found a free and simple alternative for the penny pinchers out there. http://duckdns.org. There are other free services available but just like the cut of their  jib.

Get and prepare your SSL Certificate

Finding a free and reliable SSL certificate provider is not easy. I did come across a website that lists a few alternatives. The one I selected was StartSSL (https://www.startssl.com). They will provide you with a free SSL certificate that will work for our purposes, but I would not recommend the free version for any type of e-commerce website. We only need to prevent eavesdroppers so StartSSl’s free certificate will work well for this project. After signing up for an account and completing their authentication process, you will be provided with a private key and a certificate to use for your website.

Install your SSL Certificate in your Flask App

In order to use your new certificate you will need to complete a couple of steps to install it to your Nginx server. First, you will need to copy the private key and the certificate to your Raspberry Pi. I recommend installing it all to a folder in your /etc directory.

raspberrypi$ sudo -i
raspberrypi# cd /etc/ssl/certs/
raspberrypi# mkdir flask
raspberrypi# cd flask

Now download the ssl.crt and ssl.key from StartSSl and place them in that newly created directory. Then decrypt and change the permissions of the private key.

raspberrypi# openssl rsa -in ssl.key -out private.key
raspberrypi# chmod 600 private.key

Let’s prepare all of the files for use with Nginx.

raspberrypi# wget http://www.startssl.com/certs/ca.pem
raspberrypi# wget http://www.startssl.com/certs/sub.class1.server.ca.pem
raspberrypi# cat ssl.crt sub.class1.server.ca.pem ca.pem > ssl-unified.crt

Now we can edit our Nginx site to enable SSL. Edit the /etc/nginx/sites-enabled/default file.

server {
        #listen   80; ## listen for ipv4; this line is default and implied
        #listen   [::]:80 default_server ipv6only=on; ## listen for ipv6

        listen 443;
        # Use the domain you picked from duckdns.org here
        server_name yourdomain.duckdns.org localhost;
        ssl on;
        ssl_certificate /etc/ssl/certs/flask/ssl-unified.crt;
        ssl_certificate_key /etc/ssl/certs/flask/private.key;
        ssl_session_timeout 5m;
        ssl_protocols SSLv3 TLSv1;
        ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
        ssl_prefer_server_ciphers on;

Now let’s do a little “Hello World” test. Create this new file and edit it. You will already have this file if you followed the instructions in the Preparing your Raspberry Pi Envoronment post.

raspberrypi# mkdir /var/www/
raspberrypi# vi /var/www/hello.py

Now paste the following sample code:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">Edit /etc/uwsgi/apps-enabled/hello.ini to start the app (if you haven't edited it from the </span><a style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;" title="Preparing your Raspberry Pi Environment" href="http://www.pratermade.com/2014/08/setting-up-your-raspberry-pi-environment/">Preparing your Raspberry Pi Environment</a><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;"> post.)</span>

Now restart your Nginx and uwsgi servers:

raspberrypi# service nginx restart
raspberrypi# service uwsgi restart

And test it by going to https://yourdomain.duckdns.org in a web browser. You will have to configure your router to forward requests to port 443 to your Raspberry Pi if you are accessing this from an outside network. If all went well, you should see a webpage with “Hello World!” printed on it. Look for the SSL lock indicator on your browser to ensure the SSL session is functioning correctly.

 Authentication

Let’s write a simple authentication process.Now that our application is communicating securely over SSL, we can implement some sort of key for authentication purposes. The easiest way to do this is with a shared key. Since this is for personal use, this should prove to be secure enough. I’ve commented all of the relevant changes in the example.

from flask import Flask
from flask import render_template
# import request from flask to handle the HTTP GET
# parameters we will be using for authentication
from flask import request
app = Flask(__name__)
# Select and set a shared key. This should be long and random.
# This will be used by your client to prove that you are
# allowed to access the resource.
sharedkey = "34BXVHFNSczi0VOvHz*H"

# Note that we are going to use a different webpage path for this version by specifying a different app.route().
@app.route('/hello/')
def hello_world():
# This next line makes sure that you included the correct
# key in the get request.
# The URL that you are going to use now will look something like:
# https://yourdomain.dynduck.org/hello?key=34BXVHFNSczi0VOvHz*H
 secret = request.args.get('key','');
 if secret == sharedkey:
 return "<html><body><h1 align=\"center\">Congratulations!</h1><p style=\"float:center;\"> You've successfully installed a SSL Certificate</p>"
 else:
 # Returns this if the key is wrong or missing.
 return '<h1 align="center">Incorrect Key</h1>'

Although not the easiest project, it is an important one that should protect your server from foreign invaders and keep everything relatively secure. Again, if you would like to access this from an external network make sure to forward port 443 to your Pi in your router. The method for doing this is different for every router so I will refer you to your router’s instructions for this. This method provides you with a very basic level of security but it should be adequate for the projects on this website. There are much stronger ways of securing things but frankly, this is the method that I use because sometimes simple is better. If you see an inherent security flaw with my process, be sure to leave me a message so I can address the issue. I’d hate to lead anyone down an insecure road. Close

 

About

I’m just your everyday average home automation hobbyist. I’ve been into technology since I was a little guy and I am currently an IT director at a small community college in rural Oklahoma. While working and playing I’ve had a great deal of fun getting to play with a very wide variety of technology, and I love to play with technology!

Posted in Operating System Tagged with:

Social