Introduction: Restrict Access to Raspberry Pi Web Server

This instructable is out of date. I updated it at: Two-way Authentication

My Home Automation projects allow control via a web server over the internet. While internet access is very useful, it is a security risk. Reduce this risk by requiring HTTPS, creating a client-side certificate, and restricting access to only those devices that have the certificate installed.

SSL facilitates encryption and trust. Trust allows a browser to validate the authenticity of a web site. For this instructable, client-side SSL certificates can be used to authenticate a cell phone or laptop to a web server.

A server/client certificate pair prevents unauthorized users from accessing your home servers. For example, one of my web servers allows the garage door to be opened and closed from a cell phone. For a cell phone to open the garage door, it must have the client-side certificate installed.

Step 1: Gather Parts

For this instructable, I am using:

  • Raspberry Pi 2 Model B running Raspbian and Apache with working WiFi
  • U-verse 2-Wire Gateway
  • MacBook Pro

Step 2: My Instructable Standards

The following conventions or standards are used in my instructables:

  • I don't want to share my passwords and other personal data on the internet. Text enclosed in clubs, such as, replace-this, should be replaced with your value. Of course, remove the clubs.
  • The instructable editor messes with http links converting them to short cuts. If you copy-and-paste a command with a link, then it may not work. So, text enclosed in hearts, such as, copy-and-paste-link, is a link. Copy and paste the command and copy-and-paste the link to use it.
  • I have difficulty using the instructable editor to store certain types of code, especially, HTML or CSS. So, code is stored in github with appropriate links.
  • Each instructable has an appendix, which may include:
    • References: Contains links to web pages used or consulted in writing the instructable
    • Updates: I use my instructables and periodically update them. My intent is to track my changes and explain why I made a change
    • Troubleshooting: Either during development or afterwards, I find issues or others may report problems and I attempt to document how to troubleshoot those issues
    • Automated install script: Some of my instructables contain a long list of commands. Since I use the instructable, it is much easier to write a script to implement all of the commands, rather than copy-and-paste each command. Also, the install script is supposed to fix common errors.
  • Most of my complex instructables are based on Raspberry Pi. Instead of including steps common to many instructables, I reference an instructable for, say, setting up Raspberry Pi and its Operating System.
  • For the most part, it doesn't matter which operating system you use. It's a personal preference. Almost all of my projects run headless (no GUI). So, I prefer DietPi.
  • In general, I develop the instructable as I am making the project. Once complete, I run through the instructions again to try and ensure I haven't forgotten anything.
  • I use a MacBook instead of a windows-based laptop.
  • Improvements in the instructable editor seem to cause new and inexplicable problems. The new format doesn't seem to be able to size images correctly, and just makes them into a mess. This instructable explains they need to be a 4:3 ratio with a canvas size of 600x450

Step 3: ​Enable SSL and Generate Certificate (use HTTPS)

Open a terminal window on the MacBook and login into the Raspberry Pi using its IP address.

$ ssh pi@raspberry-pi-ip

login: raspberry-pi-password

Generate a certificate key for the web server. You'll be asked to enter a pass phrase. This pass phrase can be anything, such as, cert-password. Re-enter ♣cert-password♣.

$ sudo openssl genrsa -des3 -out server.key 1024
$ sudo openssl rsa -in server.key -out server.key.insecure 

The next command generates the certificate, and asks several questions (only the fully qualified domain name or FQDN matters):

$ sudo openssl req -new -key server.key -out server.csr 
$ sudo openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Copy the certificate (cert) into the SSL directory

$ sudo cp server.crt /etc/ssl/certs 
$ sudo cp server.key /etc/ssl/private 

Enable SSL

$ sudo a2enmod ssl
$ sudo a2ensite default-ssl 

Restart apache (I am not sure which one to use. So, I did both)

$ sudo /etc/init.d/apache2 restart 
$ sudo service apache2 restart

Open a browser and in the url field enter:


A warning about the certificate not being from a trusted source will appear.

Press Continue. Make an exception and it should work.

Step 4: Creating Server/client Certificate Pair Using OpenSSL

Open a terminal window on the Mac and login to Raspberry Pi:

$ ssh pi@raspberry-pi-ip

login: ♣raspberry-pi-password

Remove keys made in previous step:

$ sudo rm server.key 
$ sudo rm server.crt 
$ sudo rm server.csr 
$ sudo rm server.key.insecure 

Generate Certificate Authority (CA)

Before creating server/client certificate, setup a self-signed Certificate Authority (CA), which can be used to sign the server/client certificates. Once created, the CA cert will act as the trusted authority for both your server and client certificates (or certs).

$ sudo openssl req -newkey rsa:4096 -keyform PEM -keyout ca.key -x509 -days 3650 -outform PEM -out ca.cer 

pass phrase = ♣cert-password♣ 

Generates: ca.cer, ca.key

Generate Apache server SSL key and certificate

Generate server.key:

$ sudo openssl genrsa -out server.key 4096 

Generate a certificate generation request.

$ sudo openssl req -new -key server.key -out server.req 

Use the certificate generation request and the CA cert to generate the server cert

$ sudo openssl x509 -req -in server.req -CA ca.cer -CAkey ca.key -set_serial 100 -extensions server -days 1460 -outform PEM -out server.cer 

Clean up – now that the cert has been created, we no longer need the request.

$ sudo rm server.req 

Install the server certificate in Apache

Copy the CA cert to a permanent place. We’ll need to specify our CA cert in Apache since it is a self generated CA and not one that is included in operating systems everywhere.

$ sudo cp ca.cer /etc/ssl/certs/ 

Copy the server cert and private key to permanent place.

$ sudo cp server.cer /etc/ssl/certs/server.crt 
$ sudo cp server.key /etc/ssl/private/server.key 

Activate the SSL module in Apache.

$ sudo a2enmod ssl  

Activate the SSL site in Apache

$ sudo a2ensite default-ssl

Disable the HTTP site

$ sudo a2dissite default 

Edit the config file for the SSL enabled site

$ sudo nano /etc/apache2/sites-enabled/000-default-ssl 

and add the lines below:

SSLCACertificateFile /etc/ssl/certs/ca.cer 
SSLCertificateFile /etc/ssl/certs/server.crt 
SSLCertificateKeyFile /etc/ssl/private/server.key 

Apply the config in Apache.

$ sudo service apache2 restart 

Right now if you visit your https site, you will get an SSL error similar to “SSL peer was unable to negotiate an acceptable set of security parameters.” That is good – it means your site won’t accept a connection unless your browser is using a trusted client cert. We’ll generate one now.

Generate a client SSL certificate

Generate a private key for the SSL client.

$ sudo openssl genrsa -out client.key 4096 

Use the client’s private key to generate a cert request.

$ sudo openssl req -new -key client.key -out client.req 

Issue the client certificate using the cert request and the CA cert/key.

$ sudo openssl x509 -req -in client.req -CA ca.cer -CAkey ca.key -set_serial 101 -extensions client -days 365 -outform PEM -out client.cer

Convert the client certificate and private key to pkcs#12 format for use by browsers.

$ sudo openssl pkcs12 -export -inkey client.key -in client.cer -out client.p12

Clean up – remove the client private key, client cert and client request files as the pkcs12 has everything needed.

$ sudo rm client.key client.cer client.req

Step 5: Add Client-side Certificate to Devices

Import the client.p12 file into your browser.

To copy client.p12 from the Raspberry Pi to a Mac, open a terminal window and enter the command:

$ pwd
$ scp pi@♣raspberry-pi-ip♣:client.p12 /Users/♣your-username♣ 

Double click the file to import into the operating system’s keystore that will be used by IE and Chrome.

For Firefox, open the Options → Advanced → Certificates → View Certificates → Your Certificates and import the certificate.

For Android phones, the browser must be Chrome.

Email client.p12 as an attachment to your device.

Open the email on the Android phone and save the attachment to downloads

Go to home screen and open Settings → Security → Credential Storage → Install from device storage → Open the client.p12 file

Enter pass phrase: ♣cert-password♣

For Apple phones, email the cert and double click on it, then follow the directions.

Email client.p12 and ca.cer as attachments

Step 6: ​Disable HTTP in Apache

$ sudo nano /etc/apache2/ports.conf

Comment out these lines:

NameVirtualHost *:80
Listen 80

So it looks like:

# NameVirtualHost *:80
# Listen 80
Listen 443
NameVirtualHost *:443
<VirtualHost *:443>
	ServerName ♣raspberry-pi-hostname♣
	Redirect permanent https://♣u-verse-gateway-ip♣

Save the file (CTRL-o, ENTER, CTRL-x)

Restart Apache

$ sudo service apache2 restart

Step 7: Disable HTTP on U-verse Gateway

HTTPS is secure and uses port 443. HTTP is insecure and uses port 80. My internet service provider's (ISP) U-Verse 2-Wire gateway provides a firewall.

Login to U-verse 2-Wire Gateway.

On MacBook, open browser and enter: ♣u-verse-gateway-ip-address♣. My gateway's IP is

Go to: Settings → Firewall → Applications, Pinholes and DMZ

And, select ♣raspberry-pi-hostname♣

Remove applications that allow port 80 through the web server

Keep or add port 443 on HTTPS Server

Now, visit your website with the browser where you imported the client certificate. You’ll likely be prompted for which client certificate to use – select it. Then you’ll be authenticated and allowed in!

Check each device.

If they all work, then you are done!

Step 8: Appendix: References