Some questions about mail_crypt setups
Some questions about mail_crypt setups
I have global mail enecryption working nicely, and replication works nicely between two systems. The main problem is that the private and public keys are *right there* on the server in /etc/dovecot/private ... Fine for a completely controlled system, but not so fine when on a rented VPS etc.
When are the keys read in by dovecot ? Are they ever read in again while dovecot is running, or does it cache them in ram until dovecot is restarted ?
Would it be possible for dovecot to read the keys as output from a script ? I'm thinking of a small script that would reach out to an authentication service like Authy or Okta or similar. Admin gets an alert on their phone, taps OK, UNLOCK and the two keys are returned to the script, which then hands them back to dovecot and away it goes.
The mail_crypt config normally contains
mail_crypt_global_private_key = </etc/dovecot/private/dovecot_crypt_privkey mail_crypt_global_public_key = </etc/dovecot/private/dovecot_crypt_pubkey
Perhaps add another variable like
mail_crypt_global_script = </etc/dovecot/private/dovecot_crypt_script
That script would run and feed the two keys back into dovecot (no matter how it got to them).
So I started looking into per-user/per-folder encryption to see how that would work, and I have that setup nicely too. The config looks like this
# /etc/dovecot/conf.d/99-mailcrypt.conf #-------------------------------------- mail_attribute_dict = file:%h/Maildir/dovecot-attributes plugin { mail_crypt_require_encrypted_user_key = yes mail_crypt_save_version = 2 mail_crypt_curve = secp521r1 }
# /etc/dovecot/dovecot-sql.conf.ext #---------------------------------- # CREATE TABLE IF NOT EXISTS
users
( #username
varchar(64) character set utf8 collate utf8_bin NOT NULL COMMENT 'localpart of email-address', #domain
varchar(64) character set utf8 collate utf8_bin NOT NULL COMMENT 'domain-part of email-address', #name
varchar(64) character set utf8 collate utf8_bin NOT NULL COMMENT 'Full name of user', #password
varchar(128) character set utf8 collate utf8_bin NOT NULL COMMENT 'base64-encoded SHA512 hash of password', # PRIMARY KEY (username
,domain
) # ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='users';driver = mysql connect = host=/var/run/mysqld/mysqld.sock dbname=emailers user=dovecot password=RandomPassword default_pass_scheme = SHA512-CRYPT
password_query = SELECT username, password, '%w' AS userdb_mail_crypt_private_password, '/var/mail/%d/%n' AS userdb_home, 'vmail' AS userdb_uid, 'vmail' AS userdb_gid FROM users WHERE username = '%n' AND domain = '%d'
# For LDA: user_query = SELECT '/var/mail/%d/%n' AS home, 'vmail' AS uid, 'vmail' AS gid FROM users WHERE username = '%n' AND domain = '%d'
# For using doveadm -A: iterate_query = SELECT username, domain FROM users
Except that replication doesn't work due to the user password not being available. Actually, indexing fails too for the same reason.
Feb 21 14:02:13 indexer-worker(testuser@example.com)<120846><l8W9BzWuMmAK2AEAqNyxgw:MyoJDDWuMmAO2AEAqNyxgw>: Error: Mailbox INBOX: UID=1: read() failed: read(/var/mail/example.com/testuser/Maildir/INBOX/new/1613934133.M132059P120842.dove1,S=2568,W=2624) failed: Private key not available: Cannot decrypt key f64e7c12a60b3df12ebf865a70bec57fedd3e9b4fd98df93205f1096db14fda7: Cannot decrypt key eca099273f525ca46b2f5640253770ad19e0578543244d8cd34bde183e996bd5: Password not available (read reason=fts indexing)
Feb 21 14:02:13 indexer-worker(testuser@example.com)<120846><l8W9BzWuMmAK2AEAqNyxgw:MyoJDDWuMmAO2AEAqNyxgw>: Error: Failed to read mailbox INBOX mail UID=1 stream: Mailbox INBOX: UID=1: read() failed: read(/var/mail/example.com/testuser/Maildir/INBOX/new/1613934133.M132059P120842.dove1,S=2568,W=2624) failed: Private key not available: Cannot decrypt key f64e7c12a60b3df12ebf865a70bec57fedd3e9b4fd98df93205f1096db14fda7: Cannot decrypt key eca099273f525ca46b2f5640253770ad19e0578543244d8cd34bde183e996bd5: Password not available (read reason=fts indexing)
Feb 21 14:02:13 indexer-worker(testuser@example.com)<120846><l8W9BzWuMmAK2AEAqNyxgw:MyoJDDWuMmAO2AEAqNyxgw>: Error: Mailbox INBOX: Mail search failed: Internal error occurred. Refer to server log for more information. [2021-02-21 14:02:13]
Feb 21 14:02:13 indexer-worker(testuser@example.com)<120846><l8W9BzWuMmAK2AEAqNyxgw:MyoJDDWuMmAO2AEAqNyxgw>: Error: Mailbox INBOX: Transaction commit failed: FTS transaction commit failed: transaction context (attempted to index 1 messages (UIDs 1..1))
Feb 21 14:02:13 dsync-local(testuser@example.com)<TGxiDTWuMmAN2AEAqNyxgw>: Error: Mailbox INBOX: UID=1: read() failed: read(/var/mail/example.com/testuser/Maildir/INBOX/new/1613934133.M132059P120842.dove1,S=2568,W=2624) failed: Private key not available: Cannot decrypt key f64e7c12a60b3df12ebf865a70bec57fedd3e9b4fd98df93205f1096db14fda7: Cannot decrypt key eca099273f525ca46b2f5640253770ad19e0578543244d8cd34bde183e996bd5: Password not available (read reason=prefetch)
What are the options here for providing the decryption password or key ? The user password is already stored in the mysql database as a SHA512-CRYPT so we don't want to store it unencrypted ... I saw the mail_crypt docs (https://doc.dovecot.org/configuration_manual/mail_crypt_plugin/) mention
mail_crypt_private_key - Private key to decrypt user's master key, can be base64 encoded
I'm assuming that can just be a key generated with
openssl ecparam -name secp521r1 -genkey | openssl pkey
but that leaves the situation almost the same as with a global key - they key is local on the system, though a bit better in that it's not just visible there as a simple file, it can be buried in the database. One could retrieve it with
password_query = SELECT username, password, privkey AS userdb_mail_crypt_private_key, '/var/mail/%d/%n' AS userdb_home, 'vmail' AS userdb_uid, 'vmail' AS userdb_gid FROM users WHERE username = '%n' AND domain = '%d'
How are other people handling mail store encryption ?
-- Dean Carpenter deano is at areyes dot com 203 six oh four 6644
On Sun, Feb 21, 2021 at 05:20:59PM -0500, deano-dovecot@areyes.com wrote:
I have global mail enecryption working nicely, and replication works nicely between two systems. The main problem is that the private and public keys are *right there* on the server in /etc/dovecot/private ... Fine for a completely controlled system, but not so fine when on a rented VPS etc.
I'm not running a Dovecot instance myself at the moment, but I have been wondering about the above.
My current understanding is that Dovecot, like any other piece of software that needs to decrypt data from disk, will inevitably need to either:
keep the private keys in memory for at least *some* time, in order to be able to perform decryption using the CPU; OR
use an HSM (or equivalent, such as maybe a TPM or an OpenPGP Card) to perform decryption as needed.
In a case where there is no HSM (or equivalent), any attacker who gains root or hypervisor privileges over the machine can in principle extract the key from memory irrespective of whether the private key is on disk. They can then decrypt messages at their leisure. In such a case, the security is already quite low and little additional security is lost by keeping the private key in a local file on disk that is readable only by root (and perhaps also readable by one other carefully-chosen account if necessary).
The above applies to rented VPSes. You are vulnerable to the VPS provider, because they have hypervisor privileges. So, if you want the email store to be private, the first thing to do is have it on your own hardware.
In the better case where you have your own hardware, then the concern becomes: how to avoid attackers accessing the private keys if they gain root, or if they gain physical access. Here, an HSM (or equivalent) will help, by keeping the private keys off the filesystem and out of RAM/cache/etc. A properly-implemented HSM or smartcard will make it infeasible for an attacker to obtain the private key even if they gain root; and will make it expensive for an attacker to obtain the private key even if they gain physical access.
Can Dovecot utilise an HSM (or equivalent)? I'm not sure. I look forward to finding out.
Would it be possible for dovecot to read the keys as output from a script ? I'm thinking of a small script that would reach out to an authentication service like Authy or Okta or similar.
Making your own ability to access the email store dependent upon an untrustworthy third-party like Okta is, IMO, even worse than using a VPS. Not only are you leaving the door open to an attacker should that service provider prove to be either compromised or malicious; you also leave yourself vulnerable to a whole new class of DoS attacks.
(Okta is mostly security theatre. The basic premise is bad enough, but auditing various Okta deployments, and meeting and speaking with Okta technical staff, left me with an even worse impression of that company.)
Sam
-- A: When it messes up the order in which people normally read text. Q: When is top-posting a bad thing?
() ASCII ribbon campaign. Please avoid HTML emails & proprietary /\ file formats. (Why? See e.g. https://v.gd/jrmGbS ). Thank you.
On 22/02/2021 00:20 deano-dovecot@areyes.com wrote:
Some questions about mail_crypt setups I have global mail enecryption working nicely, and replication works nicely between two systems. The main problem is that the private and public keys are *right there* on the server in /etc/dovecot/private ... Fine for a completely controlled system, but not so fine when on a rented VPS etc. When are the keys read in by dovecot ? Are they ever read in again while dovecot is running, or does it cache them in ram until dovecot is restarted ? Would it be possible for dovecot to read the keys as output from a script ? I'm thinking of a small script that would reach out to an authentication service like Authy or Okta or similar. Admin gets an alert on their phone, taps OK, UNLOCK and the two keys are returned to the script, which then hands them back to dovecot and away it goes. The mail_crypt config normally contains
mail_crypt_global_private_key = </etc/dovecot/private/dovecot_crypt_privkey mail_crypt_global_public_key = </etc/dovecot/private/dovecot_crypt_pubkey Perhaps add another variable like mail_crypt_global_script = </etc/dovecot/private/dovecot_crypt_script That script would run and feed the two keys back into dovecot (no matter how it got to them).
So I started looking into per-user/per-folder encryption to see how that would work, and I have that setup nicely too. The config looks like this
# /etc/dovecot/conf.d/99-mailcrypt.conf #-------------------------------------- mail_attribute_dict = file:%h/Maildir/dovecot-attributes plugin { mail_crypt_require_encrypted_user_key = yes mail_crypt_save_version = 2 mail_crypt_curve = secp521r1 }
# /etc/dovecot/dovecot-sql.conf.ext #---------------------------------- # CREATE TABLE IF NOT EXISTS
users
( #username
varchar(64) character set utf8 collate utf8_bin NOT NULL COMMENT 'localpart of email-address', #domain
varchar(64) character set utf8 collate utf8_bin NOT NULL COMMENT 'domain-part of email-address', #name
varchar(64) character set utf8 collate utf8_bin NOT NULL COMMENT 'Full name of user', #password
varchar(128) character set utf8 collate utf8_bin NOT NULL COMMENT 'base64-encoded SHA512 hash of password', # PRIMARY KEY (username
,domain
) # ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='users'; driver = mysql connect = host=/var/run/mysqld/mysqld.sock dbname=emailers user=dovecot password=RandomPassword default_pass_scheme = SHA512-CRYPT password_query = SELECT username, password, '%w' AS userdb_mail_crypt_private_password, '/var/mail/%d/%n' AS userdb_home, 'vmail' AS userdb_uid, 'vmail' AS userdb_gid FROM users WHERE username = '%n' AND domain = '%d' # For LDA: user_query = SELECT '/var/mail/%d/%n' AS home, 'vmail' AS uid, 'vmail' AS gid FROM users WHERE username = '%n' AND domain = '%d' # For using doveadm -A: iterate_query = SELECT username, domain FROM usersExcept that replication doesn't work due to the user password not being available. Actually, indexing fails too for the same reason.
Feb 21 14:02:13 indexer-worker(testuser@example.com)<120846><l8W9BzWuMmAK2AEAqNyxgw:MyoJDDWuMmAO2AEAqNyxgw>: Error: Mailbox INBOX: UID=1: read() failed: read(/var/mail/example.com/testuser/Maildir/INBOX/new/1613934133.M132059P120842.dove1,S=2568,W=2624) failed: Private key not available: Cannot decrypt key f64e7c12a60b3df12ebf865a70bec57fedd3e9b4fd98df93205f1096db14fda7: Cannot decrypt key eca099273f525ca46b2f5640253770ad19e0578543244d8cd34bde183e996bd5: Password not available (read reason=fts indexing) Feb 21 14:02:13 indexer-worker(testuser@example.com)<120846><l8W9BzWuMmAK2AEAqNyxgw:MyoJDDWuMmAO2AEAqNyxgw>: Error: Failed to read mailbox INBOX mail UID=1 stream: Mailbox INBOX: UID=1: read() failed: read(/var/mail/example.com/testuser/Maildir/INBOX/new/1613934133.M132059P120842.dove1,S=2568,W=2624) failed: Private key not available: Cannot decrypt key f64e7c12a60b3df12ebf865a70bec57fedd3e9b4fd98df93205f1096db14fda7: Cannot decrypt key eca099273f525ca46b2f5640253770ad19e0578543244d8cd34bde183e996bd5: Password not available (read reason=fts indexing) Feb 21 14:02:13 indexer-worker(testuser@example.com)<120846><l8W9BzWuMmAK2AEAqNyxgw:MyoJDDWuMmAO2AEAqNyxgw>: Error: Mailbox INBOX: Mail search failed: Internal error occurred. Refer to server log for more information. [2021-02-21 14:02:13] Feb 21 14:02:13 indexer-worker(testuser@example.com)<120846><l8W9BzWuMmAK2AEAqNyxgw:MyoJDDWuMmAO2AEAqNyxgw>: Error: Mailbox INBOX: Transaction commit failed: FTS transaction commit failed: transaction context (attempted to index 1 messages (UIDs 1..1)) Feb 21 14:02:13 dsync-local(testuser@example.com)<TGxiDTWuMmAN2AEAqNyxgw>: Error: Mailbox INBOX: UID=1: read() failed: read(/var/mail/example.com/testuser/Maildir/INBOX/new/1613934133.M132059P120842.dove1,S=2568,W=2624) failed: Private key not available: Cannot decrypt key f64e7c12a60b3df12ebf865a70bec57fedd3e9b4fd98df93205f1096db14fda7: Cannot decrypt key eca099273f525ca46b2f5640253770ad19e0578543244d8cd34bde183e996bd5: Password not available (read reason=prefetch)
What are the options here for providing the decryption password or key ? The user password is already stored in the mysql database as a SHA512-CRYPT so we don't want to store it unencrypted ... I saw the mail_crypt docs (https://doc.dovecot.org/configuration_manual/mail_crypt_plugin/) mention
mail_crypt_private_key - Private key to decrypt user’s master key, can be base64 encoded I'm assuming that can just be a key generated with openssl ecparam -name secp521r1 -genkey | openssl pkey but that leaves the situation almost the same as with a global key - they key is local on the system, though a bit better in that it's not just visible there as a simple file, it can be buried in the database. One could retrieve it with password_query = SELECT username, password, privkey AS userdb_mail_crypt_private_key, '/var/mail/%d/%n' AS userdb_home, 'vmail' AS userdb_uid, 'vmail' AS userdb_gid FROM users WHERE username = '%n' AND domain = '%d' How are other people handling mail store encryption ?
Dean Carpenter deano is at areyes dot com 203 six oh four 6644
The primary use of mail_crypt plugin is to secure mail resting on insecure external storage, such as NFS share, object storage, etc.
You can also use it to secure mail with user's password, but this has several limitations, as you've noticed. Using replication with mail_crypt is one of these, especially if the keys are secured with password.
You can use various ways to configure mail_crypt plugin, the security of the system is directly in relation to your key management.
Securing keys with user's password (or derivation of it), can be done, but this will require some thought on how to manage those passwords.
Recently one solution used was to provide per-user global keypair, which is used to encrypt everything for a user. This can be easier than using the managed keys and encrypting the user's key with password.
Aki
On 2021-02-22 2:25 am, Aki Tuomi wrote:
On 22/02/2021 00:20 deano-dovecot@areyes.comwrote: Some questions about mail_crypt setups I have global mail encryption working nicely, and replication works nicely between two systems. The main problem is that the private and public keys are *right there* on the server in /etc/dovecot/private ... Fine for a completely controlled system, but not so fine when on a rented VPS etc. When are the keys read in by dovecot ? Are they ever read in again while dovecot is running, or does it cache them in ram until dovecot is restarted ? Would it be possible for dovecot to read the keys as output from a script ? I'm thinking of a small script that would reach out to an authentication service like Authy or Okta or similar. Admin gets an alert on their phone, taps OK, UNLOCK and the two keys are returned to the script, which then hands them back to dovecot and away it goes. The mail_crypt config normally contains mail_crypt_global_private_key = </etc/dovecot/private/dovecot_crypt_privkey mail_crypt_global_public_key = </etc/dovecot/private/dovecot_crypt_pubkey Perhaps add another variable like mail_crypt_global_script = </etc/dovecot/private/dovecot_crypt_script That script would run and feed the two keys back into dovecot (no matter how it got to them). So I started looking into per-user/per-folder encryption to see how that would work, and I have that setup nicely too. The config looks like this
Any thoughts about something like this for providing the keys ? That is, from a script. If the format is kept simple (eg. script must provide two keys, private first, then newline, then public, then newline) admins can come up with many variations on getting the keys into dovecot. Especially if they're only read once per dovecot invocation.
Recently one solution used was to provide per-user global keypair, which is used to encrypt everything for a user. This can be easier than using the managed keys and encrypting the user's key with password.
Any examples around ?
DC
participants (4)
-
Aki Tuomi
-
Dean Carpenter
-
deano-dovecot@areyes.com
-
Sam Kuper