#!/bin/bash
# We need to add a record in the DNS for postfixadmin
#
# For njalla domains login and go to domains > manage
#
# https://njal.la/
#
# Click the + add record to add an A record
#
# Enter:
#
# Type: A
# Name: postfixadmin
# IPv4 Address: 94.158.244.150 (WireGuard server dedicated static IP)
# After this script is run the user needs to open a URL and finish the PostfixAdmin setup at:
# https://postfixadmin.tuxmail.io/setup.php
# In our test setup we entered user1@tuxmail.com for the email addresss of the super admin, password penguin12
#
# To administer it:
# https://postfixadmin.tuxmail.io/main.php
# Set the initial variables we'll need for the script to execute
ROOT_MYSQL_PASSWORD="penguin"
POSTFIX_ADMIN_PASSWORD="penguin"
POSTFIX_ADMIN_SETUP_PASSWORD="penguin"
domain_name="tuxmail.io"
user1_email_address="user1"
user1_password="penguin12"
postfix_admin_download_url="https://github.com/postfixadmin/postfixadmin/archive/"
postfix_admin_version="3.3.14"
postfix_admin_download_file_name="postfixadmin-$postfix_admin_version.tar.gz"
postfix_super_admin_password="penguin12"
# Don't forget to repair partition before continuing
# Managing Mail: Installing Postfix Admin
apt update
apt install -y mariadb-server mariadb-client
# Start it at boot
systemctl enable mariadb
# Secure database with mysql_secure_installation
script -q -c 'mysql_secure_installation' <> "/var/www/postfixadmin/config.local.php"
<?php
\$CONF['configured'] = true;
\$CONF['database_type'] = 'mysqli';
\$CONF['database_host'] = 'localhost';
\$CONF['database_port'] = '3306';
\$CONF['database_user'] = 'postfixadmin';
\$CONF['database_password'] = '$POSTFIX_ADMIN_PASSWORD';
\$CONF['database_name'] = 'postfixadmin';
\$CONF['encrypt'] = 'dovecot:ARGON2I';
\$CONF['dovecotpw'] = "/usr/bin/doveadm pw -r 5";
if(@file_exists('/usr/bin/doveadm')) { // @ to silence openbase_dir stuff; see https://github.com/postfixadmin/postfixadmin/issues/171
\$CONF['dovecotpw'] = "/usr/bin/doveadm pw -r 5"; # debian
\$CONF['setup_password'] = password_hash("$POSTFIX_ADMIN_SETUP_PASSWORD", PASSWORD_DEFAULT);
}
EOF
# Create Apache Virtual Host Config File for PostfixAdmin
cat << EOF >> "/etc/apache2/sites-available/postfixadmin.conf"
ServerName postfixadmin.$domain_name
DocumentRoot /var/www/postfixadmin/public
ErrorLog ${APACHE_LOG_DIR}/postfixadmin_error.log
CustomLog ${APACHE_LOG_DIR}/postfixadmin_access.log combined
Options FollowSymLinks
AllowOverride All
Options FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
EOF
# Enable virtual host
a2ensite postfixadmin.conf
systemctl restart apache2
# Install PHP modules needed by PostfixAdmin
apt install -y php8.1-fpm php8.1-imap php8.1-mbstring php8.1-mysql php8.1-curl php8.1-zip php8.1-xml php8.1-bz2 php8.1-intl php8.1-gmp php8.1-redis libapache2-mod-php
# Restart apache2
systemctl restart apache2
# Enable PHP 8.1 FPM in Apache2
a2enmod proxy_fcgi setenvif
systemctl restart apache2
a2enconf php8.1-fpm
systemctl reload apache2
# Install certbot and certbot apache plugin
apt install -y certbot python3-certbot-apache
# Run this command to obtain and install TLS certificate
certbot --apache --agree-tos --redirect --hsts --staple-ocsp --email $user1_email_address@$domain_name -d postfixadmin.$domain_name
# Enable PostfixAdmin to read Dovecot statistics
cat << EOF >> "/etc/dovecot/conf.d/10-master.conf"
service stats {
unix_listener stats-reader {
user = www-data
group = www-data
mode = 0660
}
unix_listener stats-writer {
user = www-data
group = www-data
mode = 0660
}
}
EOF
# Add the web server to the dovecot group
gpasswd -a www-data dovecot
# Restart dovecot
systemctl restart dovecot
# Grant permissions to the www-data user
setfacl -R -m u:www-data:rwx /var/run/dovecot/stats-reader /var/run/dovecot/stats-writer
# Add a SuperAdmin user
/var/www/postfixadmin/scripts/postfixadmin-cli admin add user1_email_address@domain_name --superadmin 1 --active 1 --password "$postfix_super_admin_password" --password2 "$postfix_super_admin_password"
# Configure Postfix to use virtual mailbox domains, add MySQL map support for Postfix
apt install -y postfix-mysql
# Add the below to tell postfix where to find stuff
cat << EOF >> "/etc/postfix/main.cf"
virtual_mailbox_domains = proxy:mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf
virtual_mailbox_maps =
proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf,
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
virtual_alias_maps =
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf,
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf,
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
virtual_transport = lmtp:unix:private/dovecot-lmtp
EOF
# Create the sql directory
mkdir /etc/postfix/sql/
cat << EOF >> "/etc/postfix/sql/mysql_virtual_domains_maps.cf"
user = postfixadmin
password = $POSTFIX_ADMIN_PASSWORD
hosts = localhost
dbname = postfixadmin
query = SELECT domain FROM domain WHERE domain='%s' AND active = '1'
#query = SELECT domain FROM domain WHERE domain='%s'
#optional query to use when relaying for backup MX
#query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1'
#expansion_limit = 100
EOF
cat << EOF >> "/etc/postfix/sql/mysql_virtual_mailbox_maps.cf"
user = postfixadmin
password = $POSTFIX_ADMIN_PASSWORD
hosts = localhost
dbname = postfixadmin
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
#expansion_limit = 100
EOF
cat << EOF >> "/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf"
user = postfixadmin
password = $POSTFIX_ADMIN_PASSWORD
hosts = localhost
dbname = postfixadmin
query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'
EOF
cat << EOF >> "/etc/postfix/sql/mysql_virtual_alias_maps.cf"
user = postfixadmin
password = $POSTFIX_ADMIN_PASSWORD
hosts = localhost
dbname = postfixadmin
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'
#expansion_limit = 100
EOF
cat << EOF >> "/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf"
user = postfixadmin
password = $POSTFIX_ADMIN_PASSWORD
hosts = localhost
dbname = postfixadmin
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
EOF
cat << EOF >> "/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf"
# handles catch-all settings of target-domain
user = postfixadmin
password = $POSTFIX_ADMIN_PASSWORD
hosts = localhost
dbname = postfixadmin
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
EOF
# Plaintext passwords, so set readable only by user postfix and root only
chmod 0640 /etc/postfix/sql/*
setfacl -R -m u:postfix:rx /etc/postfix/sql/
# Remove the apex domain name from the list
postconf -e "mydestination = \$myhostname, localhost.\$mydomain, localhost"
cat << EOF >> "/etc/postfix/main.cf"
virtual_mailbox_base = /var/vmail
virtual_minimum_uid = 2000
virtual_uid_maps = static:2000
virtual_gid_maps = static:2000
EOF
# Restart postfix for changed to take effect
systemctl restart postfix
# Create a user named vmail with ID 2000 and a group with ID 2000
adduser vmail --system --group --uid 2000 --disabled-login --no-create-home
# Create mail directory location
mkdir /var/vmail/
# Make vmail as the owner
chown vmail:vmail /var/vmail/ -R
# Add MySQL support for Dovecot
apt install dovecot-mysql
# Add mail_home = /var/vmail/%d/%n/ just below mail_location = maildir:~/Maildir in /etc/dovecot/conf.d/10-mail.conf
sed '/^mail_location = maildir:~\/Maildir/a\
mail_home = \/var\/vmail\/%d\/%n\/' /etc/dovecot/conf.d/10-mail.conf > /tmp/10-mail.conf-2
mv /tmp/10-mail.conf-2 /etc/dovecot/conf.d/10-mail.conf
# Replace auth_username_format = %n with auth_username_format = %u
sed -i "s/auth_username_format = %n/auth_username_format = %u/g" /etc/dovecot/conf.d/10-auth.conf
# Replace #auth_default_realm = with auth_default_realm = example.com
sed -i "s/#auth_default_realm =/auth_default_realm = $(echo $domain_name) /g" /etc/dovecot/conf.d/10-auth.conf
# Uncomment #!include auth-sql.conf.ext
sed -i "s/#\!include auth-sql.conf.ext/\!include auth-sql.conf.ext/g" /etc/dovecot/conf.d/10-auth.conf
# Comment out !include auth-system.conf.ext so local Unix users can't send emails without registering email addresses in PostfixAdmin
sed -i "s/\!include auth-system.conf.ext/#\!include auth-system.conf.ext/g" /etc/dovecot/conf.d/10-auth.conf
# When a user tries to log in, Dovecot would use the Argon2 algorithm to generate a
# password hash from the password entered by the user, then compare it with the password hash stored in the database.
cat << EOF >> "/etc/dovecot/dovecot-sql.conf.ext"
driver = mysql
connect = host=localhost dbname=postfixadmin user=postfixadmin password=$POSTFIX_ADMIN_PASSWORD
default_pass_scheme = ARGON2I
password_query = SELECT username AS user,password FROM mailbox WHERE username = '%u' AND active='1'
user_query = SELECT maildir, 2000 AS uid, 2000 AS gid FROM mailbox WHERE username = '%u' AND active='1'
iterate_query = SELECT username AS user FROM mailbox
EOF
systemctl restart dovecot
# Add domain
/var/www/postfixadmin/scripts/postfixadmin-cli domain add $domain_name --mailboxes 0 --aliases 0 --active 1 --password-expiry 0
# Add user1
/var/www/postfixadmin/scripts/postfixadmin-cli mailbox add $user1_email_address@$domain_name --password "$user1_password" --password2 "$user1_password"
# Expunge old emails that have been placed in trash after two weeks
crontab -l | { cat; echo "@monthly setfacl -R -m u:www-data:rx /etc/letsencrypt/live/ /etc/letsencrypt/archive/ && doveadm expunge -A mailbox Junk savedbefore 2w"; } | crontab -
# Restrict which local users can send mail
cat << EOF >> "/etc/postfix/main.cf"
authorized_submit_users = root,www-data,vmail
EOF
# Restart postfix
systemctl restart postfix