What's new
VORON Design

Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members!

Protect your klipper printer (mainsail), password, ssl, firewall rules

Indeed

Active member
This guide is divided in two parts due to the 10000 character limit.

The methods listed below work on my printer but I don't know if they will work for you.
My machine has an Intel NUC with ubuntu 22.04.
Mainsail, klipper and friends were installed with kiauh.
I have not tried this with a raspberry pi but the setup should be similar, if not identical.


Level 1, trusted home network
First we'll look at a simple way to limit access to your printer running Mainsail behind nginx as usual, using "basic auth".
"basic auth" is quite insecure if used by itself only, credentials are sent in clear text over the network but this may be good enough depending on what your goal is.

Maybe the printer is on your home network where you trust all users and all you want to do is to stop a family member, who has no malicious intent, from browsing the Mainsail printer page and make accidental changes.
Who knows, fat fingers on a phone or curiosity might result in an emergency stop just minutes before your 15 hour print is finished, a restart of previous print, a config file edit, or worse.

If protection from accidental changes is all you need then the "basic auth" password protection may be enough.
See section PASSWORD below.


Level 2, workplace network
Next level is if your printer is on a network where you kind of trust all users, but perhaps not with your life or wallet. Such as an office network or a corporate network.
Then you can try and improve things by adding https (ssl) and a couple of local firewall (iptables) rules in addition to the password protection (basic auth).
See sections HTTPS and FIREWALL below. Or rather, in part 2 of this guide.


Level 3, internet
If you intend to connect to your printer over the internet, then don't trust this guide.
Go with some other solution instead, such as a vpn.



PASSWORD​


First we'll create a password file with "htpasswd",
then add a couple of lines to the nginx config and make nginx use the "basic auth" method,
and finally restart nginx.

htpasswd is found in package apache2-utils, install it if needed:
Code:
sudo apt install apache2-utils
Or you can instead use apt-get if you like:
Code:
sudo apt-get install apache2-utils

Create a file with a user+password entry. Replace USER_NAME with your choice of username. You will be prompted for a password.
Code:
sudo htpasswd -c /etc/nginx/0-passwords.txt  USER_NAME

Make a backup of the nginx mainsail config. Just in case.
Code:
sudo cp /etc/nginx/sites-available/mainsail  /etc/nginx/sites-available/mainsail.orig

Edit file /etc/nginx/sites-available/mainsail
Code:
sudo nano /etc/nginx/sites-available/mainsail

and add these two lines somewhere under the "server" section.
Code:
    auth_basic            "<here you can write a message if you like>";
    auth_basic_user_file  /etc/nginx/0-passwords.txt;


Example config from my machine
Code:
# /etc/nginx/sites-available/mainsail
server {
    listen 80;
    auth_basic            "Go away";
    auth_basic_user_file  /etc/nginx/0-passwords.txt;


Save, then restart nginx. One of these will probably work. Both work on my machine.
Code:
sudo service nginx restart
sudo systemctl restart nginx


Notes
The password file doesn't have to be "/etc/nginx/0-passwords.txt", it's just what I called it.
I like to prefix my files with "0-" so there's no doubt that the file was created by me and didn't come with the software package.
You can call it anything and place it in any directory. Such as "/home/pi/htaccess".

If it didn't work you can revert with
Code:
sudo cp /etc/nginx/sites-available/mainsail.orig  /etc/nginx/sites-available/mainsail
and then restart nginx again.

It should also be noted that if the printer is on a wireless network (where by design all traffic is broadcasted to all clients more or less), anyone using a sniffer such as wireshark, tcpdump or similar will be able to see the user credentials in clear text.
But this method may still be good enough for use at home where you know and trust all users.


Continued in part 2
 
Last edited:
This guide is in two parts due to the 10000 character limit.

HTTPS (ssl)​


This will add encryption to the connection, making it a little harder to intercept and read the credentials sent in clear text by the "basic auth" method.

To keep it simple I did everything below as root.
Code:
sudo su

Create a self-signed ssl certificate
A self signed certificate is good enough for this purpose. In our case the only practical difference from a "proper" certificate made by a well known "Certificate Authority" (such as letsencrypt, verisign, thawte) is that the browser will show an annoying warning that our shiny new certificate can't be trusted.
But we know it can be trusted since we just made it ourselves.

You'll need openssl for this. You probably have it already but if you don't then:
Code:
apt install openssl

Choose a nice location and name for the cert and key file.
I selected /etc/nginx/0-snakeoil.* but it can be anything.

Now make a self signed certificate, valid for at least 10 years so we don't have to replace it too often
There will be a bunch of questions such as Country Name, Email and whatnot.
Just accept whatever the defaults are and press [enter], it doesn't matter what the replies are.
Code:
openssl req -x509 -nodes -days 36500 -newkey rsa:2048  -keyout /etc/nginx/0-snakeoil.key  -out /etc/nginx/0-snakeoil.crt


Now it's time to edit file /etc/nginx/sites-available/mainsail again. See section PASSWORD above. (in part 1)
Perhaps also make a new backup of the file before you start editing, so you can roll back if things don't go as planned.


Under the "server" section, comment out the "listen 80;" line.

Add these three lines
Code:
    listen 443 ssl default_server;
    ssl_certificate      /etc/nginx/0-snakeoil.crt;
    ssl_certificate_key  /etc/nginx/0-snakeoil.key;

I saw somewhere that it's a good idea to comment out all "gzip" directives under "server" when ssl is enabled.
Not sure why. I assume it's to ease the load on the cpu.
In any case, I commented out all "gzip" directives for good measure.

Example config from my machine, including the "auth_basic" config from section PASSWORD above (in part 1)
Code:
# /etc/nginx/sites-available/mainsail
server {
    auth_basic           "go away";
    auth_basic_user_file /etc/nginx/0-passwords.txt;
    #listen 80;
    listen 443 ssl default_server;
    ssl_certificate      /etc/nginx/0-snakeoil.crt;
    ssl_certificate_key  /etc/nginx/0-snakeoil.key;

    access_log /var/log/nginx/mainsail-access.log;
    error_log /var/log/nginx/mainsail-error.log;

    # disable this section on smaller hardware like a pi zero
    #gzip on;
    #gzip_vary on;
    #gzip_proxied any;
    #gzip_proxied expired no-cache no-store private auth;
    #gzip_comp_level 4;
    #gzip_buffers 16 8k;
    #gzip_http_version 1.1;
    #gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml;


Restart nginx, either "service nginx restart" or "systemctl restart nginx"

You should now be able to access your printer using https://

I had to clear my browser cache to get it going, maybe you have to do the same.
Until I cleared the cache all I got was error messages about moonraker being unavailable, maybe due to a cached script or some such.

Since we commented out "listen 80;" you won't be able to access your printer using http anymore. Only https.



FIREWALL​


This will add a couple of firewall rules and stop all (more or less) incoming connection attempts but ssh and https.

I used iptables, there are probably newer and better ways to do this but iptables is what I'm familiar with, so iptables it is.

To keep it simple I did everything below as root.
Code:
sudo su

You'll want iptables-persistent for this. It will enable saved firewall rules to become active at boot time.
Code:
apt install iptables-persistent

You will be asked if you want to save current rules, answer yes to both questions (ipv4 and ipv6) even if you don't have any rules to save yet.


create a script with this content, maybe call it firewall_clear.sh:
Code:
#!/bin/sh
iptables -P INPUT   ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT  ACCEPT
iptables -t nat -P PREROUTING  ACCEPT
iptables -t nat -P POSTROUTING ACCEPT
iptables -t nat -P OUTPUT      ACCEPT
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X

make it executable
Code:
chmod 755 firewall_clear.sh

It can be used to remove all firewall rules in case there is a problem later on.


Now create a script "firewall_set.sh" or similar, with the firewall rules we will attempt to use.
None of the lines with "icmp" are really necessary, you can omit them if you like.
They are nice to have though, in case someone tries to get your host to honor malicious redirects or similar.
Code:
#!/bin/sh
PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
#------------------------------------------------------------------------------
#***** Remove all rules, set input and forward policy to DROP
iptables -P INPUT   DROP
iptables -P FORWARD DROP
iptables -P OUTPUT  ACCEPT
iptables -t nat -P PREROUTING  ACCEPT
iptables -t nat -P POSTROUTING ACCEPT
iptables -t nat -P OUTPUT      ACCEPT
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X

#------------------------------------------------------------------------------
#***** Declare additional chain names
iptables -N icmp_packets

#***** allow all to 127.0.0.1
iptables -I INPUT 1 -i lo -j ACCEPT

#***** drop invalid packets
iptables -A FORWARD -m state --state INVALID -j DROP

#***** allow all established and related traffic
iptables -A INPUT   -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

#***** allow ssh
iptables -A INPUT  -p tcp  --dport 22    -j ACCEPT

#***** allow https (mainsail)
iptables -A INPUT  -p tcp  --dport 443 -j ACCEPT

#***** catch icmp packets and send them to chain icmp_packets
iptables -A INPUT  -p icmp -j icmp_packets

#***** Drop packets that made it all the way down here.
#***** There shouldn't be any, but you never know...
iptables -A INPUT   -j DROP
iptables -A FORWARD -j DROP

#------------------------------------------------------------------------------
#***** chain icmp, allow some icmp packets
#***** 0=Echo Reply  3=Destination Unreachable  5=Redirect  8=Echo  11=Time Exceeded
iptables -A icmp_packets -p icmp -s 0/0 --icmp-type 0  -j ACCEPT
iptables -A icmp_packets -p icmp -s 0/0 --icmp-type 3  -j ACCEPT
iptables -A icmp_packets -p icmp -s 0/0 --icmp-type 8  -j ACCEPT
iptables -A icmp_packets -p icmp -s 0/0 --icmp-type 11 -j ACCEPT
iptables -A icmp_packets -p icmp -s 0/0                -j DROP
#------------------------------------------------------------------------------
echo "*** rc.firewall was executed on $(uname -n) ***"

make it executable
Code:
chmod 755 firewall_set.sh

Now run it
Code:
./firewall_set.sh

Before anything else try to open a new ssh session to your printer, without closing your current ssh session.
If you can't then run the firewall clear script
Code:
./firewall_clear.sh

If you are really unlucky you may get locked out entirely and can no longer access the printer at all.
Not to worry!
We have not yet saved the firewall rules, so all you have to do is reboot.
Or power cycle, since you probably can't even reboot anymore...

Then have a closer look at "firewall_set.sh" and see if you can find any errors like a missing space, a misplaced " or something.
This line is of particular interest, it opens up for ssh connctions
Code:
iptables -A INPUT  -p tcp  --dport 22    -j ACCEPT

Repeat until you are able to apply the rules and then open a new ssh connection.



If it did work and you could open a new ssh connection then you can go ahead and save the firewall rules so they will be run at boot time
Code:
iptables-save > /etc/iptables/rules.v4

Maybe verify that the rules did get saved with
Code:
cat /etc/iptables/rules.v4

Then reboot and verify that the rules have been applied
Code:
iptables -L


That should be it.
The printer is now firewalled, password protected and https enabled.
At least mine is.
I hope I didn't forget anything...
 
Last edited:
Very helpful, thanks! I was a bit nervous when I found Mainsail/Fluidd had not access control, leaving the printer interface wide-open on my local LAN on port 80. I'm used to Octoprint (and may go back to using it at some point), which does have access control. Using your quick guide here, I added basic_auth and ssl to nginx in just a few minutes and feel much better about that particular risk. Now back to printing/tuning...
 
for remote use, I'm a huge fan of Tailscale! (which is now free for up to 3 users/100 devices, up from 1/20)
 
Instead of just turning off http it might be a better idea to just redirect it to https

Your config would look like this:
Code:
server {
    listen 80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}
server {
    auth_basic           "go away";
    auth_basic_user_file /etc/nginx/0-passwords.txt;
    listen 443 ssl default_server;
    ssl_certificate      /etc/nginx/0-snakeoil.crt;
    ssl_certificate_key  /etc/nginx/0-snakeoil.key;

    access_log /var/log/nginx/mainsail-access.log;
    error_log /var/log/nginx/mainsail-error.log;
}

This should seamlessly redirect any unsecured http traffic to https automagically.

**Edited to fix typo transonic_gecko pointed out
 
Last edited:
Instead of just turning off http it might be a better idea to just redirect it to https

Your config would look like this:
Code:
server {
    listen 80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
server {
    auth_basic           "go away";
    auth_basic_user_file /etc/nginx/0-passwords.txt;
    listen 443 ssl default_server;
    ssl_certificate      /etc/nginx/0-snakeoil.crt;
    ssl_certificate_key  /etc/nginx/0-snakeoil.key;

    access_log /var/log/nginx/mainsail-access.log;
    error_log /var/log/nginx/mainsail-error.log;
}

This should seamlessly redirect any unsecured http traffic to https automagically.
This also worked like a charm. However, there is a typo -- don't forget to close the first 'server {' block with a '}' prior to the next 'server {' block!
 

Thanks for this HowTo

As it seems You know what u do I have a question.

I already have an SSL and Password protected reverse proxy - but dont get it working with mainsail.

Maybe you would be so kind to help.

Thats how my Dreambox works:
Code:
<VirtualHost _default_:443>
    ServerName dreambox.MyURL.de
    Redirect / https://dreambox.MyURL.de
        <Location />
            Deny from all
            AuthType basic
            AuthName "home"
            AuthUserFile  /etc/apache2/ssl/httpsusers
            Satisfy Any
            Require valid-user
        </Location>
<IfModule mod_proxy.c>
      ProxyRequests off
      RewriteEngine On
      redirectmatch ^/$ /
      rewritecond %{REQUEST_URI} ^/
      rewriterule (.*) $1 [PT]

      rewritecond %{HTTP_REFERER} dreambox.MyURL.de/dreambox/ [OR]
      rewritecond %{HTTP_REFERER} dreambox.MyURL.de/web/ [OR]
      rewritecond %{HTTP_REFERER} dreambox.MyURL.de/webadmin/ [OR]
      rewritecond %{HTTP_REFERER} dreambox.MyURL.de/dreambox/autotimereditor/ [OR]
      rewritecond %{HTTP_REFERER} dreambox.MyURL.de/dreambox/bouqueteditor/
      rewriterule ^/(.*) /$1 [PT]
      rewriterule ^/webadmin/(.*) /webadmin/$1 [PT]
      rewriterule ^/web/(.*) /web/$1 [PT]
      rewriterule ^/autotimereditor/(.*) /autotimereditor/$1 [PT]
      rewriterule ^/bouqueteditor/(.*) /bouqueteditor/$1 [PT]

      ProxyPass / http://192.0.0.233:81/ timeout=1200
      ProxyPassReverse / http://192.0.0.233:81/ timeout=1200
</IfModule>
</VirtualHost>


Changed it to this for Mainsail but alwais get

"Connection Failed- Cannot connect to Moonraker"

Code:
<VirtualHost _default_:80>
    ServerName 3DPrint.MyURL.de
    Redirect / http://3DPrint.MyURL.de
        <Location />
            Deny from all
            AuthType basic
            AuthName "home"
            AuthUserFile  /etc/apache2/ssl/httpsusers
            Satisfy Any
            Require valid-user
        </Location>
<IfModule mod_proxy.c>
      ProxyRequests off
      RewriteEngine On
      redirectmatch ^/$ /
      rewritecond %{REQUEST_URI} ^/
      rewriterule (.*) $1 [PT]

      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/ [OR]
      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/console/ [OR]
      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/websocket/ [OR]
      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/login/ [OR]
      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/printer/  [OR]
      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/api/ [OR]
      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/access/ [OR]
      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/machine/ [OR]
      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/server/ [OR]
      rewritecond %{HTTP_REFERER} 3DPrint.MyURL.de/socket/


      rewriterule ^/(.*) /$1 [PT]
      rewriterule ^/console/(.*) /console/$1 [PT]
      rewriterule ^/websocket/(.*) /websocket/$1 [PT]
      rewriterule ^/login/(.*) /login/$1 [PT]
      rewriterule ^/printer/(.*) /printer/$1 [PT]
      rewriterule ^/printer/(.*) /api/$1 [PT]
      rewriterule ^/printer/(.*) /access/$1 [PT]
      rewriterule ^/printer/(.*) /machine/$1 [PT]
      rewriterule ^/printer/(.*) /server/$1 [PT]
      rewriterule ^/printer/(.*) /socket/$1 [PT]

      ProxyPass / http://192.0.0.51:80/ timeout=1200
      ProxyPassReverse / http://192.0.0.51:80/ timeout=1200
</IfModule>
</VirtualHost>


Any Ideas???

thx alot.
 
Last edited:
I would bet something in your rewrites is screwing up your connection.

Possibly start by commenting them out one at a time?
 
I would recommend using a pfSense machine and secure the whole network; cannot specify how much more stable and efficient my network is when I removed the ISP router/access point combo and enabled my pfSense machine with Unifi APs.. I had been installing them at work, and setting them up for multiple clients and finally had to upgrade my own. I had Cisco AP's at one point also; but man this Unifi gear really beats everything.
 
Early 2023 when this was posted.....if you aren't using a OPNSense firewall alike solution for your home network back then, "the protect your printer" becomes completely pointless lmao
Sure, adding authentication to Mainsail for those with kids is valid, the protect bit still pointless.

You wanna start securing your devices from the network level, not having local changes, local firewall rules that are gonna go RIP if you need to reinstall the whole OS.
If you have to manually protect devices individually like so, you got way bigger problems than an "unprotected printer".
 
Top