Tag Archives: ssl

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!

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

SNI bug fix for ssl-cert-check, now on Github

Recently I found some new bug in ssl-cert-check, my tool for checking the expiry dates of local and remote SSL certificates. The tool has worked fine for me over the years, but I used to have expiry dates that were very close to another, so I never noticed that it did not work for web servers using SNI to serve multiple domains with virtual hosts from the same IP address. I am sorry I did not think of this earlier. I fixed the bug now and in case you have such a setup, I encourage you to get the new release.

I also noticed it becomes cumbersome to maintain the updates with blog posts and downloads here. Therefore, ssl-cert-check is now on Github and I will continue to make releases from there.

You can get the latest release tagged 2014-08-20 either as a local download or from github.com.

Checking expiry dates of local and remote SSL certificates

A few years ago I already covered how to check the expiry date of a SSL certificate using OpenSSL. When the recent global outage of Windows Azure was caused by an expired SSL certificate, this got me thinking about this topic once again.

In my previous post, I presented a mechanism to check the remaining lifetime of an SSL certificate using the PEM certificate file locally on disk. However, in the case with Azure recently, customers were dependent on the actions by Microsoft and had no access to the actual file. Thus, there might be a need to check remote certificates of services you use, e.g. your off-site backup provider. As you want to ensure you can always use this service, why not check this for expiring certificates once in a while? Maybe it’s your notification to the sysadmins who saves yourself and all other customers from being locked out.

Here is the updated script ssl-cert-check that now not only handles x509 certificate files, but also remote addresses for various forms and protocols. For simplicity, here are the usage instruction from inside this script:

Continue reading

Checking expiry dates of SSL certificates

Once again I missed the expiry date of one of the SSL certificates on my server. Therefore I am now using a cronjob to warn me early enough that a certificate is about to expire.

This is the script /usr/local/bin/ssl-cert-check which checks the expiry date of the certificate files passed as arguments:

#!/bin/bash
 
DAYS=30
 
for file in "$@"; do
    openssl x509 -checkend $(( 86400 * $DAYS )) -in "$file" > /dev/null
    if [ $? != 0 ]; then
        echo "==> Certificate $file is about to expire soon:"
        openssl x509 -enddate -in "$file" -noout
    fi
done

And the corresponding cronjob entry checking SSL certificates once a day:

MAILTO=root
6       6    * * *  nobody  /usr/local/bin/ssl-cert-check /etc/apache2/ssl/*.crt /etc/ssl/certs/dovecot.pem