Category Archives: Sysadmin

Postfix with relayhost over stunnel on macOS 10.12 Sierra

I like to have a working mail setup on all machines as this allows to be notified about cronjobs that failed and also to be able to send other notifications that would otherwise be lost. It is also especially useful for things like git send-email or automatically sending GPG signatures with caff to others.

However, mails cannot just be sent from any device and mail servers on the internet usually reject mails from dial-up IPs or public WiFi networks. To fight spam, techniques like SPF have been developed that restrict the mail servers that are allowed to send mails for the domain name used in the From: field. Therefore the best way is to relay all outgoing mail through the mail server that is responsible for your domains.

While most tools also allow you to configure an external SMTP server, it is on one hand tedious to configure it everywhere and on the other hand also insecure if you have to write the username and password for authentication to many user-readable configuration files on your system. Therefore I am running a local MTA on all the computers I administrate to relay mails to a central mail server.

macOS already includes Postfix in the base installation. Luckily, this is also the mail daemon I am most experienced with, although other options such as msmtp or nullmailer exist. However, due to the System Integrity Protection (SIP) on recent macOS versions, there is no way to disable the builtin Postfix. Therefore the best option is to embrace that as a feature and use it.

On macOS, services can automatically be started by triggers configured in the launchd init system. This is also the case for Postfix, which is way there is no process running unless a mail needs to be send. As soon a mail is written to the mail spool directory by sendmail, the Postfix master will be started to pickup and process the mail as usual. This is already configured out of the box on macOS and no special configuration is needed.

Generic relayhost configuration

The following describes the necessary configuration to relay mail over another host with authentication. Note this will also work on other non-macOS machines that have a regular Postfix daemon that runs all the time.

The following additions to /etc/postfix/main.cf are required. First, tell Postfix with which name it should identify itself to other mail servers and which domain name should be appended when sending mail from local users. If your local username is foo, the mail will appear to be from foo@example.org. The relay host is the machine that will accept mails after authentication and send them to the and For the authentication to the SMTP server, SASL has to be enabled with an additional configuration file which is the password storage. The last option ensures that Postfix will only use TLS encrypted connections. Note that this setup assumes you are using port 587 with STARTTLS. Refer to the Postfix manual if you need to use SMTPS over port 465.

# /etc/postfix/main.cf

# Relay mails over mail.example.org
myhostname = foo.example.org
myorigin = example.org
relayhost = [mail.example.org]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/saslpasswd
smtp_tls_security_level = encrypt

The password storage should look like the following example. The password is stored as plaintext, so make sure you set the permissions of the file in a way that only root can access it. It might be a good idea to create a separate user per host on your relay host for this, in order to be able to change or revoke passwords without having to reconfigure all machines.

# /etc/postfix/saslpasswd
[mail.example.org]:587 user:password

Every time you make changes to this file, you have to remember to execute the postmap command to update the .db file right next to it.

$ sudo postmap /etc/postfix/saslpasswd

And that’s it already. Now you are able to send mails via the local sendmail interface. Use the following command with your own email address to test the setup.

$ echo "Foo Bar Baz" | mail -s Test foo@example.org

Broken Postfix on macOS 10.12.6 Sierra

This setup worked fine for years until macOS 10.12.6 Sierra or the following security update arrived, with which Apple broke the TLS client of Postfix. Apparently this was fixed on macOS 10.13 High Sierra, so the following section will not apply to you if you have already upgraded.

As there are no traditional log files on macOS anymore, it took me quite a while to figure out how to debug it as the various Postfix processes all have different names. Use the following command to see everything related to Postfix:

$ log stream --predicate  '(process == "master" OR process == "qmgr" OR process == "pickup" OR process == "cleanup" OR process == "smtp")' --level debug
Filtering the log data using "process == "master" OR process == "qmgr" OR process == "pickup" OR process == "cleanup" OR process == "smtp""
Timestamp                       Thread     Type        Activity             PID
...
2018-03-21 23:41:28.629231+0100 0x158e938  Default     0x0                  82375  smtp: warning: Digest algorithm "md5" not found
2018-03-21 23:41:28.629310+0100 0x158e938  Default     0x0                  82375  smtp: warning: disabling TLS support
...

It looks like that EVP_get_digestbyname(const char *name) from libcrypto will always return NULL and cannot find any digests. I guess Apple patched something and it is now missing a call to OpenSSL_add_all_algorithms(); or similar. One of those classic mistakes when writing your first program against the OpenSSL API.

Using stunnel as an on demand TLS wrapper for Postfix

As the TLS client in Postfix is not usable, I had to find a new solution as I really do not want to send mails over an unencrypted connection. Therefore I chose stunnel as a TLS wrapper acting as a client and relaying connections from a local listening port to a remote host.

$ sudo port install stunnel certsync  # or curl-ca-bundle

Once again, launchd can be used with a new LaunchDaemon in inetd mode to only start the process when it is needed. Choose a free port to run this on, in this example I am using port 555. I recommend to use a port <1024 to ensure it cannot be bound by any other user except root on the same machine. This is using the stunnel3 launch script, as stunnel 5.x only works with configuration files, but I really do not want to keep a file lying around.

<!-- /Library/LaunchDaemons/org.example.mail.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>org.example.mail</string>
	<key>ProgramArguments</key>
	<array>
		<string>/opt/local/bin/stunnel3</string>
		<string>-c</string>
		<string>-r</string>
		<string>mail.example.org:587</string>
		<string>-n</string>
		<string>smtp</string>
                <string>-v</string>
                <string>2</string>
		<string>-A</string>
		<string>/opt/local/etc/openssl/cert.pem</string>
	</array>
        <key>UserName</key>
        <string>nobody</string>
	<key>Sockets</key>
	<dict>
		<key>Listeners</key>
		<dict>
                        <key>SockNodeName</key>
                        <string>localhost</string>
			<key>SockServiceName</key>
			<string>555</string>
			<key>SockType</key>
			<string>stream</string>
		</dict>
	</dict>
	<key>inetdCompatibility</key>
	<dict>
		<key>Wait</key>
		<false/>
	</dict>
</dict>
</plist>

After creating the file, load the new LaunchDaemon and mark it to be loaded automatically each time the system boots. In case you need it, you can permanently disable it again with unload -w. Afterwards test the connection with netcat and see how stunnel connects to the remote host and handles the STARTTLS sequence to transparently encrypt this connection with TLS.

$ sudo launchctl load -w /Library/LaunchDaemons/org.example.mail.plist
$ nc localhost 555
220 mail.example.org ESMTP Postfix

Now all that is left is to edit the Postfix configuration in /etc/postfix/main.cf to connect to localhost:555 instead of the original mail host. As TLS is broken, the security level needs to be reduced to allow unencrypted connections. You will also have to adapt the SASL password storage accordingly and remember to run postmap afterwards.

# /etc/postfix/main.cf

...
relayhost = [localhost]:555
smtp_tls_security_level = may
...

Now test it again by sending yourself an email with the mail command. Enjoy your local mail server setup!

Upgrading a VM from macOS 10.12 Sierra to macOS 10.13 High Sierra in VirtualBox

For testing purposes, I have a VM in VirtualBox currently runnning macOS 10.12 Sierra. Now that macOS 10.13 High Sierra is in Beta, I wanted to upgrade my VM to this new release. However, this proved to be difficult with the usual ways. This blog post will describe how to upgrade a Sierra VM to High Sierra.

Continue reading

How to run rsync on remote host with sudo

Sometimes I want to transfer files including ownership. This is not possible as normal user as the chown(2) system call requires special privileges, that is: uid == 0. However, I do not want to open ssh access for root, but go with the usual way to elevate my privileges: sudo.

I will go through common solutions presented on the web and explain why these do not work at all without significant modifications on the remote host and then present a working solution using X11-Forwarding that is less invasive.

Continue reading

Should we distrust Comodo after issuing a rogue SSL certificate for Windows Live?

About a year ago, I wrote an article why I no longer trust StartSSL. Back then, I said I switched to a paid certificate issued by Comodo under the PositiveSSL brand instead. A reader now brought a recent issue with a Comodo certificate erroneously issued for Microsoft’s Windows Live to my attention and asked whether I would still prefer them over StartSSL.

Arno wrote this comment (link):

Do you still trust Commodo to be more trustworthy than StartCom just because they asked for money to handle revocations? Think twice – a guy from Finland managed to get a valid certificate from Commodo for “live.fi”, (Microsoft Live in Finland), just because he was able to register “hostmaster@live.fi” as his e-mail-address:

http://arstechnica.com/security/2015/03/bogus-ssl-certificate-for-windows-live-could-allow-man-in-the-middle-hacks/

I started to type my answer as a comment as well, but soon I realized my explanation just became too long to be a comment, so I turned it into an article on its own.
Continue reading

Backup with duply to Amazon S3: BackendException: No connection to backend

I stumbled across this problem during setting up duplicity backups to a S3 bucket. As it took me quite a while to resolve this, I wanted to document this problem and its solution here. I just hope someone else with the same problem may find this blog post.

I tried to set up duply, a frontend for the backup tool duplicity, to back up to Amazon S3 storage.

The challenge appeared to be that I wanted to do this with the version available in Debian wheezy. The problem described here is probably already fixed in duplicity >= 0.7.0. These are the versions I used:

i duplicity	wheezy-backports	0.6.24-1~bpo70
i duply         stable			1.5.5.5-1
i python-boto   wheezy-backports	2.25.0-1~bpo7

Problem

I added S3 as a target to the duply configuration as documented on various places on the web. However, I always ran into this error message:

$ duply donkey-s3-test status
Start duply v1.5.5.5, time is 2015-03-12 00:03:40.
Using profile '/etc/duply/donkey-s3-test'.
Using installed duplicity version 0.6.24, python 2.7.3, gpg 1.4.12 (Home: ~/.gnupg), awk 'GNU Awk 4.0.1', bash '4.2.37(1)-release (x86_64-pc-linux-gnu)'.
Signing disabled. Not GPG_KEY entries in config.
Test - Encryption with passphrase (OK)
Test - Decryption with passphrase (OK)
Test - Compare (OK)
Cleanup - Delete '/tmp/duply.10622.1426115020_*'(OK)

--- Start running command STATUS at 00:03:40.984 ---
BackendException: No connection to backend
00:03:41.301 Task 'STATUS' failed with exit code '23'.
--- Finished state FAILED 'code 23' at 00:03:41.301 - Runtime 00:00:00.316 ---

Similar occurrences of this bug are also tracked here: https://bugs.launchpad.net/duplicity/+bug/1278529

Solution

The exception above is highly unspecific and returning such a generic error message is bad style in my opinion. It took me quite a while to find the solution. To make it short, with this snippet from my /etc/duply/donkey-s3-test/conf file I got this to work:

TARGET='s3://s3-eu-central-1.amazonaws.com/.../'
TARGET_USER='...'
TARGET_PASS='...'
DUPLY_PARAMS="$DUPLY_PARAMS --s3-use-rrs"
# XXX: workaround for S3 with boto to s3-eu-central-1
export S3_USE_SIGV4="True"

Using a shell export in the configuration file is clearly a hack, but it works. In fact, you can also export it to the environment before running duply or set it in the configuration file of the boto library. However, with the former, you do not have to change anything on the duply invocation.

Why does this solve the problem?

I found out that the problem was not reproducible for some people because it only appears in specific regions. I use Frankfurt, EU (eu-central-1) as my Amazon S3 region. According to the documentation, only the newest API V4 is supported in this region:

Any new regions after January 30, 2014 will support only Signature Version 4 and therefore all requests to those regions must be made with Signature Version 4.

The region Frankfurt, EU was introduced after this date. This means this new region only accepts requests with “Signature Version 4” and not any prior version. Meanwhile other regions continue to accept the old API requests.

This kind of setup is complete madness for me. Especially for open source projects with developers all around the globe, this just means that some developers could not reproduce the problem. Who would assume your endpoint region matters?

In fact, the duplicity manual page has a whole section on how European endpoints are different from other locations. Unfortunately, the recommended --s3-use-new-style --s3-european-buckets does not solve this problem. I could not even observe any difference in behavior with these flags.

Apparently, the boto library used by duplicity for access to Amazon S3 supports the new “Signature Version 4” for API requests, but it is not enabled by default. By exporting this environment variable S3_USE_SIGV4=True the library is forced to use “Signature Version 4”.

The specification of the target protocol for duplicity is another peculiarity. Make sure you use s3:// and specify an explicit endpoint region in the URL, as I could not get it work with s3+http:// and also always with the hostname for your region.

Further Investigations

Unfortunately, the duplicity option --s3-use-rrs which is supposed to put the files into the cheaper Reduced Redundancy Storage (RRS) does not seem to do anything and all uploaded files get the standard storage class. Probably I have to maintain my own installation of the latest versions of duplicity and boto to get all the features to work.

Depending on where you are in the world, YMMV.