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.
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♣
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
$ 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♣
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
$ 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 /Users/♣your-username♣ $ 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♣ </VirtualHost>
Save the file (CTRL-o, ENTER, CTRL-x)
$ 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 192.168.1.254.
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!