A watchful pi

I recently set up my home desktop computer for working remotely and wanted to share what I did — mostly as a personal reference, but perhaps someone out in the world will find this info useful!

The Goal

The goal was to set up a system that would allow me to ssh into my desktop at home from any network. There were a few steps to making this happen:

Keeping track of our home network’s dynamic IP address

Our home network, like most folks’ home networks, has a dynamic IP address. This means that every once in a while, our internet service provider may assign our network a new IP address. That change would then prevent me from being able to reach my home computer as I’d have no way of knowing the new IP (unless I’m home).

There are many ways to work around a dynamic IP address (eg, a Dynamic DNS service), but I wanted something free :). One of my housemates had a Raspberry Pi lying around, so I set up the Pi to monitor our home network’s IP address and notify me via email if it ever changes.

To do this, I set up a Cron job to run a “check IP” script every five minutes. The script is:

#!/bin/bash
LOCK_FILE=/tmp/checkip.lock
function fail {
        rm -f $LOCK_FILE
        exit 1
}
lockfile -r 0 $LOCK_FILE || exit 1
IP_FILE=/home/pi/Documents/myip.txt
# dig command below seems to periodically fail with error:
# "dig: couldn't get address for 'resolver1.opendns.com': failure"
# maybe due to wifi dropping out or throttling by opendns?
# just exit if it fails, it should start working again in a few minutes
# and being notified with ip="" is not useful info
NEW_IP=`dig +short myip.opendns.com @resolver1.opendns.com` || fail
OLD_IP=$(cat $IP_FILE)
if [[ $NEW_IP != $OLD_IP ]]; then
    echo $NEW_IP > $IP_FILE
    echo -e "Subject: IP Change - sneezy\r\n\r\n$OLD_IP -> $NEW_IP" | sendmail $MY_EMAIL_ADDRESS
fi
rm -f $LOCK_FILE

I used the sendmail utility to handle sending email, which pretty much required no set up after installation and is used as shown above.

Sleeping/Waking

Another piece of this puzzle was establishing a way to wake up my desktop remotely, since it’s generally asleep if I’m not actively using it.

Again, there are a few ways to go about doing this depending on the network setup and what the computer hardware supports. In my case, my desktop is connected via wifi to our network so Wake-on-LAN was out, and its wireless card doesn’t support Wake-on-Wireless-LAN so that was out too. Having already had the Raspberry Pi in the picture, I was able to work around this problem by setting up a wired network just between my desktop and the Pi:

  • Attach an ethernet cable between the desktop and Pi.
  • Set up a wired network connection with static IPs on each device

Now, I can ssh into the Pi (since its keeping track of our home’s dynamic IP address for us) and send a Wake-on-Lan message to the desktop over their wired network. I use the wakeonlan utility over ssh like this: ssh <pi IP and port> wakeonlan -i <desktop wired static IP> <desktop MAC>.

Similarly, I can sleep the computer by sending a sleep command (this time directly to the now-awake desktop) like so: ssh -t <desktop IP and port> sudo systemctl suspend.

Staying awake

I noticed that my desktop would not count remote ssh connections/activity against its sleep timer, so I had to do a bit more configuring to prevent the computer from going to sleep while I’m connected to it. This bit heavily depends on the operating system, but for Ubuntu 18.04 (and any machine using systemd) the following should do the trick:

  • Add the following block of code as a new service file in /etc/systemd/system/, call it something like “dontsleep.service”:
[Unit]
Description=Inhibit suspend in case of [some] activity
Before=sleep.target

[Service]
Type=oneshot
ExecStart=/bin/bash -c "if [ $(ss | grep ssh | wc -l) -gt 0 ]; then exit 1; else exit 0; fi"

[Install]
RequiredBy=sleep.target                                                                             
  • Enable the service via systemctl enable dontsleep.service so it will always be active at boot.
  • Start the service via systemctl start dontsleep.service so it will be active now.

By being required by sleep.target, this service will be run every time the computer wants to go to sleep, and if it returns a nonzero value the sleep will be prevented. The service runs a script that checks for active ssh connections, and returns nonzero if there are any, else 0.

Static IPs and Port Forwarding

The final piece was setting up static IP addresses on our home network for the desktop and Raspberry Pi, and forwarding ports so that I can ssh into the devices from the Internet at large. This was easily done through our router’s web interface, so I won’t go into detail here.

Security

Oh, and a note on security. With all this set up, people may be able to detect that the machines at home are listening for ssh connections. They may try to log in to the devices by brute forcing usernames and passwords. A few best practices to prevent these attacks are to:

  • Change the ssh server port from the default 22 (the Port option in the ssh server configuration file).
  • Disable password-based authentication and only use a password-protected public-private key pair (by adding the remote device’s public key to the home devices’ authorized hosts via ssh-copy-id, then setting PasswordAuthentication no in the ssh server configuration file).

Questions?

If you’re trying to set up something similar and have any questions, feel free to reach out via any of my contact info below! I hope this is useful for someone out there — at least for me in a year when I need to re-understand what I’ve done :)