Protecting the Pi: Restricting SSH to the USB gadget
Why?
SSH is a security hole but is pretty much the only way to work on a Raspberry Pi without an attached keyboard/monitor or serial console cable. There are some good articles that describe how to lock down SSH via password change or other security measure.I chose a slightly different approach that limits SSH based on the interface. 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
The system can ALLOW / DENY ssh on any network interface. We can create inbound firewall rules. The script below uses iptables / ip6tables to block ssh wireless traffic and leaving SSH enabled for wired connections. Some folks may wish to further disable SSH over hardwire ethernet connections.

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 have 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 force 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
Comments
Post a Comment