Setup Virtual Domains in Postfix (Without a Database)

Comments

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: joe@domain1.internal and pete@domain2.internal

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:

mkdir /home/vmail/vmail

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

Configure Postfix

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 virtual_mailbox_domains 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:

domain1.internal
domain2.internal

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:

joe@domain1.internal domain1.internal/joe/
pete@domain2.internal domain2.internal/pete/

You can add a catch-all address like so:

@domain1.internal domain1.internal/catchall/

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 virtual_mailbox_base.

valias.txt’s syntax is similar to vmaps.txt, however it allows you to redirect emails to multiple accounts, for example:

tom@domain1.internal joe@domain1.internal, pete@domain2.internal

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:

postfix reload

Next, connect to Postfix and send a test message. I chose to send it to tom@domain1.internal 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: joe@domain1.internal
250 2.1.0 Ok
rcpt to: tom@domain1.internal
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[2987]: F063C7F9F3: from=<joe@domain1.internal>, size=309, nrcpt=2 (queue active)
Jun 24 15:04:48 ubuntu-test postfix/virtual[3004]: F063C7F9F3: to=<joe@domain1.internal>, orig_to=<tom@domain1.internal>, 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[3004]: F063C7F9F3: to=<pete@domain2.internal>, orig_to=<tom@domain1.internal>, 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[2987]: 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:

  1. create a new domain directory (if needed) and the user directory and set permissions, etc
  2. modify vmaps.txt and hash it
  3. modify valias (if you need to) and hash it
  4. reload the Postfix config

The steps seem like a hassle but are actually very easy to automate via script.

Next Post
Postfix - Pipe Emails to a Script

Previous Post
Python - Generate Random Password Strings


comments powered by Disqus