Restrict Access to OctoPi Utilizing SSL Client Certificates


#1

Disclaimer: First and foremost, I am not an internet security expert and I have no real idea if this procedure helps with securing your OctoPrint instance, but from what I can tell it doen't hurt. I do not take any responsibility for the security of your network and by following this procedure you release me of any liability associated with such. This process assumes that you are using an OctoPi image and only forwarding port 443 from your router to port 443 of your pi.

SSH to your pi and create a working folder and change into it.

mkdir ssl
cd ssl

Create your root certificate authority for signing other certificate requests for both the server and users. Follow the prompts to enter passphrase and verify passphrase. This passphrase will be used any time you sign a new certificate request so don't forget it.

openssl genrsa -des3 -out OctoPrintCA.key 4096
openssl req -x509 -new -nodes -key OctoPrintCA.key -sha256 -days 1825 -out OctoPrintCA.crt

Fill in whatever you want for the requested information, but for the Common Name you want to use something that you'll be able to recognize as the certificate authority. In this example I used OctoPrint CA for the common name and "blanked" everything else out using a "." for each prompt.

Country Name (2 letter code) [AU]:.
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:.
Organizational Unit Name (eg, section) []:.
Common Name (e.g. server FQDN or YOUR name) []:OctoPrint CA
Email Address []:.

Now we'll create a server certificate that is signed by our newly created root CA. Ideally you'd have a registered domain name pointing to your router's public ip address. This can be a freely available dynamic dns domain name, and is what I actually use. If you don't have a registered domain name then you will have to contend with name mismatch errors in your browser. If you are just using a static public ip address you can also just enter that for common name as well.

openssl genrsa -out mydomain.com.key 2048
openssl req -new -key mydomain.com.key -out mydomain.com.csr
openssl x509 -req -in mydomain.com.csr -CA OctoPrintCA.crt -CAkey OctoPrintCA.key -CAcreateserial -out mydomain.com.crt -days 500 -sha256
cat mydomain.com.crt mydomain.com.key > mydomain.com.pem

Create an individual user's certificate that will be used to "authorize" a device to connect to haproxy once we lock that down. It will be signed by our new CA we created above, and the process is the same as creating the server certificate. The important part here is to enter the Common Name as the user you want to identify as. When exporting the p12 format file make sure to remember the password as that will be needed any time you import the file into a device that will be used to connect.

openssl genrsa -out username1.key 2048
openssl req -new -key username1.key -out username1.csr
openssl x509 -req -in username1.csr -CA OctoPrintCA.crt -CAkey OctoPrintCA.key -CAcreateserial -out username1.crt -days 499 -sha256
openssl pkcs12 -export -in username1.crt -inkey username1.key -out username1.p12

Now we'll update the haproxy config to bind port 443 to our new server cert after creating a backup of the original file.

cd /etc/haproxy
sudo cp haproxy.cfg haproxy.cfg.bak
sudo nano haproxy.cfg

Your specific configuration in haproxy.cfg may be different but the changes you will be making are in the global and frontend public sections. For the global section we just need to add a line that tell haproxy to use a higher bit length for the ssl connection. At the end of the global section just add the line tune.ssl.default-dh-param 2048.

Now in the frontend public section find the line like this bind :::443 v4v6 ssl crt /etc/ssl/snakeoil.pem and change it to bind :::443 v4v6 ssl crt /home/pi/ssl/mydomain.com.pem ca-file /home/pi/ssl/OctoPrintCA.crt verify required. This next change is optional but recommended; instruct haproxy to redirect standard http port 80 to use https port 443. Find the line option forwardfor except 127.0.0.1 and just below it enter redirect scheme https code 301 if !{ ssl_fc }.

Your final haproxy.cfg file should look something like the one below. This one started as the default config that came with OctoPi 0.15.1 with the above modifications completed.

global
        maxconn 4096
        user haproxy
        group haproxy
        log 127.0.0.1 local1 debug
		tune.ssl.default-dh-param 2048

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        retries 3
        option redispatch
        option http-server-close
        option forwardfor
        maxconn 2000
        timeout connect 5s
        timeout client  15min
        timeout server  15min

frontend public
        bind :::80 v4v6
        bind :::443 v4v6 ssl crt /home/pi/ssl/mydomain.com.pem ca-file /home/pi/OctoPrintCA.crt verify required
        option forwardfor except 127.0.0.1
        redirect scheme https code 301 if !{ ssl_fc }
        use_backend webcam if { path_beg /webcam/ }
		use_backend gstreamer if { path_beg /gstreamer/ }
        default_backend octoprint

backend octoprint
        acl needs_scheme req.hdr_cnt(X-Scheme) eq 0

        reqrep ^([^\ :]*)\ /(.*) \1\ /\2
        reqadd X-Scheme:\ https if needs_scheme { ssl_fc }
        reqadd X-Scheme:\ http if needs_scheme !{ ssl_fc }
        option forwardfor
        server octoprint1 127.0.0.1:5000
        errorfile 503 /etc/haproxy/errors/503-no-octoprint.http

backend webcam
        reqrep ^([^\ :]*)\ /webcam/(.*)     \1\ /\2
        server webcam1  127.0.0.1:8080
        errorfile 503 /etc/haproxy/errors/503-no-webcam.http

At this point save the file by pressing ctrl+x followed by y for yes and enter to overwrite the previous file. Restart haproxy sudo service haproxy restart and if we attempt to go to our octoprint https address you will get the below error. This is good news, because this is what we want. If you don't have a user certificate signed by our Certificate Authority it will not allow the connection.

image

You will now need to transfer the username1.p12 file from the pi to any device that you want to grant access to OctoPrint. This process varies from device to device, but for windows you just double-click the file and run through the certificate import wizard selecting all the default options and entering the password used when exporting the p12 file previously. If you want to avoid getting a trusted root certificate authority error you will also need to import the file OctoPrintCA.crt, but during the import wizard choose the option to add it to a specific store, press browse and choose the Trusted Root Certification Authorities.

If all goes well now when you attempt to go to the https address of your OctoPi you'll get right in to the standard web interface and be able to login, etc.

You may get certificate errors where you can click the Advanced link (in chrome) and choose the option to proceed to the site. This typically will happen because of a common name/url mismatch or an untrusted root certificate authority. This doesn't necessarily mean the connection isn't secure, but means the browser is not seeing what it expects to see related to the connection and the certificate(s) used to establish the connection.

Some common fixes for these errors are importing the CA certificate as mentioned previously, update your hosts file to point the common name used to generate the server certificate to your internal ip address (if you used a dns name for this) and access by the name instead of ip.


Access your OctoPrint remotely
Https using Lets Encrypt
#2

What version of Octopi/Raspbian are you using?
After making the changes and setting up everything as shown here, haproxy is throwing a ton of errors about v4 isn't a recognized command, :::443 not a valid address etc.

Linux octopi 4.14.67-v7+ #1139 SMP Wed Aug 29 15:17:05 BST 2018 armv7l GNU/Linux

HA-Proxy version 1.7.5-2 2017/05/27
Copyright 2000-2017 Willy Tarreau willy@haproxy.org

Thanks


#3

I have this same setup on both an octopi 0.14 and 0.15.1 installs.


#4

443 is the port address being used. Double check that your router and the Octo are the same address. 443 is HTTP with SSL, you may be using 80, HTTP, or 8080, HTTP proxy.


#5

I started having other network issues as well, and popped in a fresh build of Jessie. everything worked as it should, so I re-downloaded Octopi, reflashed, configured and all is good. Somehow something got corrupted. Ne SD card (San Disk, and not a clone)