diff --git a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md index c8e4532..24de61c 100644 --- a/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md +++ b/content/blog/mail_server_alpine_postfix_dovecot_tutorial/index.md @@ -1899,9 +1899,6 @@ Dovecot their own log files. You may want to get your domain whitelisted on [dnswl.org](https://www.dnswl.org/), an email whitelist service where admins can submit their domain and IP address to indicate trustworthiness. -Finally, you can [PGP-encrypt users' incoming mail with their public keys, much like -Protonmail](/blog/pgp_encrypting_all_incoming_emails/). - [^server_trust]: This is only true to the extent that your server is not compromised. You could say there's an order of diff --git a/content/blog/pgp_encrypting_all_incoming_emails.md b/content/blog/pgp_encrypting_all_incoming_emails.md deleted file mode 100644 index d7bdeb9..0000000 --- a/content/blog/pgp_encrypting_all_incoming_emails.md +++ /dev/null @@ -1,257 +0,0 @@ -+++ -date = '2024-12-13T16:06:23Z' -draft = false -title = 'PGP Encrypting All (Incoming) Emails' -tags = ['mail server', 'alpine linux', 'dovecot', 'sieve'] -+++ - -Let's say we want emails on our mail server to be encrypted at rest, such that only the user has the key. Luckily, -there already exists a popular solution for encrypting emails such that only the recipient can read them: OpenPGP. - -Using [Dovecot Sieve scripts](https://doc.dovecot.org/main/core/plugins/sieve.html), we can easily PGP-encrypt all -incoming email for a user. - -A lot of people have done this before, and I didn't come up with the idea. Please see the [Further -reading](#further-reading) section for some recommended articles I referred to. - -The only prerequisite is an existing Dovecot server set up and running. This guide will be 100% compatible with [my -mail server guide](/blog/mail_server_alpine_postfix_dovecot_tutorial/). This guide assumes you are using system users -as mail users, and may require changes if you are using virtual users. - -This instructions should be distro-agnostic, though it was written for an Alpine Linux server. I think the only -Alpine-specific part should be how to install the required packages, which can just be replaced by the relevant command -for your distro's package manager.[^alpine_searchability] - -This is compatible with Dovecot's mail\_crypt plugin, because mail\_crypt's encryption is transparent to the user. - -Finally, this only encrypts incoming mail, because Sieve scripts aren't applied to outgoing mail. - -# Admin guide - -Install Pigeonhole (a Sieve implementation for Dovecot): - - # apk add dovecot-pigeonhole-plugin - -Set Dovecot to use Pigeonhole. Edit `/etc/dovecot/conf.d/20-lmtp.conf`: - -```conf -protocol lmtp { - mail_plugins = $mail_plugins sieve -} -``` - -If you use LDA, you should do: - -```conf -protocol lda { - mail_plugins = $mail_plugins sieve -} -``` - -Now set Dovecot to use the `sieve_extprograms` Sieve plugin. This allows Sieve to run external executables. Don't -worry; it won't allow users to execute arbitrary executables, but only executables you specify. - -`sieve_extprograms` may come installed with Pigeonhole, or you may have to install it separately. For me, my -`/etc/dovecot/conf.d/90-sieve.conf` contains a comment that states: - -> The sieve\_extprograms plugin is included in this release. - -To enable `sieve_extprograms`, anywhere in your Dovecot config (I put it in `/etc/dovecot/conf.d/90-sieve.conf`): - -```conf -plugin { - sieve_plugins = sieve_extprograms - sieve_extensions = +vnd.dovecot.filter - sieve_filter_bin_dir = /etc/dovecot/sieve-filters -} -``` - -We add `vnd.dovecot.filter` to the list of Sieve extensions, to allow users to use the `filter` Sieve command. A filter -is an executable that takes an email from stdin, performs an action on it, and outputs the modified email to stdout. - -By specifying `sieve_filter_bin_dir`, we are saying that we will place any Sieve filters in -`/etc/dovecot/sieve-filters`. - -> WARNING! -> -> Users can execute *any executable* you place in `/etc/dovecot/sieve-filters`. Only put executables you trust in -> there! - -Sieve filters will be executed with the following environment variables, and *only* the following environment -variables: - -* `HOME` -* `USER` -* `SENDER` -* `RECIPIENT` -* `ORIG_RECIPIENT` - -They can take one argument specified by the user in their Sieve script. - -Now let's add the Sieve filter itself. This can be any executable that takes an email from stdin and outputs the -PGP-encrypted email from stdout. - -[Here's one written in Perl](https://gitlab.com/mikecardwell/gpgit). I had trouble with installing the Perl -dependencies on Alpine, so I ended up using [a Python script by Julian Andres -Klode](https://github.com/julian-klode/ansible.jak-linux.org/blob/dovecot/roles/mailserver/files/usr/local/lib/dovecot-sieve-filters/gpgmymail). - -On Alpine Linux, to use this script, you should install [python-gnupg](https://gnupg.readthedocs.io/en/latest/) as: - - # apk add py3-gnupg - -Now place the executable you want to use in `/etc/dovecot/sieve-filters` and make it executable (`chmod +x`). You can -see the version of Julian Andres Klode's script I use on my server -[here](https://git.revsuine.xyz/revsuine/gpgmymail). - -You also need to make sure that users can set their own personal Sieve scripts. You can set: - -```conf -plugin { - sieve = ~/.dovecot.sieve -} -``` - -to make it so that a user's Sieve script would be at `~/.dovecot.sieve`. You can also set - -```conf -plugin { - sieve = file:~/sieve;active=~/.dovecot.sieve -} -``` - -so that `~/sieve/` is a directory full of sieve scripts, and the active one is symlinked at `~/.dovecot.sieve`. See -[Dovecot docs on Sieve script locations](https://doc.dovecot.org/main/core/plugins/sieve.html#script-locations), or [my -further explanation of the `sieve = file:~/sieve;active=~/.dovecot.sieve` example on my previous blog -post](/blog/mail_server_alpine_postfix_dovecot_tutorial/#installing-and-setting-up-pigeonhole). - -Restart Dovecot for your changes to take effect: - - # rc-service dovecot restart - -You are now done from the admin side of things. - -# User guide - -In order for gpgmymail (the script linked above) to have the user's public PGP key, they need to import it to their -system GnuPG keyring. If they have shell access, - - user@localhost $ gpg --export --armor user@revsuine.xyz > public.asc - user@localhost $ scp public.asc user@revsuine.xyz:~/public.asc - user@localhost $ ssh user@revsuine.xyz - user@revsuine.xyz $ gpg --import ~/public.asc - -Or you could copy and paste the ASCII armored public key into an SSH shell, etc. - -> ==***DO NOT PUT YOUR PRIVATE KEY ON THE SERVER!***== -> -> Not only is this a security hole, but this also entirely defeats the point of this setup, which is designed to -> protect against an attacker who gains full disk access to the mail server from reading your emails. If the private -> key is stored on the server, this attacker with full disk access will have the key to decrypt and read your emails. - -Because `$HOME` and `$USER` are included in a Sieve filter's environment, python-gnupg can see the public keys in the -user's personal GPG keyring. - -If the user doesn't have shell access, they need to send their public key to a server admin who can run `gpg --import` -as their user (e.g. with `doas -u`). - -You also need to mark the public key as trusted so that GPG doesn't refuse to encrypt data with the key: - - user@revsuine.xyz $ gpg --edit-key user@revsuine.xyz - -Then enter `trust`, select `5`, enter `y`, then enter `save`: - - gpg> trust - 1 = I don't know or won't say - 2 = I do NOT trust - 3 = I trust marginally - 4 = I trust fully - 5 = I trust ultimately - m = back to the main menu - Your decision? 5 - Do you really want to set this key to ultimate trust? (y/N) y - gpg> save - -Now the user needs to create or amend their Sieve script. A minimal Sieve script could be - -```sieve -require "vnd.dovecot.filter"; - -filter "gpgmymail" "user@revsuine.xyz"; -``` - -Note that `filter` commands need to go *before* `fileinto` commands for them to take effect. - -If your Sieve filter is named something else, replace `gpgmymail` with the name of your script (relative to -`/etc/dovecot/sieve-filters/`). - -Your Sieve filter does not need to implement behaviour such as "don't encrypt emails from `domain.com`", because this -is exactly what Sieve scripting is for. If you want to apply conditions to encrypting mail, do it with Sieve, e.g. - -```sieve -require "vnd.dovecot.filter"; - -if not address :is :domain "from" ["revsuine.xyz", "gmail.com"] { - filter "gpgmymail" "user@revsuine.xyz"; -} -``` - -# Assessment - -Given a trusted server admin to implement this, and not spy on emails prior to them passing through the Sieve filter, -this solution protects against an attacker who can read the full disk of the server, as stated previously. Potential -threats this defends against include seizure of the server by law enforcement who bypass full disk encryption, or a VPS -host who reads the FDE key from RAM and reads the disk contents; essentially, -any instance of a third party who gains full disk access. - -This does not do much to protect against a server admin who is intent upon reading their users' emails, because the -email is unencrypted the whole time it moves through the Postfix queue. At the end of the day, there is really nothing -at all that can be done to stop a server admin from reading something that arrives at the server unencrypted, such as -an unencrypted email. - -This solution is an improvement over services such as Protonmail or Tuta, because unlike with Protonmail, users of a -mail server with this Sieve filter do not have to have their private keys stored on the server. Protonmail, assuming an -entirely non-technical user, manages PGP keys for the user, and therefore generates and stores them server-side. -However, with our solution, emails become encrypted on the server, but only get decrypted on the user's local machine. -Their public key is stored on the server, but not their private key. Also, unlike Protonmail and Tuta, this solution -works out-of-the-box with IMAP or POP3, not requiring a bridge like Protonmail does. - -As mentioned at the start, outgoing mail is still stored unencrypted on the server, unless the user has encrypted it -themselves (e.g. with PGP). - -This solution will also make it impossible to search for message contents, as they are all encrypted. If you also -encrypt subject lines, you can essentially only search for emails by sender or date. - -This solution shouldn't break spam filters if they are [integrated with your MTA](https://cwiki.apache.org/confluence/display/SPAMASSASSIN/IntegratedInMta), -but if your spam filter happens after Sieve filtering for some reason (likely only if your spam filter is client-side), -it obviously won't work because the message contents are encrypted and unreadable to a spam filter. Modifying the email -in this way also renders DKIM signing invalid, but DKIM validation should be integrated with your MTA, in which case -you'll still have an email header indicating DKIM status prior to encryption. - -## Compatibility - -[OpenKeychain](https://www.openkeychain.org/) fails to decrypt emails encrypted this way. As far as I can tell, the -only way these emails can be read on Android is by using Termux to decrypt emails with GnuPG, i.e. not with any -conventional Android IMAP client. - -[Desktop Thunderbird](https://www.thunderbird.net/) seems to have an issue rendering quoted-printable or base64-encoded -emails encrypted this way, however I've had no problem with [GNOME Evolution](https://wiki.gnome.org/Apps/Evolution/). -I haven't tested with other desktop email clients. - -Because all gpgmymail does is, essentially, encrypt emails with a Python wrapper for GnuPG, any desktop email client -that uses GPG to decrypt PGP-encrypted emails should be able to read gpgmymail-encrypted emails. When you run -`gpg --decrypt` on a gpgmymail'ed email, you will see the email headers twice, and then the email as it was prior to -being gpgmymail'ed. You could easily not have a client render your email, and just read the raw decrypted email with -`gpg --decrypt`. - -# Further reading - -In order: - -1. https://www.grepular.com/Automatically_Encrypting_all_Incoming_Email -2. https://perot.me/encrypt-specific-incoming-emails-using-dovecot-and-sieve -3. https://blog.jak-linux.org/2019/06/13/encrypted-email-storage/ -4. https://github.com/julian-klode/ansible.jak-linux.org/blob/dovecot/roles/mailserver/files/usr/local/lib/dovecot-sieve-filters/gpgmymail - -[^alpine_searchability]: Tagged [#alpine linux](/tags/alpine-linux/) for findability when searching. - diff --git a/hugo.toml b/hugo.toml index 8e97826..138a814 100644 --- a/hugo.toml +++ b/hugo.toml @@ -18,10 +18,6 @@ theme = 'terminal' # start by listing h1s startLevel = 1 [markup.goldmark] - [markup.goldmark.extensions] - [markup.goldmark.extensions.extras] - [markup.goldmark.extensions.extras.mark] - enable = true [markup.goldmark.renderer] unsafe = true