Postfix+uucp+ssh
At $DAYJOB, we run servers in Google Compute Engine. Google (wisely) does not allow TCP port 25 traffic into or out of its Compute Engine offering. How to get email in and out? Well, we can put together an email stack using Postfix as the mail transfer agent (and sendmail equivalent), Taylor UUCP as a non-SMTP transport facility, and OpenSSH to carry the UUCP protocol.
Why these components?
A bit of rationale for each of these:
- Postfix: I like its config file format and flexibility. And I find it easier to get along with than sendmail. After finding Postfix, I've never bothered to get acquainted with exim.
- Taylor UUCP: It's the UUCP available on Debian. The config files aren't too hard to understand. And there is plenty of support if you want it available via Google.
- OpenSSH provides us cryptographic authentication of both ends, management of leaf systems' access by public keys, it provides a reliable pipe interface usable by UUCP, and can be configured for securely identifying both parties without having a human type a password.
Architecture
This is a leaf and trunk setup. The Google Compute Engine machines will be connecting to a central mail hub system running in Amazon EC2. These leaf systems will periodically (driven by cron) run the uucico component of the UUCP stack to send messages to (and potentially pull them from) the Amazon mail hub machine.
Required software packages
First off, we need some software on each of the leaf systems. All our systems are running Debian or Ubuntu, so installing software is an apt-get install
away.
For the leaf and hub systems, do this:
apt-get install openssh-server openssh-client uucp postfix
When debconf asks its questions about postfix configuration, tell it that we're on a "local only" system. We'll fix up that config later.
A leaf to hub communications channel
Setting up SSH communications
client side
The UUCP facility runs as user uucp
on both the leaf and hub systems. So setting up the SSH communications channel means setting up the uucp
user's SSH config on both ends. For the short term, the uucp
user will need regular shell access. Set it up with bash like so:
sudo chsh -s /bin/bash uucp sudo su - uucp
And generate a bit, fat SSH RSA keypair for as the uucp
user:
cd ~uucp # should put us in /var/spool/uucp mkdir -m 700 .ssh || true ssh-keygen -vv -t rsa -b 8192 -N ''
Explanation of key generation parameters:
- -vv
- Be extra verbose
- -t rsa
- Generate an SSH protocol version 2 RSA key
- -b 8192
- Make the key 8192 bits long. This should be secure for a very, very long time.
- -N ''
- Do not secure the private key with a passphrase. Which is what we'll need to do use it as a batch process.
Without a -f filename
option, ssh-keygen
will leave the RSA private key file it generates in $HOME/.ssh/id_rsa
server side
Again, we need to do some stuff as user uucp
. Set that up:
sudo chsh -s /bin/bash uucp sudo su - uucp
Create a .ssh directory in user uucp
's home directory:
cd ~uucp mkdir -m 700 .ssh
Allow key-based authentication from the leaf system. We do this by putting the leaf system's uucp
user's public key in the hub system's uucp
user's .ssh/authorized_keys
file. You'll want to copy and paste the leaf system's public key from its user ~uucp/.ssh/id_rsa.pub
file. It will look something like this:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAEAQC+MsCRxe8+YEBqWmu+OArYqJAJ/Kxrv0Th8LrCqtiedwaIY+0MoxCbIbOKb6qcIPcSD8abwVWet6ao72BH5VGfxTF4NsIyzZuY3yw1halwEcA4nFJxjkSovQsSIpZNYfSUQ2uKI+HZwtFMNCrLwozsW7OwrSHHZehApNH34NrCrn3ToG8GjCo7YPvg+FvqwvLOKdBiPUiKkK+K5f8VVO59YGX3Jrs2W+Y30jeddNAs2b7JjlDFogg8usGHGLdLwGIcyUA+/fdHzZJd3w58U27Q3fqv0EghtD8AH+AkM5mcu3+PqFuPPzzgnCd9YDH69tM6/JVh6D4V14P+bwxiIWl2+6cVoRopwsvy0aXyMaCbMIbu18hy2ECr6T4I96l2xNs5L/945tev33uUeYx5GpWhIFMP+uPbb+UaISbEbLD1X2wpHIUXWmmu+Wt16NtEa2ZJWOOMptv8Mzk/xSVjcro071xGcJVREiusr8xLGYBbgAWFo4SWX4PqPXwzPOq6cE6mKrz/4hF/cAfiexppZp5bsqoy2RFQSLtUSTpocYjIW4Q7XJheyh8Ha4PNfZGo+Mh6AMc6hJBZH0PKlkqR2QenjaWrZ2fUPrKmI1sNK1RD9n3HigV5lTumjgQTEVuYaQxIC4uyXVV54/ifBXzXh6OtVkdnakf9YEry97FBqvUGtNvpTkgmmrz7sVOg+IPFDQmD2RpcOUE4Mh+mxuIRTi+CZAU7nWjGGcObFBrSzVR8m2oVWuOare/eOD3PM3EAC8C8Ks1diISyc1u9iwG5xQ1PQOgpWPC4DWskC8ewI+KJT0uz2l+v6zYiu0Hxm2MNoqJm3cJv4hOgLU1trCYbhI1piHYVNDd7Pm1M9DwZYbhCF5rvhMd+vRWxeTgU3sswdGeA9+UjHBh26emLBZSRziE3n1XkdOqwSAJ8hlXZoWDkttyOZnHDs9Z+jl+YAqEMLecbGCIhKoiQYxZkhCI83DqX8uJ3KgMf+RwtwoD8+nDGYIj052z22eMiSue2/+bybzuv72wBThteg28dY9e6SyYeaWu7iFqZG8SbkDKsVExmE+PKhdPQ0jrc5zjs0j4xpo7G+d4Bxf+IctV0WEJV5Q1tZRruNlO2573Sy/6dhLWm9vZFt9HjgGdF/+/fmvg3yWzDzpRTe7+db4Mt7T54DZVpxeGtaG5TeiIvEdp5jZsG7x1l/emydi49uyzfo/u9D5OgtC7wcFM8b7GxSAAMX4rz3crLddbga/vGtkSRHyMinjT9v6+/KiTIPNWK4/oabkvo0s+21tDtx7tlG/aUzGuQPOLOplKU/LVDq17Y3w2oNKIk3Ef6MTs0vfZB0YDGMGNn7Q1Dt8PRdDSI5Sn6uVRT uucp@platform-development-0
OpenSSH's sshd
allows additional restrictions to be placed on the use of a key. These go at the front of the line in the user authorized_keys
file. Here is a suggested set to start with:
from="162.222.179.81",no-agent-forwarding,no-port-forwarding,no-user-rc,no-X11-forwarding,command="/usr/sbin/uucico --debug 5 --prompt --login arch.imarketingplatform.com"
A bit of explanation for those restrictions:
- from="162.222.179.81"
- This key is only allowed to be used by a client at 162.222.179.81.
- no-agent-forwarding
- This key is not allowed to set up an ssh-agent proxy connection
- no-port-forwarding
- This key is not allowed to forward arbitrary TCP ports through the server
- no-user-rc
- Do not read the
uucp
user's.ssh/rc
file - no-X11-forwarding
- Do not forward X11 traffic
- command="/usr/sbin/uucico --debug 5 --prompt --login arch.imarketingplatform.com"
- When this key is used to authenticate, the specified command will be run.
uucico
details:- /usr/sbin/uucico
- this is the UUCP file transfer program
- --debug 5
- Sets the amount of debugging info logged. 5 is probably overkill, but it's not a huge amount of disk space required.
- --prompt
- tells uucico that it will be printing its own "login: " and "password: " prompts.
- --login arch.imarketingplatform.com
- tells uucico that the remote user name is arch.imarketingplatform.com and that there is no need to prompt for a user name. This setting allows us to associate an SSH key with a single remote UUCP system. Without this being set, the remote system gets to tell uucico which user to authenticate as.
testing this layer
On the client system, as the uucp
user, run this:
ssh -v -i .ssh/id_rsa uucp@mail.imarketingplatform.com < /dev/null
And expect output similar to this:
OpenSSH_6.6.1, OpenSSL 1.0.1e 11 Feb 2013 debug1: Reading configuration data /etc/ssh/ssh_config debug1: /etc/ssh/ssh_config line 19: Applying options for * Pseudo-terminal will not be allocated because stdin is not a terminal. debug1: Connecting to mail.imarketingplatform.com [54.186.217.34] port 22. debug1: Connection established. debug1: identity file .ssh/id_rsa type 1 debug1: identity file .ssh/id_rsa-cert type -1 debug1: Enabling compatibility mode for protocol 2.0 debug1: Local version string SSH-2.0-OpenSSH_6.6.1p1 Debian-4~bpo70+1 debug1: Remote protocol version 2.0, remote software version OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 debug1: match: OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 pat OpenSSH_6.6.1* compat 0x04000000 debug1: SSH2_MSG_KEXINIT sent debug1: SSH2_MSG_KEXINIT received debug1: kex: server->client aes128-ctr hmac-md5-etm@openssh.com none debug1: kex: client->server aes128-ctr hmac-md5-etm@openssh.com none debug1: sending SSH2_MSG_KEX_ECDH_INIT debug1: expecting SSH2_MSG_KEX_ECDH_REPLY debug1: Server host key: ECDSA c9:47:bd:43:30:39:70:2d:91:b9:3b:46:3d:8b:48:39 debug1: Host 'mail.imarketingplatform.com' is known and matches the ECDSA host key. debug1: Found key in /var/spool/uucp/.ssh/known_hosts:1 debug1: ssh_ecdsa_verify: signature correct debug1: SSH2_MSG_NEWKEYS sent debug1: expecting SSH2_MSG_NEWKEYS debug1: SSH2_MSG_NEWKEYS received debug1: Roaming not allowed by server debug1: SSH2_MSG_SERVICE_REQUEST sent debug1: SSH2_MSG_SERVICE_ACCEPT received debug1: Authentications that can continue: publickey,password,keyboard-interactive debug1: Next authentication method: publickey debug1: Offering RSA public key: .ssh/id_rsa debug1: Server accepts key: pkalg ssh-rsa blen 1047 debug1: key_parse_private2: missing begin marker debug1: read PEM private key done: type RSA debug1: Authentication succeeded (publickey). Authenticated to mail.imarketingplatform.com ([54.186.217.34]:22). debug1: channel 0: new [client-session] debug1: Requesting no-more-sessions@openssh.com debug1: Entering interactive session. debug1: Remote: Agent forwarding disabled. debug1: Remote: Port forwarding disabled. debug1: Remote: User rc file execution disabled. debug1: Remote: X11 forwarding disabled. debug1: Remote: Forced command. debug1: Remote: Agent forwarding disabled. debug1: Remote: Port forwarding disabled. debug1: Remote: User rc file execution disabled. debug1: Remote: X11 forwarding disabled. debug1: Remote: Forced command. debug1: Sending environment. debug1: Sending env LANG = en_US.UTF-8 Password:debug1: client_input_channel_req: channel 0 rtype exit-status reply 0 debug1: channel 0: free: client-session, nchannels 1 debug1: fd 0 clearing O_NONBLOCK Transferred: sent 5676, received 3804 bytes, in 2.4 seconds Bytes per second: sent 2376.9, received 1593.0 debug1: Exit status 0
If the two output lines reading "debug1: Authentication succeeded (publickey)." and "Authenticated to mail.imarketingplatform.com ([54.186.217.34]:22)." are missing, this indicates a problem doing public key authentication. Check the SSH server log for clues. The most likely issues are file permissions on the authorized_keys
file on the server.
The "Password:" in the above output is uucico
prompting for the client system's password. If that isn't there, make sure UUCP is installed on the server system, check the server side UUCP debug log, and double check the command setting for that key in the authorized_keys
file on the server.
Near the end, also not the "Remote: Port forwarding disabled." and similar indications. These reflect the restrictions in the authorized_keys
entry for this key.
Taylor UUCP configuration
Wherein we will tell the hub and leaf systems about each other, configure UUCP authentication, and set them up to talk over our SSH connection
leaf node side
The leaf system will be doing all of the calling out, so we'll set that up first.
cron will be running something like uucico -smail.imarketingplatform.com
periodically. This will tell it to connect to the remote UUCP system it knows as mail.imarketingplatform.com (as listed in /etc/uucp/sys
) send any waiting files it has to the remote system and pull any files the remote system has down.
The leaf system needs to have the following in /etc/uucp/sys:
system mail.imarketingplatform.com time Any port mail.imarketingplatform.com call-login * call-password * protocol a sequence true free-space 67108864 commands rmail
Some explanation of those:
- system mail.imarketingplatform.com
- This begins the description of the remote system. Note that this is only a UUCP system name and need not correspond to any real or DNS name.
- time Any
- when is the local uucio allowed to call the specified remote system?
- port mail.imarketingplatform.com
- Look for a corresponding communications port description in
/etc/uucp/port
- call-login *
- The username we will use to do UUCP authentication with the remote system will be looked up in
/etc/uucp/call
- call-password *
- The password we will use to for UUCP authentication with the remote system will be looked up in
/etc/uucp/call
- protocol a
- This tells uucico to use the Taylor uucp 'a' file transfer protocol. Experience has shown this to be the best choice for links with asymmetric bandwidth (ie, most DSL and cable modem setups)
- sequence true
- Use conversation sequencing. This is a security measure.
- free-space 67108864
- Ensures that at least 64Mbytes of space will be available after receiving the incoming file. Else, the file transfer will be rejected. Keeps us from filling up /var.
- commands rmail
- the remote system will only be able to send email. No Usenet style news. No file transfers. No other remote command execution. This is another security restriction
So, how do things look now if we ask uucico to poll mail.imarketingplatform.com?
$ sudo -u uucp /usr/sbin/uucico -x 11 -s mail.imarketingplatform.com; echo $? 0 $
That looks fine. Let's check the log file:
$ uulog uucico mail.imarketingplatform.com - (2014-10-10 17:45:46.36 22019) ERROR: No matching ports $
Here, uucico is saying that it doesn't know how to connect to the remote system we told it to talk to. Which goes back to the port mail.imarketingplatform.com
listing in our /etc/uucp/sys
file. We'll fix that next.