Blog

Automatically encrypt emails using WKD and Postfix

January 16, 2022

Exchanging public PGP keys has always been hard to get right. Can you really be sure that the key you are fetching from a public keyserver over HKP belongs to the person you are trying to contact?

A relatively new way of distributing public keys called Web Key Directory (WKD) makes it easy to get a current public key for a given email address over HTTPS. The public keys are published under the .well-known directory on the domain name of the recipient (e.g. gauthier.dk/.well-known/openpgpkey/hu/[...]) for william@gauthier.dk). Because WKD leverages the HTTPS protocol and fetches the public key directly from the recipient's domain name, we can be quite certain that the key hasn't been tampered with and belongs to the recipient.

Protonmail has implemented external key discovery via WKD so that all outgoing emails to supported recipients are encrypted. I wanted something similar on my own email server, so I wrote a small Python script that encrypts all outgoing emails to recipients who have published their public key using WKD. This is a small guide on how to configure this on your own email server:

1. Create a dedicated system user and setup the Python script

First, we will create an unprivileged system account that the script will run under:

adduser --system wkd-user

Next, download the script and place it under the system user's home folder. And don't forget to make it executable:

wget https://raw.githubusercontent.com/wjgauthier/postfix-wkd/main/postfix-wkd.py -O /home/wkd-user/postfix-wkd.py
chmod +x /home/wkd-user/postfix-wkd.py

Although this is the default for gpg, I still like to explicitly state that we only want to fetch keys over WKD (unless it already exists locally). Set this in /home/wkd-user/.gnupg/gpg.conf:

auto-key-locate local,wkd

Install the required dependency:

apt install python3-gpg
2. Configure Postfix Milter

Now we have to setup a content filter using the Postfix pipe delivery agent, and then we configure an after-filter on localhost:10026 where emails will be sent back to after they have been processed by our script.

In /etc/postfix/master.cf, make the following additions/changes:


# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (no)    (never) (100)
# ==========================================================================
localhost:10026 inet  n       -       n       -       10      smtpd
    -o content_filter= 
    -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters
    -o smtpd_helo_restrictions=
    -o smtpd_client_restrictions=
    -o smtpd_sender_restrictions=
    # Postfix 2.10 and later: specify empty smtpd_relay_restrictions.
    -o smtpd_relay_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o smtpd_authorized_xforward_hosts=127.0.0.0/8
smtp      inet  n       -       y       -       -       smtpd
    -o content_filter=filter:dummy
	
[...]
filter unix - n n - 10 pipe flags=Rq user=wkd-user null_sender= argv=/home/wkd-user/wkd-postfix.py -f ${sender} -- ${recipient}

And then reload Postfix:

systemctl reload postfix

That's it. Everytime an email is sent, Postfix will try to lookup the public key of the recipient using WKD. A good way to test that everything is working is to send an email to a protonmail.com address that you own, as all public keys of their users are published in WKD. You should see a green padlock in the webmail interface.

***

I think the decentralized trust model where users bind a public key to its owner using the web of trust is a lost cause. WKD can make the encryption process almost completely invisible to the end user, which is probably the only way we will see widespread usage of PGP - if ever.

I would love to hear from you if you got the script working on your own email server or if you have any suggestions or questions. Feel free to reach out. My public key is published in WKD