August 6, 2014

Shunting reminders and files with UUCP

Unix-to-Unix Copy is a rather old way of sending files between Unix systems.  Before SMTP was invented, it was the de-facto way to shunt email around, and prior to NNTP, was also the backbone of Usenet.

Fast forward to today, we’ve got a lot of options open to us.  So why would I use a crusty old one like UUCP?

UUCP has one big advantage, it doesn’t assume your system is always online.

  • It might be a workstation at your workplace which is behind a corporate firewall.
  • It might be a more powerful desktop computer at home that’s usually in “sleep” mode to save power.

Because the initial connection can be established in either direction, it is ideal for a system that may not be directly reachable, but is able to poll on a regular schedule for instructions.  It’s also useful, since UUCP assumes some steps need to be taken to bring a link up, to perform tasks such as powering on a system using IPMI or Wake-on-LAN, wait for it to come up, perform a task, then have the machine power back down when finished.

UUCP over the Internet

Now, UUCP can and does work directly over the Internet.  in.uucpd runs from inetd, and basically fires up uucico each time someone connects to it. But: it is unencrypted and insecure. It’s not what you want exposed on today’s public Internet.

UUCP does support SSL, and there are ways to make stunnel work with packages like Taylor UUCP. This still requires some significant setup and an additional open port.

There’s another way. Thanks to the OpenBSD community, we have OpenSSH, and it is very trivial to set up a secure UUCP link using public key authentication, to lock down the public key to only be used with uucico, and to effectively secure communications between your hosts.

Generating the SSH key

Since this is going to be used with an automated service, one needs to make it a passwordless key. Go for something secure in terms of the key length and algorithm: I used 4096-bit RSA. To do this, log in as root then:

# su uucp -
$ ssh-keygen -t rsa -b 4096 -N '' -C 'UUCP key'
Generating public/private rsa key pair.
Enter file in which to save the key (/var/spool/uucp/.ssh/id_rsa): 
Your identification has been saved in /var/spool/uucp/.ssh/id_rsa.
Your public key has been saved in /var/spool/uucp/.ssh/id_rsa.
The key fingerprint is:
c3:42:5d:77:a9:c2:3a:da:bd:98:6a:5d:03:62:79:19 UUCP key
The key's randomart image is:
+--[ RSA 4096]----+
|          . . .. |
|       .E. . ..  |
|      ...+   .   |
|     .+.+ o .    |
|     ..oSo .     |
|       .o.o      |
|       + + .     |
|      o oo.      |
|     ...o ..     |

You have a choice. You can either: make a keypair for each host, and set up authorized_keys so the hosts can log into eachother, or you can use the same keypair for all hosts. I went the latter route, as I’m not that paranoid. Whilst still logged in as the UUCP user:

$ echo 'command="/usr/sbin/uucico -l" '$(< .ssh/ ) > .ssh/authorized_keys

Now, securely transfer the UUCP user’s .ssh directory between your hosts. This will allow uucp to log in.

Populating known_hosts

The easiest way to do this, is to log into each host as the UUCP user, then run a script like this:

$ for h in host1 host2 host3 ; do ssh $host true; done

Check each key carefully, answer yes if you’re satisfied.

UUCP Log-in script

Taylor UUCP, has the ability to define a “port” that runs an arbitrary application. You could put a call to SSH here, but there’s another trick I use. As root:

# cat < /usr/local/bin/uussh
echo -n 'Address: '
read user host wake

if [ -n "${wake}" ]; then
        until ping6 -c 1 -w 1 -n "${host}" 2>&1 >/dev/null \
                        || ping -c 1 -w 1 -n "${host}" 2>&1 >/dev/null \
                        || [ $timeout -le 0 ]; do
                timeout=$(( ${timeout} - 1 ))
                /usr/bin/wol ${wake} 2>&1 > /dev/null

exec /usr/bin/ssh -x -o StrictHostKeyChecking=no -o batchmode=yes ${user}@${host}
# chmod 755 /usr/local/bin/uussh

Now we can define one “SSH” port, that will automatically wake a computer if needed, wait for it to become alive, then initiate the SSH link. The chat script will specify the host name.

Taylor UUCP configuration

Now we come to UUCP itself. First, let’s create this special port. Edit /etc/uucp/port and add the following:

port ssh
type pipe
command /usr/local/bin/uussh

Now, we’ll set up login usernames and passwords for each host. The easiest way is to do this from a local shell, then distribute the generated passwords.

$ for src in host1 host2 host3 host4; do
   [ -d $src ] || mkdir $src
   for dest in host1 host2 host3 host4; do
      [ -d $dest ] || mkdir $dest
      if [ $src != $dest ]; then
         passwd=$( dd if=/dev/urandom bs=12 count=1 2>/dev/null | base64 )
         echo "$dest $src $passwd" >> $src/call
         echo "$src $passwd" >> $dest/passwd
$ for h in host1 host2 host3 host4; do scp $h/* root@$h:/etc/uucp/; done

Now we have separate usernames and passwords on each host. We can finish up with the /etc/uucp/sys file:

commands rmail rnews gruntreceive-uucp
chat-timeout 120

These are some initial instructions that apply to all hosts. Here, I give permission to run rnews, rmail and gruntreceive-uucp, and I tell it to wait 2 minutes before giving up.

The following is an example of a host that sleeps and needs to be woken first:

system host1
chat Address: uucp\shost1.local\saa:bb:cc:dd:ee:ff\n\c login: \L Password: \P
port ssh
time any
call-login *
call-password *
protocol t
forward-from ANY
forward-to ANY

The following, is an always-on host.

system host2
chat Address: uucp\shost2.some.domain\n\c login: \L Password: \P
port ssh
time any
call-login *
call-password *
protocol t
forward-from ANY
forward-to ANY

Phoning home and scheduling retries

In the case of satellite systems behind some resticted network, assuming you have a way of tunnelling out of the network, you can “phone home” on a regular basis. You also want to periodically call uucico on all hosts to check if there’s any scheduled work on. This is done via /etc/crontab:

* * * * * uucp /usr/sbin/uucico -r1 -q
0 * * * * uucp /usr/sbin/uucico -r1 -s main_hub -c

The first line is a good idea on all hosts. It checks each minute for work to do, and calls uucico to do it.

The second line is the phone-home bit. In this case, it phones home to a system called main_hub, which in my case, is my public web server. You’ll want this second line on your satellite systems. It basically unconditionally phones home, and checks for instructions.

Great, UUCP works, what now?

Well, now you have a way of sending files between hosts. Two services that run well over UUCP worth investigating:

  • grunt: is a tool for securely running commands on another host. It can work over email or UUCP and uses GnuPG signature verification for authentication.
  • Many MTAs support UUCP as a back-end, such as Postfix. Very handy for sending reminders to yourself in a manner that is guaranteed to be noticed and not get buried in spam.