By default, Linux servers will only deliver to system users. However this is obviously not useful for mail servers which need to handle multiple virtual mail accounts. A common solution is to manage all the domains through a database however if you can’t/don’t want to do that, here’s how to configure Postfix to use flat files instead.
For this guide I will configure 2 domains - domain1.internal and domain2.internal - as well as 2 email addresses: email@example.com and firstname.lastname@example.org
Create a virtual domain environment
As emails need to be secure, we need to lock them down to a specific user so that only it can interact with them. Add the user (I tend to call it ‘vmail’) like so:
useradd -m vmail
Next, you’ll need to get it’s user and group IDs so that Postfix will know which user to interact with:
$ grep vmail /etc/passwd /etc/passwd:vmail:x:1001:1001::/home/vmail:/bin/sh
You’ll see two numbers there - the first one is the user ID and the second the group ID.
Next, create the directory to store the emails. It’s location isn’t vital but the home directory and /var/spool/ seem to be popular choices. Let’s put it the home directory:
Next, we need a layout for each domain and user. This can be as simple or complex as you want it, but for simplicity let’s use the layout [email domain]/[email user]:
mkdir -p /home/vmail/vmail/domain1.internal/joe mkdir -p /home/vmail/vmail/domain2.internal/pete
Finally, let’s make sure that only the vmail user can access this directory:
chown -R vmail:vmail /home/vmail/vmail chmod -R 0700 /home/vmail/vmail
Edit the Postfix config file (normally /etc/postfix/main.cf) and add the following changes:
virtual_mailbox_domains = /etc/postfix/vhosts.txt virtual_mailbox_base = /home/vmail/vmail virtual_mailbox_maps = hash:/etc/postfix/vmaps.txt virtual_uid_maps = static:1001 virtual_gid_maps = static:1001 virtual_alias_maps = hash:/etc/postfix/valias.txt
Note that the
virtual_uid_maps number is the user ID. Likewise,
virtual_gid_maps is the group ID. The
line points Postfix to the file containing the email domains, and
virtual_mailbox_maps is for the actual email accounts.
virtual_alias_maps, logically, is for the file containing any
aliases. Let’s now create them.
Create the config files
vhosts.txt contains a list of domain names, 1 per line. For example,
I’ve added the following:
To allow for more flexibility, Postfix doesn’t check if these domains actually exist, so you can call them whatever you want, really.
vmaps.txt contains a list of email addresses, one per line, as well as
the user’s mail directory:
email@example.com domain1.internal/joe/ firstname.lastname@example.org domain2.internal/pete/
You can add a catch-all address like so:
Thing to note: the directory must end in a slash otherwise mail
won’t be delivered (you’ll get ‘unable to create lock file’ errors, for
example). Also, don’t specify the entire path to the user’s directory.
This is because it’ll get appended to the directory specified by
valias.txt’s syntax is similar to
vmaps.txt, however it allows you
to redirect emails to multiple accounts, for example:
email@example.com firstname.lastname@example.org, email@example.com
Notice that Tom’s address doesn’t actually exist. Being able to create ‘fake’ addresses is a handy feature :)
Now, if you were paying attention you would have noticed that the config lines for the mailbox and aliases files in main.cf had ‘hash’ in them. This is because Postfix wants to use hashed files for them, not plain-text. This is not a problem:
postmap /etc/postfix/vmaps.txt postmap /etc/postfix/valias.txt
Test the setup
First, reload the Postfix config:
Next, connect to Postfix and send a test message. I chose to send it to firstname.lastname@example.org to test the alias setup as well. All the lines after “Escape character is ‘^]’.” that don’t start with a number are what I typed in:
$ telnet localhost 25 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 ubuntu-test ESMTP Postfix (Ubuntu) helo me 250 ubuntu-test mail from: email@example.com 250 2.1.0 Ok rcpt to: firstname.lastname@example.org 250 2.1.5 Ok data 354 End data with <CR><LF>.<CR><LF> subject: test email . 250 2.0.0 Ok: queued as F063C7F9F3 quit 221 2.0.0 Bye
Checking the mail logs showed that it has been delivered successfully. If you see ‘(delivered to maildir)’ then all is well:
Jun 24 15:04:48 ubuntu-test postfix/qmgr: F063C7F9F3: from=<email@example.com>, size=309, nrcpt=2 (queue active) Jun 24 15:04:48 ubuntu-test postfix/virtual: F063C7F9F3: to=<firstname.lastname@example.org>, orig_to=<email@example.com>, relay=virtual, delay=25, delays=25/0.01/0/0.01, dsn=2.0.0, status=sent (delivered to maildir) Jun 24 15:04:48 ubuntu-test postfix/virtual: F063C7F9F3: to=<firstname.lastname@example.org>, orig_to=<email@example.com>, relay=virtual, delay=25, delays=25/0.01/0/0, dsn=2.0.0, status=sent (delivered to maildir) Jun 24 15:04:48 ubuntu-test postfix/qmgr: F063C7F9F3: removed
Apologies for not formatting it properly, log entries are often very long :( Anyway, that’s pretty much it. If you’re wondering why I didn’t create any passwords for the users, it’s because authentication isn’t the job of the MTA (Mail Transfer Agent) :)
Now, in order to add a new account, you need to do the following:
- create a new domain directory (if needed) and the user directory and set permissions, etc
- modify vmaps.txt and hash it
- modify valias (if you need to) and hash it
- reload the Postfix config
The steps seem like a hassle but are actually very easy to automate via script.
Postfix - Pipe Emails to a Script
Python - Generate Random Password Strings