Files
obsidian/tank/programming/Mailserver setup/Complete Mail Server Setup Guide.md
2026-05-07 14:39:10 +02:00

1555 lines
28 KiB
Markdown
Executable File

---
id: Complete Mail Server Setup Guide
aliases: []
tags: []
---
# Complete Mail Server Setup Guide - Arch Linux Edition
## Postfix + Dovecot + MySQL + Notmuch + Encryption on Arch Linux LXC
This is the Arch Linux version of the complete mail server setup.
---
## Part 1: Proxmox LXC Container Setup for Arch Linux
### Download Arch Linux Template
In Proxmox shell:
```bash
pveam update
pveam available | grep arch
pveam download local archlinux-base_20231118-1_amd64.tar.zst
```
Or download manually from: https://uk.lxd.images.canonical.com/images/archlinux/current/amd64/default/
### Create the LXC Container
In Proxmox UI:
- **CT ID**: Choose available ID
- **Template**: archlinux-base
- **Disk**: 20GB minimum
- **CPU**: 2 cores
- **RAM**: 2GB minimum
- **Network**: Bridge, static IP (e.g., 192.168.1.100)
- **Unprivileged**: Yes
- **Features**: `nesting=1` (if you want Docker option later)
Start the container and note the IP address.
### Initial Container Setup
SSH into your Arch LXC container:
```bash
# Initialize pacman keyring (Arch specific)
pacman-key --init
pacman-key --populate archlinux
# Update system
pacman -Syu --noconfirm
# Install essential tools
pacman -S --noconfirm base-devel vim wget curl git sudo openssh
# Set hostname
hostnamectl set-hostname mail.example.com
echo "192.168.1.100 mail.example.com mail" >> /etc/hosts
# Enable time sync
systemctl enable --now systemd-timesyncd
```
### Configure Locale
```bash
# Uncomment your locale in /etc/locale.gen
vim /etc/locale.gen
# Uncomment: en_US.UTF-8 UTF-8
# Generate locale
locale-gen
# Set locale
echo "LANG=en_US.UTF-8" > /etc/locale.conf
```
---
## Part 2: MySQL/MariaDB Database Setup
### Install MariaDB
```bash
pacman -S --noconfirm mariadb mariadb-clients
```
### Initialize MariaDB
```bash
# Initialize the database
mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
# Start and enable MariaDB
systemctl enable --now mariadb
# Secure installation
mysql_secure_installation
```
During secure installation:
- Set root password: Yes (choose strong password)
- Remove anonymous users: Yes
- Disallow root login remotely: Yes
- Remove test database: Yes
- Reload privilege tables: Yes
### Create Mail Database and Tables
```bash
mysql -u root -p
```
In MySQL prompt:
```sql
CREATE DATABASE mailserver CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'mailuser'@'localhost' IDENTIFIED BY '1ChagemaiL';
GRANT ALL PRIVILEGES ON mailserver.* TO 'mailuser'@'localhost';
FLUSH PRIVILEGES;
USE mailserver;
-- Domains table
CREATE TABLE IF NOT EXISTS virtual_domains (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Users table
CREATE TABLE IF NOT EXISTS virtual_users (
id INT NOT NULL AUTO_INCREMENT,
domain_id INT NOT NULL,
password VARCHAR(200) NOT NULL,
email VARCHAR(120) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY email (email),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Aliases table
CREATE TABLE IF NOT EXISTS virtual_aliases (
id INT NOT NULL AUTO_INCREMENT,
domain_id INT NOT NULL,
source VARCHAR(100) NOT NULL,
destination VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Add your domain
INSERT INTO virtual_domains (name) VALUES ('liphlink.xyz');
-- Verify
SELECT * FROM virtual_domains;
EXIT;
```
**Save your database password securely!**
---
## Part 3: Postfix Installation and Configuration
### Install Postfix
```bash
pacman -S --noconfirm postfix postfix-mysql
```
### Backup Original Configuration
```bash
cp /etc/postfix/main.cf /etc/postfix/main.cf.orig
```
### Configure Postfix Main Settings
Edit `/etc/postfix/main.cf`:
```bash
vim /etc/postfix/main.cf
```
Replace with:
```
# Basic Settings
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = localhost.$mydomain, localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = ipv4
# Compatibility with Dovecot
compatibility_level = 3.6
# Virtual Mailbox Settings
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf
# Virtual transport
virtual_transport = lmtp:unix:private/dovecot-lmtp
# Mailbox Location
virtual_mailbox_base = /var/mail/vhosts
virtual_minimum_uid = 100
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
# SMTP-AUTH Settings
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes
# TLS Settings
smtpd_tls_cert_file=/etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_use_tls=yes
smtpd_tls_auth_only = yes
smtpd_tls_security_level = may
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_ciphers = high
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_ciphers = high
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# SMTP Restrictions
smtpd_recipient_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination,
reject_invalid_hostname,
reject_non_fqdn_hostname,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_unknown_sender_domain,
reject_unknown_recipient_domain,
reject_rbl_client zen.spamhaus.org,
reject_rbl_client bl.spamcop.net,
permit
smtpd_helo_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_invalid_helo_hostname,
reject_non_fqdn_helo_hostname,
reject_unknown_helo_hostname,
permit
smtpd_sender_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_non_fqdn_sender,
reject_unknown_sender_domain,
permit
# Milter settings (for DKIM)
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
# Additional Settings
disable_vrfy_command = yes
smtpd_helo_required = yes
message_size_limit = 52428800
```
### Create MySQL Connection Files
**1. Virtual Domains**
```bash
vim /etc/postfix/mysql-virtual-mailbox-domains.cf
```
```
user = mailuser
password = your_strong_password_here
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_domains WHERE name='%s'
```
**2. Virtual Mailboxes**
```bash
vim /etc/postfix/mysql-virtual-mailbox-maps.cf
```
```
user = mailuser
password = your_strong_password_here
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_users WHERE email='%s'
```
**3. Virtual Aliases**
```bash
vim /etc/postfix/mysql-virtual-alias-maps.cf
```
```
user = mailuser
password = your_strong_password_here
hosts = 127.0.0.1
dbname = mailserver
query = SELECT destination FROM virtual_aliases WHERE source='%s'
```
### Secure MySQL Configuration Files
```bash
chmod 640 /etc/postfix/mysql-*.cf
chown root:postfix /etc/postfix/mysql-*.cf
```
### Configure Postfix Master.cf
Edit `/etc/postfix/master.cf`:
```bash
vim /etc/postfix/master.cf
```
Add these lines at the end:
```
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_tls_auth_only=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
smtps inet n - n - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
```
### Create Mail Directory Structure
```bash
# Create vmail user and group
groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /var/mail/vhosts -m -s /bin/bash
# Create mail directory
mkdir -p /var/mail/vhosts/example.com
chown -R vmail:vmail /var/mail/vhosts
chmod -R 770 /var/mail/vhosts
```
### Test MySQL Connectivity
```bash
postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
```
Should return `1` if working.
### Initialize Postfix Aliases
```bash
newaliases
postmap /etc/postfix/virtual
```
---
## Part 4: Dovecot Installation and Configuration
### Install Dovecot
```bash
pacman -S --noconfirm dovecot pigeonhole
```
### Main Dovecot Configuration
Edit `/etc/dovecot/dovecot.conf`:
```bash
vim /etc/dovecot/dovecot.conf
```
Replace with:
```
protocols = imap lmtp sieve
listen = *, ::
dict {
}
!include conf.d/*.conf
!include_try local.conf
```
### Configure Mail Location
Edit `/etc/dovecot/conf.d/10-mail.conf`:
```bash
vim /etc/dovecot/conf.d/10-mail.conf
```
Set these values:
```
mail_location = maildir:/var/mail/vhosts/%d/%n
mail_privileged_group = vmail
namespace inbox {
inbox = yes
mailbox Drafts {
special_use = \Drafts
}
mailbox Junk {
special_use = \Junk
}
mailbox Trash {
special_use = \Trash
}
mailbox Sent {
special_use = \Sent
}
}
first_valid_uid = 5000
last_valid_uid = 5000
first_valid_gid = 5000
last_valid_gid = 5000
```
### Configure Authentication
Edit `/etc/dovecot/conf.d/10-auth.conf`:
```bash
vim /etc/dovecot/conf.d/10-auth.conf
```
Modify:
```
disable_plaintext_auth = yes
auth_mechanisms = plain login
# Comment out
#!include auth-system.conf.ext
# Uncomment
!include auth-sql.conf.ext
```
### SQL Authentication Backend
Edit `/etc/dovecot/conf.d/auth-sql.conf.ext`:
```bash
vim /etc/dovecot/conf.d/auth-sql.conf.ext
```
```
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = static
args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}
```
### SQL Connection Configuration
Create `/etc/dovecot/dovecot-sql.conf.ext`:
```bash
vim /etc/dovecot/dovecot-sql.conf.ext
```
```
driver = mysql
connect = host=127.0.0.1 dbname=mailserver user=mailuser password=your_strong_password_here
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';
user_query = SELECT email as user, 'maildir:/var/mail/vhosts/%d/%n' as mail, 5000 AS uid, 5000 AS gid FROM virtual_users WHERE email='%u';
iterate_query = SELECT email AS username FROM virtual_users;
```
Secure it:
```bash
chown root:root /etc/dovecot/dovecot-sql.conf.ext
chmod 600 /etc/dovecot/dovecot-sql.conf.ext
```
### Master Services Configuration
Edit `/etc/dovecot/conf.d/10-master.conf`:
```bash
vim /etc/dovecot/conf.d/10-master.conf
```
Configure services:
```
service imap-login {
inet_listener imap {
port = 143
}
inet_listener imaps {
port = 993
ssl = yes
}
}
service pop3-login {
inet_listener pop3 {
port = 110
}
inet_listener pop3s {
port = 995
ssl = yes
}
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
service imap {
}
service pop3 {
}
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
unix_listener auth-userdb {
mode = 0600
user = vmail
group = vmail
}
user = dovecot
}
service auth-worker {
user = vmail
}
service dict {
unix_listener dict {
}
}
```
### SSL Configuration
Edit `/etc/dovecot/conf.d/10-ssl.conf`:
```bash
vim /etc/dovecot/conf.d/10-ssl.conf
```
```
ssl = required
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
ssl_min_protocol = TLSv1.2
ssl_cipher_list = ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
ssl_prefer_server_ciphers = yes
```
### Sieve Configuration
Edit `/etc/dovecot/conf.d/90-sieve.conf`:
```bash
vim /etc/dovecot/conf.d/90-sieve.conf
```
```
plugin {
sieve = file:~/sieve;active=~/.dovecot.sieve
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
}
```
### Set Permissions
```bash
chown -R vmail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot
```
---
## Part 5: SSL Certificates with Let's Encrypt
### Install Certbot
```bash
pacman -S --noconfirm certbot
```
### Obtain Certificate
Ensure ports 80 and 443 are accessible:
```bash
certbot certonly --standalone -d mail.example.com
```
Follow prompts and enter your email.
### Set Up Auto-Renewal
Create systemd timer:
```bash
systemctl enable --now certbot-renew.timer
```
Create renewal hook:
```bash
mkdir -p /etc/letsencrypt/renewal-hooks/post
vim /etc/letsencrypt/renewal-hooks/post/reload-mail-services.sh
```
```bash
#!/bin/bash
systemctl reload postfix
systemctl reload dovecot
```
Make executable:
```bash
chmod +x /etc/letsencrypt/renewal-hooks/post/reload-mail-services.sh
```
---
## Part 6: OpenDKIM Setup
### Install OpenDKIM
```bash
pacman -S --noconfirm opendkim
```
### Configure OpenDKIM
Edit `/etc/opendkim/opendkim.conf`:
```bash
vim /etc/opendkim/opendkim.conf
```
```
AutoRestart Yes
AutoRestartRate 10/1h
UMask 002
Syslog yes
SyslogSuccess Yes
LogWhy Yes
Canonicalization relaxed/simple
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
Mode sv
PidFile /run/opendkim/opendkim.pid
SignatureAlgorithm rsa-sha256
UserID opendkim:mail
Socket inet:8891@localhost
```
### Create Directory Structure
```bash
mkdir -p /etc/opendkim/keys/example.com
chown -R opendkim:mail /etc/opendkim
chmod -R 700 /etc/opendkim/keys
```
# Create Configuration Files
**TrustedHosts**:
```bash
vim /etc/opendkim/TrustedHosts
```
```
127.0.0.1
localhost
192.168.1.0/24
*.example.com
```
**KeyTable**:
```bash
vim /etc/opendkim/KeyTable
```
```
mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/example.com/mail.private
```
**SigningTable**:
```bash
vim /etc/opendkim/SigningTable
```
```
*@example.com mail._domainkey.example.com
```
### Generate DKIM Keys
```bash
cd /etc/opendkim/keys/liphlink.xyz
opendkim-genkey -s mail -d liphlink.xyz
chown opendkim:mail mail.private
chmod 600 mail.private
```
### Get Public Key for DNS
```bash
cat /etc/opendkim/keys/liphlink.xyz/mail.txt
```
Save this for your DNS records.
### Start OpenDKIM
```bash
systemctl enable --now opendkim
```
---
## Part 7: Start and Enable Services
### Start All Services
```bash
# Create postfix spool directory for dovecot
mkdir -p /var/spool/postfix/private
chown postfix:postfix /var/spool/postfix/private
# Start services
systemctl enable --now postfix
systemctl enable --now dovecot
systemctl enable --now opendkim
# Check status
systemctl status postfix
systemctl status dovecot
systemctl status opendkim
```
All should show `active (running)`.
---
## Part 8: Notmuch Integration
### Install Notmuch
```bash
pacman -S --noconfirm notmuch
```
### Configure Notmuch for vmail User
```bash
# Switch to vmail user
su - vmail
cd /var/mail/vhosts/liphlink.xyz
# Create user directory if doesn't exist
mkdir -p phil
cd phil
# Initialize notmuch
notmuch setup
```
Prompts:
- Your full name: User Name
- Primary email: user@example.com
- Maildir location: /var/mail/vhosts/example.com/user
- Tags for new messages: unread;inbox
- Tags to exclude: deleted;spam
Index existing mail:
```bash
notmuch new
exit # Exit vmail user
```
### Create Indexing Script
```bash
vim
```
```bash
#!/bin/bash
for domain in /var/mail/vhosts/*; do
if [ -d "$domain" ]; then
for user in "$domain"/*; do
if [ -d "$user" ]; then
cd "$user" || continue
if [ ! -d ".notmuch" ]; then
su -s /bin/bash vmail -c "cd '$user' && notmuch new"
else
su -s /bin/bash vmail -c "cd '$user' && notmuch new"
fi
fi
done
fi
done
```
Make executable:
```bash
chmod +x /usr/local/bin/notmuch-index-all.sh
```
### Set Up Automatic Indexing
Create systemd service:
```bash
vim /etc/systemd/system/notmuch-index.service
```
```ini
[Unit]
Description=Notmuch email indexing
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/notmuch-index-all.sh
User=root
[Install]
WantedBy=multi-user.target
```
Create timer:
```bash
vim /etc/systemd/system/notmuch-index.timer
```
```ini
[Unit]
Description=Run notmuch indexing every 5 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
```
Enable:
```bash
systemctl daemon-reload
systemctl enable --now notmuch-index.timer
```
### Configure Dovecot for Real-time Indexing
```bash
vim /usr/local/bin/notmuch-index-single
```
```bash
#!/bin/bash
if [ -n "$HOME" ]; then
cd "$HOME" || exit 0
/usr/bin/notmuch new > /dev/null 2>&1
fi
```
Make executable:
```bash
chmod +x /usr/local/bin/notmuch-index-single
```
Create global sieve:
```bash
mkdir -p /var/mail/vhosts/sieve
vim /var/mail/vhosts/sieve/after.sieve
```
```
require ["vnd.dovecot.pipe", "copy", "environment"];
pipe :copy "notmuch-index-single";
```
Compile:
```bash
sievec /var/mail/vhosts/sieve/after.sieve
chown -R vmail:vmail /var/mail/vhosts/sieve
```
Update Dovecot config:
```bash
vim /etc/dovecot/conf.d/90-sieve.conf
```
Add:
```
plugin {
sieve = file:~/sieve;active=~/.dovecot.sieve
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
sieve_after = /var/mail/vhosts/sieve/after.sieve
}
```
Restart:
```bash
systemctl restart dovecot
```
---
## Part 9: Email Encryption with Dovecot Mail-Crypt
### Install Required Packages
```bash
# Already included in dovecot package on Arch
pacman -S --noconfirm openssl
```
### Generate Master Encryption Keys
```bash
mkdir -p /var/lib/dovecot/mail-crypt
cd /var/lib/dovecot/mail-crypt
# Generate EC keys
openssl ecparam -name prime256v1 -genkey | openssl pkey -out ecprivkey.pem
openssl pkey -in ecprivkey.pem -pubout -out ecpubkey.pem
# Set permissions
chown vmail:vmail *.pem
chmod 400 *.pem
```
### Configure Mail-Crypt Plugin
Create `/etc/dovecot/conf.d/90-mail-crypt.conf`:
```bash
vim /etc/dovecot/conf.d/90-mail-crypt.conf
```
```
mail_plugins = $mail_plugins mail_crypt
plugin {
mail_crypt_global_private_key = </var/lib/dovecot/mail-crypt/ecprivkey.pem
mail_crypt_global_public_key = </var/lib/dovecot/mail-crypt/ecpubkey.pem
mail_crypt_save_version = 2
mail_crypt_curve = secp521r1
mail_crypt_private_password = %w
}
```
Update main mail config:
```bash
vim /etc/dovecot/conf.d/10-mail.conf
```
Find `mail_plugins` and add `mail_crypt`:
```
mail_plugins = mail_crypt
```
Restart Dovecot:
```bash
systemctl restart dovecot
```
### Install PGP/GPG Tools
```bash
pacman -S --noconfirm gnupg
```
### PGP Key Management Script
```bash
vim /usr/local/bin/mail-pgp-manager
```
```bash
#!/bin/bash
USER_EMAIL="$1"
ACTION="$2"
GNUPG_HOME="/var/mail/vhosts/$(echo $USER_EMAIL | cut -d'@' -f2)/$(echo $USER_EMAIL | cut -d'@' -f1)/.gnupg"
case "$ACTION" in
generate)
mkdir -p "$GNUPG_HOME"
chmod 700 "$GNUPG_HOME"
chown vmail:vmail "$GNUPG_HOME"
su - vmail -c "gpg --homedir $GNUPG_HOME --batch --gen-key <<EOF
Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: $USER_EMAIL
Name-Email: $USER_EMAIL
Expire-Date: 0
%no-protection
%commit
EOF"
;;
export-public)
su - vmail -c "gpg --homedir $GNUPG_HOME --armor --export $USER_EMAIL"
;;
list)
su - vmail -c "gpg --homedir $GNUPG_HOME --list-keys"
;;
*)
echo "Usage: $0 <email> <generate|export-public|list>"
exit 1
;;
esac
```
Make executable:
```bash
chmod +x /usr/local/bin/mail-pgp-manager
```
---
## Part 10: Creating Email Accounts
### Generate Password Hash
```bash
doveadm pw -s SHA512-CRYPT
```
Enter your password. Copy the hash.
### Add User to Database
```bash
mysql -u root -p mailserver
```
```sql
-- Get domain ID
SELECT id FROM virtual_domains WHERE name='liphlink.xyz';
-- Add user (replace hash with your generated hash)
INSERT INTO virtual_users (domain_id, password, email)
VALUES (1, '{SHA512-CRYPT}$6$OWM2H5QRTyR5qLky$n1lDTBB7CcHpMNNtXGe4pwC.OH9/hA6Gen3pwWr9R5AoboJuqg9P/gl..WQXuy82cGizNy3WDEzRNWnevqfbx0', 'phil@liphlink.xyz');
-- Verify
SELECT * FROM virtual_users;
EXIT;
```
```sql
-- Get domain ID
SELECT id FROM virtual_domains WHERE name='liphlink.xyz';
-- Add user (replace hash with your generated hash)
INSERT INTO virtual_users (domain_id, password, email)
VALUES (1, '{SHA512-CRYPT}$6$5ZnV6ungZJDq00k4$UhNVKgXoJp1AvfTMnqyDFtyECG4/5UNd.ZPlQP9OVdeOpgjssJwP0qaOWCoP0AHjL7/zAsgUR4GIY/YczK2hB1', 'liph@liphlink.xyz');
-- Verify
SELECT * FROM virtual_users;
EXIT;
```
### Create Maildir for User
```bash
mkdir -p /var/mail/vhosts/liphlink.xyz/liph/Maildir/{cur,new,tmp}
chown -R vmail:vmail /var/mail/vhosts/liphlink.xyz/liph
chmod -R 770 /var/mail/vhosts/liphlink.xyz/liph
```
---
## Part 11: Firewall Configuration
### Install and Configure UFW
```bash
pacman -S --noconfirm ufw
# Allow SSH first!
ufw allow 22/tcp
# Mail server ports
ufw allow 25/tcp # SMTP
ufw allow 587/tcp # Submission
ufw allow 465/tcp # SMTPS
ufw allow 993/tcp # IMAPS
ufw allow 143/tcp # IMAP
ufw allow 80/tcp # HTTP (Let's Encrypt)
ufw allow 443/tcp # HTTPS
# Enable
ufw enable
systemctl enable ufw
```
---
## Part 12: Security Hardening with Fail2ban
### Install Fail2ban
```bash
pacman -S --noconfirm fail2ban
```
### Configure Fail2ban
```bash
vim /etc/fail2ban/jail.local
```
```ini
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[postfix-sasl]
enabled = true
port = smtp,submission,smtps
logpath = /var/log/mail.log
filter = postfix-sasl
[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps,submission,smtps
logpath = /var/log/mail.log
filter = dovecot
```
Enable and start:
```bash
systemctl enable --now fail2ban
```
---
## Part 13: Testing Everything
### Test Postfix
```bash
# Check config
postfix check
# Test MySQL lookups
postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
postmap -q user@example.com mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
```
### Test Dovecot Authentication
```bash
doveadm auth test user@example.com
```
### Send Test Email
```bash
# Install mail utility
pacman -S --noconfirm s-nail
# Send test
echo "Test email" | mail -s "Test" phil@liphlink.xyz
# Check logs
journalctl -u postfix -f
journalctl -u dovecot -f
```
ALTER USER 'mailuser'@'localhost' IDENTIFIED BY '1ChagemaiL';
FLUSH PRIVILEGES;
EXIT;
### Test IMAP
```bash
openssl s_client -connect localhost:993 -crlf
```
Then:
```
a LOGIN user@example.com password
b SELECT INBOX
c LOGOUT
```
### Test Notmuch
```bash
su - vmail
cd /var/mail/vhosts/example.com/user
notmuch search tag:inbox
notmuch search from:sender@example.com
exit
```
---
## Part 14: Monitoring and Logs
### View Logs
```bash
# Postfix logs
journalctl -u postfix -f
# Dovecot logs
journalctl -u dovecot -f
# All mail logs
journalctl -u postfix -u dovecot -f
# OpenDKIM logs
journalctl -u opendkim -f
```
### Check Service Status
```bash
systemctl status postfix dovecot opendkim mariadb
```
### Check Mail Queue
```bash
postqueue -p
mailq
```
---
## Part 15: Backup Script
```bash
vim /usr/local/bin/mail-backup.sh
```
```bash
#!/bin/bash
BACKUP_DIR="/root/mail-backup"
DATE=$(date +%Y-%m-%d)
mkdir -p "$BACKUP_DIR"
# Backup configs
tar -czf "$BACKUP_DIR/postfix-$DATE.tar.gz" /etc/postfix/
tar -czf "$BACKUP_DIR/dovecot-$DATE.tar.gz" /etc/dovecot/
tar -czf "$BACKUP_DIR/opendkim-$DATE.tar.gz" /etc/opendkim/
# Backup database
mysqldump -u root -p mailserver > "$BACKUP_DIR/mailserver-$DATE.sql"
# Backup mail (optional - can be large)
# tar -czf "$BACKUP_DIR/vmail-$DATE.tar.gz" /var/mail/vhosts/
# Keep only last 7 days
find "$BACKUP_DIR" -type f -mtime +7 -delete
echo "Backup completed: $DATE"
```
Make executable:
```bash
chmod +x /usr/local/bin/mail-backup.sh
```
Schedule with cron or systemd timer.
---
## Arch Linux Specific Notes
### Package Management
```bash
# Update system
pacman -Syu
# Search packages
pacman -Ss package-name
# Install package
pacman -S package-name
# Remove package
pacman -R package-name
```
### AUR Helper (Optional)
For packages not in official repos:
```bash
# Install yay
pacman -S --noconfirm git base-devel
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si
```
### Systemd Journal Size
Limit journal size:
```bash
vim /etc/systemd/journald.conf
```
```ini
[Journal]
SystemMaxUse=500M
```
Restart:
```bash
systemctl restart systemd-journald
```
---
## Quick Reference Commands
### User Management
```bash
# Create user with encryption
doveadm pw -s SHA512-CRYPT
mysql -u root -p mailserver -e "INSERT INTO virtual_users VALUES (1, 'HASH', 'user@example.com');"
mail-pgp-manager user@example.com generate
```
### Service Management
```bash
# Restart services
systemctl restart postfix dovecot opendkim
# View logs
journalctl -u postfix -u dovecot --since "1 hour ago"
# Check mail queue
postqueue -p
```
### Notmuch
```bash
# Manual index
/usr/local/bin/notmuch-index-all.sh
# Check timer
systemctl status notmuch-index.timer
```
---
## Troubleshooting
### Services won't start
```bash
# Check for errors
systemctl status servicename
journalctl -xeu servicename
# Check configuration
postfix check
doveconf -n
```
### Permission issues
```bash
# Fix ownership
chown -R vmail:vmail /var/mail/vhosts
chown -R postfix:postfix /var/spool/postfix
chown -R dovecot:dovecot /etc/dovecot
# Fix permissions
chmod -R 770 /var/mail/vhosts
chmod 600 /etc/dovecot/dovecot-sql.conf.ext
```
### Database connection fails
```bash
# Test connection
mysql -u mailuser -p mailserver
# Check postfix mysql maps
postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
```
---
## Advantages of Arch Linux for Mail Server
**Rolling release**: Always latest stable versions
**Minimal bloat**: Only what you install
**Great documentation**: Arch Wiki is excellent
**Performance**: Optimized packages
**Control**: Full customization
**SystemD native**: Modern service management
**Fast package manager**: Pacman is very fast
---
## Next Steps
1. Configure DNS records (see dns-cloudflare-reference.md)
2. Test email delivery with mail-tester.com
3. Set up webmail (Roundcube) if desired
4. Configure backups
5. Monitor deliverability
6. Set up additional security (AppArmor/SELinux)
---
## Additional Resources
- **Arch Wiki Mail Server**: https://wiki.archlinux.org/title/Mail_server
- **Arch Wiki Postfix**: https://wiki.archlinux.org/title/Postfix
- **Arch Wiki Dovecot**: https://wiki.archlinux.org/title/Dovecot
Your Arch Linux mail server is now complete with encryption, notmuch indexing, and all modern security features! 🚀
Enjoy your fully customizable, privacy-respecting email infrastructure on Arch Linux!