Protecting the Pi: Restricting SSH to the USB gadget
Why?
ssh is a remote login that is pretty much the only way to work on a headless Raspberry Pi without an attached keyboard/monitor or serial console cable. Remote login capability is a security risk even with something like SSH. There are some good articles that describe how to lock down SSH via password change or other security measures.I decided to limit SSH to specific network interfaces. Raspberry PIs can have several network interfaces. The only hardwire network interfaces Raspberry Pi Zero when it is in Network Gadget mode. I only truly trust the USB private LAN (usb0) since it requires a direct connection and cannot be directly seen by any other device.
Interfaces
Our Raspberry PI can have several different Network Interfaces. All of them are candidates for allowing or denying SSH or other inbound access.
Linux lets us manage ssh by letting us configure port related ALLOW / DENY on all network interfaces. We can create inbound iptables firewall rules. The script below blocks ssh wireless traffic and leave SSH enabled for wired connections. It is based on iptables / ip6tables. Some folks may wish to disable SSH over specific hardwire ethernet connections. These are some of the standard Raspberry Pi network devices.
- usb0: Raspberry Pi Zero running in gadget mode with a hard wire (USB) connection to a PC.
- eth0: Raspberry Pi 3 and 3+ hard-wire Ethernet jacks.
- wlan0: Raspberry Pi Zero W, Raspberry Pi 3, 3+ with wi-fi enabled or Raspberry Pi (2, Zero...) with USB network adapter and no built-in wi-fi networking.
- wlan1: Raspberry Pi Zero W, Pi 3 with built-in wi-fi networking and USB wi-fi adapter
Finding the Right Interface
A Raspberry Pi running the network gadget has more than one active network interface. They also have two network addresses per interface, one IPV4 and one IPV6. The Pi broadcasts its' DNS information for all interfaces so it can be picked up by mDNS / Bonjour. A Pi with two network interfaces will have 4 addresses, two IP4 and two IP6. You normally don't care about this because the target service is enabled across all devices.
Our ssh driven change removes ssh access from some of the devices. Now we have forced ssh to the interface/ip that is still enabled. You must use dns-sd to explicitly find the set of IPs for all active network devices on the Raspberry Pi.
- dns-sd -G v4v6 <hostname>.local
The following dns-sd command shows two network interfaces. I know I want to use the 169.x.x.x address because it represents the private connection between the PC and Raspberry PI over the USB gadget link. In this case, we would ssh into the 169.254.107.129 IP address.
C:\Users\joe>dns-sd -G v4v6 pi-520863f1.local
Timestamp A/R Flags if Hostname Address TTL
21:57:00.586 Add 3 20 pi-520863f1.local. FE80:0000:0000:0000:1B31:2156:F706:AFB8%ethernet_32785 120
21:57:00.590 Add 2 20 pi-520863f1.local. 169.254.107.129 120
21:57:00.674 Add 3 13 pi-520863f1.local. FE80:0000:0000:0000:1BE6:83EB:185C:D72F%ethernet_32777 120
21:57:00.676 Add 2 13 pi-520863f1.local. 192.168.1.3
Connection delays on the private LAN
I have found that ssh can take a while the first time. It feels like the machine first tries to use the main interface. Then there is some kind of network timeout after which ssh quickly connects. Normally I just ping <hostname.local> until the ping is successful.
Better living through scripting
The following script enables ssh and then blocks inbound ssh on wireless connections wlan0 and wlan1. It can also block ssh on eth0 for those that only allow ssh over the gadget interface. The most recent source code can be found on GitHub .
#!/bin/bash #!/bin/bash # is rerunable # # Portions styled from # ref: https://kerneltalks.com/virtualization/how-to-reset-iptables-to-default-settings/ # ref: https://makezine.com/2017/09/07/secure-your-raspberry-pi-against-attackers/ # ref: https://www.raspberrypi.org/documentation/remote-access/ssh/ # ref: https://serverfault.com/questions/475717/iptables-block-incoming-on-eth1-and-allow-all-from-eth0 # ref: https://askubuntu.com/a/30157/8698 # eth0 hardware, # wlan0 PZero, P3, P3+ builtin or P2 USB Wi-fi # wlan1 P3, P3+ USB Wi-Fi disable_eth0=false disable_wlan0=true disable_wlan1=true if ! [ $(id -u) = 0 ]; then echo "The script need to be run as root." >&2 exit 1 fi if [ $SUDO_USER ]; then real_user=$SUDO_USER else real_user=$(whoami) fi # Commands that you don't want run with root would be invoked # with: sudo -u $real_user # So they will be ran as the user who invoked the sudo command # Keep in mind, if the user is using a root shell (they're logged in as root), # then $real_user is actually root # sudo -u $real_user non-root-command # Commands that need to be ran with root would be invoked without sudo # log the current rules echo "----------------------------------------------" echo inbound rules before more reset and ssh block iptables --list --verbose echo ip6tables --list --verbose echo "----------------------------------------------" # reset the inbound rules iptables -F INPUT ip6tables -F INPUT echo "Reset Input Rules" # enable iptables inbound rule blocking SSH on wlan0/eth0 if [ "$disable_wlan0" = true ]; then if [ -e /sys/class/net/wlan0 ]; then echo "wlan0: Blocking port 22" iptables -A INPUT -p tcp --dport 22 -i wlan0 -j DROP ip6tables -A INPUT -p tcp --dport 22 -i wlan0 -j DROP fi else echo "wlan0: No block requested" fi if [ "$disable_wlan1" = true ]; then if [ -e /sys/class/net/wlan1 ]; then echo "wlan1: Blocking port 22" iptables -A INPUT -p tcp --dport 22 -i wlan1 -j DROP ip6tables -A INPUT -p tcp --dport 22 -i wlan1 -j DROP fi else echo "wlan1: No block requested" fi if [ "$disable_eth0" = true ]; then if [ -e /sys/class/net/eth0 ]; then echo "eth0: Blocking port 22" iptables -A INPUT -p tcp --dport 22 -i eth0 -j DROP ip6tables -A INPUT -p tcp --dport 22 -i eth0 -j DROP fi else echo "eth0: No block requested" fi # log the current rules echo "----------------------------------------------" echo inbound rules after blocking ssh on wlan0/eth0 iptables --list --verbose echo ip6tables --list --verbose echo "----------------------------------------------" # install if not already there PKG_OK=$(dpkg-query -W --showformat='${Status}\n' iptables-persistent | grep "install ok installed") #echo Determined status for iptables-persistent: '$PKG_OK' if [ -z "$PKG_OK" ]; then echo "No iptables-persistent. Installing iptables-persistent." apt-get --yes install iptables-persistent else # persist the changes. echo "iptables-persistent present so just reconfigure." iptables-save >/etc/iptables/rules.v4 ip6tables-save >/etc/iptables/rules.v6 fi if [ "`systemctl is-active ssh`" != "active" ] then echo "starting ssh" systemctl enable ssh systemctl start ssh else echo "ssh already started" fi
Related
- https://joe.blog.freemansoft.com/2018/03/setting-up-raspberry-pi-zero-without.html
- https://joe.blog.freemansoft.com/2018/03/protecting-pi-restricting-ssh-to-usb.html
- https://joe.blog.freemansoft.com/2022/11/installing-rndis-driver-on-windows-11.htm
Comments
Post a Comment