Skip to content

Instantly share code, notes, and snippets.

@yajrendrag
Last active December 1, 2023 12:51
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yajrendrag/203b0172fee96a8b002a026362d27bf2 to your computer and use it in GitHub Desktop.
Save yajrendrag/203b0172fee96a8b002a026362d27bf2 to your computer and use it in GitHub Desktop.
postfixadmin guide for ISPMail

Background

I first came across PostfixAdmin upon implementing my first mailserver using this guide - https://www.exratione.com/2016/05/a-mailserver-on-ubuntu-16-04-postfix-dovecot-mysql/ and it's successor on Ubuntu 18.04. With the release of Ubuntu 20.04.1, i began looking around for an update and came upon the ISPMail guide as it had a few things I had wanted to try out - namely, rspamd, and encryption. But i wanted to be able to continue to use PostfixAdmin as i wanted to preserve the GUI administration of the database. So i began to implement the guide and figure out how i could integrate PostfixAdmin into it. This is the result.

Assumptions

  1. PostfixAdmin sets up the database schema (ie, no need to manually create the database tables with SQL)
  2. Presumption in guide below is that the Apache document root is /var/www/html
  3. For support of encryption, part 2 of guide below shows how to modify PostfixAdmin (& roundcube) to generate / modify user encryption keys, so once a user's account is setup, only the user can change their password via PostfixAdmin (or Roundcube) - an admin should not change a user's password any longer via the PostfixAdmin GUI or the emails will no longer be decrypted.

Basic PostfixAdmin Setup

Note i did this in Ubuntu vs Debian, & most packages & paths are the same - but, let me know if you find something that seems incorrect. You should substitute webmail.yourdomain (i.e., whatever your host's fqdn is) for all occurrences of webmail.example.org &/or substitute just yourdomain for example.org in a few instances.

  1. in step 1 the mv command should be mv postfixadmin-postfixadmin-3.2.4 postfixadmin
  2. in step 2, if following the paths in ISPMail guide, the symlink should be ln -s /srv/postfixadmin/public /var/www/html/postfixadmin
  3. in step 3, rather than follow the database setup from the postfixadmin guide, simply follow https://workaround.org/ispmail/buster/prepare-the-database/ but skip the adminer setup, and stop before creating the database tables - postfixadmin will setup the tables.
  4. You'll need some additional configuration in /srv/postfixadmin/config.local.php to add to what is shown in step 4:
$CONF['encrypt'] = 'dovecot:BLF-CRYPT';
$CONF['default_aliases'] = array (
'abuse' => 'abuse@example.org',
'hostmaster' => 'hostmaster@example.org',
'postmaster' => 'postmaster@example.org',
'webmaster' => 'webmaster@example.org');
$CONF['footer_text'] = 'Return to webmail.example.org';
$CONF['footer_link'] = 'https://webmail.example.org';
//refer to instructions in /srv/postfixadmin/config.inc.php to set Mailbox paths -
//$CONF['domain_path'] & $CONF['domain_in_mailbox'] - i used NO & YES, respectively.
//(which results in mailbox paths like: /var/vmail/domain/username/Maildir/... ie, username sans the @domainname)
$CONF['domain_path'] = 'NO';
$CONF['domain_in_mailbox'] = 'YES';
$CONF['create_mailbox_subdirs_prefix']=''; //(''for Dovecot)

I did not implement quotas, but if you wish to implement quotas, then per the instructions in /srv/postfixadmin/config.inc.php, you'll also need the following in /srv/postfixadmin/config.local.php & note that when you make an entry in postfixadmin for quota when setting up a new user, it's in MB:

$CONF['quota'] = 'YES';
$CONF['quota_multiplier'] = '1024000'; // You can either use '1024000' or '1048576'
$CONF['new_quota_table'] = 'YES'; //for postfix >= 1.2
// Optional:
// Show used quotas from Dovecot dictionary backend in virtual
// mailbox listing.
// See: DOCUMENTATION/DOVECOT.txt
//      http://wiki.dovecot.org/Quota/Dict
//
//$CONF['used_quotas'] = 'YES';
  1. in step 5 follow the instructions after browsing to https://webmail.example.org/postfixadmin/setup.php - you'll need to generate the superadmin password and hash and then re-edit /srv/postfixadmin/config.local.php and enter : $CONF['setup_password'] = 'your hashed password goes here'; - this can be at end of the file. (Note that this is not the email password for admin@example.org - you'll have to create that mailbox in addition to setting up the superadmin account).

You can now use postfixadmin - vist https://webmail.example.org/postfixadmin, login with the superadmin account that you generated, and create domains, mailboxes, & aliases. That will populate the mysql database you created above; you can use it to set up the example data in https://workaround.org/ispmail/buster/prepare-the-database/.

    1. virtual_mailbox_domains sql query: query = SELECT 1 FROM domain WHERE domain=’%s’
    2. virtual_mailbox_maps sql query: query = SELECT 1 FROM mailbox WHERE username=’%s’
    3. virtual_alias_maps query: query = SELECT goto FROM alias WHERE address=’%s’
    4. email2email sql query: query = SELECT username FROM mailbox WHERE username='%s'
    1. user_query = SELECT username as user, \
       concat('*:bytes=', quota) AS quota_rule, \
       '/var/vmail/%d/%n' AS home, \
       5000 AS uid, 5000 AS gid \
       FROM mailbox WHERE username='%u'
    2. password_query = SELECT password FROM mailbox WHERE username='%u'
    3. iterate_query = SELECT username AS user FROM mailbox
$config['password_query'] = "UPDATE mailbox SET password=%D WHERE username=%u";

That should be everything needed unless you wish to implement the optional encryption option. Users can browse to https://webmail.example.org/postfixadmin/users/login.php and login to change their password.


Additional PostfixAdmin/Roundcube Setup to Support ISPMail Optional Encryption

(Note: after 1-11-2021, postfixadmin appears to support the mail-crypt plug in, so steps 1-5 & 8 below may not be required any longer & the others only if you wish to use the command line functions &/or roundcube functions)

If continuing to setup encryption (https://workaround.org/ispmail/buster/optional-server-based-mailbox-encryption/), you'll want to continue with these additional modifications. These next modifications will update the mailserver database to accommodate the additional crypt field and will update postfixadmin to support that field. Additionally, we'll make postfixadmin automatically issue the doveadm commands needed to generate &/or update the user keys.

  1. Edit /srv/postfixadmin/config.local.php and add this to the end of the file:
function x_struct_mailbox_modify($struct) {
$struct['x_crypt'] = pacol(1, 1, 1, 'int', 'x_Crypt_Label', 'x_Crypt_Desc', 2, array(), 0, 0, "", "", "");
return $struct;
}
$CONF['mailbox_struct_hook'] = 'x_struct_mailbox_modify';
         
$CONF['mailbox_postdeletion_script'] = 'sudo -u root /usr/local/bin/postfixadmin-mailbox-postdeletion.sh';

This will enable postfixadmin to recognize and populate the crypt field in the mailserver database. New mailboxes created will be created with the crypt field set to 2 (if you wish to default to 0, then change 2 above to 0). The last line is used to delete the Maildir folder on the server when a user is deleted via the PostfixAdmin GUI.

  1. Edit /srv/postfixadmin/languages/en.lang and add these 2 lines just before the line $PALANG['please_keep_this_as_last_entry'] = ''; # needed for language-check.sh:
$PALANG['x_Crypt_Label'] = 'CRYPT';
$PALANG['x_Crypt_Desc'] = '2=Encrypt, 0=Read-Only';

(These provide descriptive text for PostfixAdmin's Add Mailbox GUI form)

  1. Edit the mailbox table to add the crypt field - starting from the shell:
  $ mysql
  MariaDB [(none)]> use mailserver;
  MariaDB [mailserver]> alter table mailbox add x_crypt int(11) default 2 after quota;
  ctl-d
  1. Edit /etc/dovecot/dovecot-sql.conf.ext and change the password query to:
password_query = SELECT password, x_crypt as userdb_mail_crypt_save_version, ‘%w’ AS userdb_mail_crypt_private_password FROM mailbox WHERE username=’%u’
  1. Edit /srv/postfixadmin/public/users/password.php and insert these lines in the if ($error == 0) block just before the exit(0); line:
/* added to update crypto password when user changes password */
system ('sudo /usr/bin/doveadm mailbox cryptokey password -u '.escapeshellarg($username).' -n '.escapeshellarg($fPassword).' -o '.escapeshellarg($fPassword_current));
  1. If using Roundcube, edit /usr/share/roundcube/plugins/password/password.php and insert these lines in the private function _save just after the case PASSWORD_SUCCESS: line:
/* added to update crypto password when user changes password */
system ('sudo /usr/bin/doveadm mailbox cryptokey password -u '.escapeshellarg(self::username()).' -n '.escapeshellarg($passwd).' -o '.escapeshellarg($curpass));
  1. If using Roundcube, edit /etc/roundcube/plugins/password/config.inc.php and add $config['password_confirm_current'] = true; to the configuration. Both the current and new passwords are needed when updating the cryptokey password.

  2. Edit /srv/postfixadmin/model/MailboxHandler.php and insert these lines just before the if (empty($cmd)) in the protected function mailbox_post_script():

/* Added to generate encryption key for newly created mailboxes  */
if ($this->new && !empty($this->values['password']) && !empty($this->id)) {
  system ('sudo /usr/bin/doveadm -o plugin/mail_crypt_private_password='.escapeshellarg($this->values['password']).' mailbox cryptokey generate -u '.escapeshellarg($this->id).' -U');
}
  1. Edit or create /etc/sudoers.d/local and add these lines:
www-data        ALL=(root) NOPASSWD: /usr/local/bin/postfixadmin-mailbox-postdeletion.sh
www-data        ALL=(root)      NOPASSWD:       /usr/bin/doveadm
www-data        ALL=(root)      NOPASSWD:       /usr/bin/rm

this will allow the apache2 user (www-data) to issue the specific commands above as root without requiring a password

Note that these last 5 steps enable automatic generation of the encryption key password or updating of same when a user changes their password using postfixadmin or rouudcube.

  1. Create a symlink to the postfixadmin-cli command:
ln -s /srv/postfixadmin/scripts/postfixadmin-cli /usr/local/bin/postfixadmin-cli
  1. Create /usr/local/bin/addmb with this content:
#!/bin/sh
#expects 4 arguments - in this order: email-address, password, name, quota (in  MB - enter 0 for unlimited)
#quote the password argument on the command line with single quotes to protect special characters from the shell, e.g., 'password'

/usr/local/bin/postfixadmin-cli mailbox add "$1" --password "$2" --password2 "$2" --name "$3" --active 1 --quota "$4"
/usr/bin/doveadm -o plugin/mail_crypt_private_password="$2" mailbox cryptokey generate -u "$1" -U
  1. make addmb executable:
chmod +x /usr/local/bin/addmb
  1. create /usr/local/bin/postfixadmin-mailbox-postdeletion.sh with this content:
#!/bin/sh

# userid in form of user@domain is argument 1
# domain is argument 2
if [ "$#" -eq 2 ]; then
  user=$(/usr/bin/echo "$1" | awk -F"@" '{ print $1 }')
  if [ -d "/var/vmail/$2/$user" ]; then
    sudo /usr/bin/rm  -rf /var/vmail/"$2"/"$user"/
  fi
else
  # you need domain and user@domain arguments to use this script
  exit 1
fi
exit 0

And make it executable with chmod +x /usr/local/bin/postfixadmin-mailbox-postdeletion.sh

(Note that you'll need to modify this script somewhat if you changed the $CONF['domain_path'] & CONF['domain_in_mailbox'] values in /srv/postfixadmin/config.local.php from that shown at the start of this guide. Be careful with this script and ensure you pass it the correct values and / or modify the checks on the arguments passed - i wiped out my complete vmail directory before adding checks on $#. Alternatively don't implement this script and leave out the last line in step 1 in this section and do any deletions manually.)

  1. reboot or minimally restart postfix and dovecot.

After these steps, you can create new mailboxes with an associated encryption key in one of two ways: (1) via the shell by logging in to server and issuing (as root or prefix with sudo):

addmb new-email-user@example.org 'passw0rd!' "first_name last_name" numerical-quota-value

or (2) by browsing to https://webmail.example.org/postfixadmin, logging in as the admin user you created above, and creating a new mailbox in your domain.

Either of these will create a new mailbox for the user with (referring to shell instructions) email address of new-email-user@example.org with password = passw0rd! and name of first_name last_name as well as generating the user key. The database will populate the new mailbox with the crypt field (x_crypt) = 2.

Users can then change their password with postfixadmin via GUI by visiting https://webmail.example.org/postfixadmin/users/login.php, logging in with their current password and clicking on change password. When they do, their user key will be updated automatically.

One CAVEAT - I don't use roundcube. If you use roundcube to change your password, you'll need to manually update encryption keys. I'll add a to-do item to try and figure out a way to execute a shell script that would allow automating encryption keys on a password change. The best way for users to change their passwords is to use postfixadmin as the enryption keys are updated automatically per above.

Users can also change their password using the roundcube password plugin based on the edit in steps 6 & 7 above.

Recommend testing before deploying to production - especially if you're adding encryption to a working server. Set the x_crypt value to 0 for all users but 1 test user for whom you can set x_crypt to 2. That way, if you run into issues, you can simply delete the test user.

TO DO:

  • Error checking on the lines inserted into few php files modified (will need help from someone more experienced in php)
  • Optionally add a new post-domain-deletion script similar to /usr/local/bin/postfixadmin-mailbox-postdeletion.sh
  • Automate encryption key update if roundcube is used to change user password. Done.
@BotoX
Copy link

BotoX commented Jan 3, 2021

Hi, I've opened an issue with my own patch on the postfixadmin GitHub: postfixadmin/postfixadmin#408
My patch: BotoX/postfixadmin@d88ed67
Doesn't support x_crypt though.

@yajrendrag
Copy link
Author

Thanks for your message... so without x-crypt, do you just enforce encryption for all users (so no need check something like x-crypt)? i'll follow along and see what they do with your issue... i might try it if i get some time. I have to imagine that one reason Dovecot considers per folder encryption to be non-production is because there's no recovery from a lost password; hopefully they figure out a way to do recovery similar to what looks like can be done with global keys. Regardless, would be good if PosfixAdmin can begin to support..

@BotoX
Copy link

BotoX commented Jan 4, 2021

Yeah I've set in my dovecot.conf:

mail_crypt_save_version = 2
mail_crypt_require_encrypted_user_key = yes

So there is only encrypted mail on my setup, unencrypted is not supported.

Forgetting your password with any kind of encryption should lead to permanent, unrecoverable loss of data.
Full disk encryption, etc. also comes with this. You lose your entire OS and data if you forget the password.
That's why you make backups of your data. For example in your local mail client, like Thunderbird, etc.
For my users (only a few friends) this is okay.

But yeah I will probably look into adding the x_crypt option too into the patch.
If postfixadmin wants to merge this feature, then it should be done properly with all of the possible options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment