SSL & Firewall for Mainsail
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...