If, like myself you’ve got one server from which you want to run many different domains for web hosting and email, you may be realising just how tricky it can be to provide email for non-system users. The typical SMTP/IMAP setup on a linux server is ignorant about what domain is being used when the request comes in. Take the email address “joe@example.com”. Traditionally this literally means “email for a user account ‘joe’ on ‘example.com’”. It makes the assumption that whichever server manages email for example.com has a user account called joe on it. example.com does not traditionally care that it’s example.com; all it cares about is who the email is for. Other servers use the example.com part purely to direct the email to the appropriate server.
This is perfectly sufficient if “joe” has a user account on that server (i.e. a record in /etc/shadow or /etc/passwd) and if that server does not belong to any domains other than “exmaple.com”. However, when you’re running a single server with perhaps only one IP address mapping to several domains and you want to have a different set of email addresses for each domain this becomes a problem. You may also want to have email addresses such as sales@yourdomain.com and support@yourdomain.com but you don’t need system accounts called “sales” and “support”. In this case we need to start hacking at the mail server configuration in order to allow the delivery of the email.
I have no doubts whatsoever that Exim4 (Exim4 is significantly different from Exim3!) is the best MTA/MX software to use for things like this. In fact, I would hands down say that you should use Exim for any MX server. It’s advanced, and very complicated to get working I’ll give it that. But with the complexity comes great flexibility. You can practically program the entire configuration and can modify the behaviour almost without restriction.
Lets not forget however, that your IMAP or POP3 server also needs to be hackable if you really want this to work. For years I used a patched version of UW-IMAP which did the job nicely but there was no configuration avaliable for it and whilst easy to get running this way, it was too restrictive because it tied me down to fixed paths and directory layouts. UW-IMAP also would not work with Maildir setups without even further patching. This article will focus on an IMAP/POP3 server called Dovecot. Dovecot has a very flexible configuration just like Exim, albeit much simpler to make sense of! However, the principles of virtual email hosting apply whichever software you use.
It’s very unlikely that there will not already be packages for exim4 and dovecot provided by your linux distro. If you can do so, install the packages as provided by your distro. If your distro does not provide suitable packages you can get them here:
- Exim4:
- Website: http://www.exim.org/
- Documentation: http://www.exim.org/docs.html
- Dovecot:
- Website: http://www.dovecot.org/
- Documentation: http://wiki.dovecot.org/
Who is that article targetted at?
This article from hereon in will assume you have a solid grasp of linux server administration, but not necessarily a deep understanding of Exim or Dovecot. If you’re not comfortable working on a linux server, editting configurtion files and making educated guesses then this article will likely be of no help to you.
Any Linux admin will know that this article is not going to give you copy and paste configuration directives (though if you’re lucky it might do) because linux package maintainers love to over-complicate things by adding their own sprinkle of salt to the configuration. I set this up on a Debian (sarge, then subsequently etch) server, however I have set this up in almost the same manner on a Gentoo server and an ArchLinux server previously. You may find that what’s written here does not directly apply to your situation but if you read the entire article rather than just the configuration excerpts you should be able to apply the principles on your own server.
Ok, lets go!
Principles involved
Traditionally, both SMTP and IMAP/POP3 will look in /etc/passwd or /etc/shadow for users. This actually works really well so we virtually copy this approach, except we provide a different passwd file for each domain we need to support. Although this article does not delve into it, both Exim and Dovecot will allow you to store your users in other ways; in a database such as MySQL for example.
passwd files are structured like this:
username:password:uid:gid:[gecos]:home:[shell]:[extra]
gecos, shell and extra are ignored in our case so they’ll appear empty, though the surrounding colon will appear.
For example, if “fred” on your server (UID=1001, GID=100) owns the domain “example.com” and wants a new email address setting up called “pete@example.com” there might be a file located at /etc/vmail/example.com/passwd with the contents:
pete:petes-password:1001:100::/home/fred::
From this we can check if pete is found in the passwd file for example.com, we can also see the uid, gid and home directory of pete, the domain owner. This is enough information for us to save the incoming email.
A domain can have multiple users, and if two domains have the same user it does not mean they are the same email account. In order to determine where the mail goes, the home directory part in the passwd file will specify where the email gets saved. passwd files also conveniently provide UID and GID values so the mail can easily be saved with the correct permissions!
In this article we’ll look at how to store passwd information in directories we’ll create at /etc/vmail/ (i.e. /etc/vmail/domain1.com/passwd, /etc/vmail/domain2.tld/passwd) and save emails to mbox style files at ~/mail/domain.tld/user/inbox (i.e. ~/vmail/example.com/joe/inbox).
This makes the assumption that each domain is owned by one of your system users. A single system user could, for example be a specially created account called “vmail” to manage all virtual domains. However, you can allow individual users to administer their own domains since emails are stored in the $HOME directory of the allocated domain owner.
Both Exim and Dovecot will read from the same passwd files for this to work so we hack them both in a similar fashion. By the way, when I say “hack”, these systems have been written with the intention of being hacked
Defining your first virtual domain and user
Create a directory named /etc/vmail.
mkdir /etc/vmail
Within that, create a directory for a domain which has a MX record pointing to your server (make sure it’s all in lowercase).
mkdir /etc/vmail/example.com
Now create a passwd file in there containing the username in the address and the uid, gid, home directory of the owner. Don’t worry about the password bit just yet. I’m just setting it to “xxx” here because incmoming mail does not require a password. You’ll see why pete@example.com cannot log in with the password “xxx” until we change this later.
echo “pete:xxx:1001:100::/home/fred:: >> /etc/vmail/example.com/passwd
The remainder of the article will make use of this file.
Now create the place where these emails will be stored (in fred’s $HOME directory).
mkdir -p /home/fred/mail/example.com/pete
chown -R pete:users /home/fred/mail
chmod -R 0700 /home/fred/mail
Of course, if you’re going to be doing this regularly you’d write a tool for creating such files since it can be quite tedious. I may post a command line tool myself, but as it stands I still do it by hand.
Setting up Exim
Be very sure that you have installed Exim4, not Exim3. Also make sure that any other SMTP server software has been removed to prevent conflicts; remember to look in inetd or xinetd if you’ve never checked there before.
There are two ways to install Exim4. You can install with a single configuration file exim.conf, or you can install with separate smaller config files in a conf.d style layout. I advise using the latter option in the interest of maintainability, but this article will apply either way. The single config file is simply a glued-together version of the conf.d layout.
The single config file will most likely reside at /etc/exim4/exim.conf. Inside that file there are sections which start with the keyword “begin”; for example “begin transports” and “begin routers”. These are where the conf.d style layout gets broken down. The conf.d style layout places sub-directories in /etc/exim4/conf.d. For example; /etc/exim4/conf.d/transport, /etc/exim4/conf.d/router etc.
Once you have Exim installed, check the daemon actually runs by starting it from /etc/init.d or /etc/rc.d. You should be able to telnet to localhost on port 25, getting a greeting message beginning with “220″. Type “QUIT” to end the session.
w3style.co.uk:~# telnet localhost 25
Trying 127.0.0.1…
Connected to mail.w3style.co.uk.
Escape character is ‘^]’.
220 mail.w3style.co.uk ESMTP Exim 4.63 Sun, 27 May 2007 16:19:15 +0100
QUIT
221 mail.w3style.co.uk closing connection
Connection closed by foreign host.
w3style.co.uk:~#
If telnet could not connect on port 25, or you get a greeting beginning with anything other than “220″ something has gone wrong with your installation. Unfortunately this article cannot delve into possible causes for the sake of brevity, but needless to say, you should consult the exim documentation. One really obvious thing to check would be the “local_interfaces” line in the configuration
Exim has four major areas in its configuration:
- General; contains information such as what interface to listen on, what domains belong to the server and who can relay through it. Essentially the general configuration sets up variables which are used in the other areas of the configuration.
- ACLs; programmed logically to determine what happens at each stage in the SMTP processing. For example you can check if the sender is trying to spoof an address or if relaying should be denied from within the ACLs.
- Transports; specifies how the emails should be delivered (written to a file on disk, added to a database, relayed to another server etc).
- Routing; determines which transport should be used.
Apart from the General configuration, Exim is configured almost entirely by a series of expressions and conditions. For programmers this might feel normal, but for non-programmers it can be a bit daunting.
We’ll work backwards here so you can more easily see the chain of how it all ties together.
The transport configuration
mbox files which we’re going to write to use a standard Exim driver called “appendfile”. You can add new transports to Exim by simply adding a new transport declaration. For the most part we can base the virtual domain transport on the mbox local transport. If you use a single exim.conf file, scroll down to the transports section inside it. If you use the conf.d layout, create a new file in /etc/exim4/conf.d/transport/virtual_transport (the name you use is up to you).
Now, add the following transport to the configuration:
virtual_mail_spool:
driver = appendfile
user = ${extract{2}{:}\
{${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}}\
}}
file = ${extract{5}{:}\
{${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}}\
}}/mail/$domain/$local_part/inbox
group = ${extract{3}{:}\
{${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}}\
}}
mode = 0700
I accept that it looks extremely complicated. It is complicated. But lets break it down a bit and suddenly it seems less intimidating. The lines ending with a backslash are just partial lines. The backslash indicates the whatever is on the following line should be appended to it. You can remove the backslashes and place everything on on line if you wanted to but I find it harder to read.
The first line, “virtual_mail_spool:” specifies that you are declaring a new transport method. You can rename this to suit your needs.
The following lines provide settings for the transport. Lets break the rest of it down because it looks pretty scary right?
appendfile is the standard mbox driver in Exim. If you used a Maildir (multiple files) instead of mbox then you’d have to include an additional “maildir_format” line too.
user = ${extract{2}{:}\
{${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}}\
}}
Ok, I know, I know. WTF is that?! Overall, the line specifies the user permissions (username or uid) under which to write the file. In this case it pulls out the uid from our passwd file.
The curly braces are used to set the order of precedence and to group expressions together. The opening ${ indicates that what follows needs to be expanded/evaluated.
The variables in there, $local_part and $domain are provided by exim. For an address pete@example.com, $local_part = pete and $domain = example.com.
The first ${extract{2}{:}{ .. stuff … }} tells Exim to split ” … stuff … ” at every “:” character and then pull out the third chunk (indexing starts at zero). For a single line in the passwd file this will directly extract the uid number.
Looking at ” … stuff … ” we’ve actually got the expression ${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}}. This finds a single line in the passwd file.
${lookup{ … keyword .. }lsearch{ … file … }} is the bit which finds the line in question. Here it’s looking for the file at /etc/vmail/example.com because $domain = exmaple.com. It then looks for the line starting with “pete”, thus giving us the correct line in the file and allowing the extract{}{:}{} to provide the correct uid.
file = ${extract{5}{:}\
{${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}}\
}}/mail/$domain/$local_part/inbox
If you understood how “user” was written, then this should hopefully make sense. As menioned earlier we want to save files at ~/mail/domain/user/inbox.
This finds the home directory of the domain owner, then appends “/mail/example.com/pete/inbox” to it. It specifies the location of the mbox file to which the mail will be saved.
group = ${extract{3}{:}\
{${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}}\
}}
This is structurally identical to the “user” line. It pulls out the gid of the domain owner from the passwd file.
Finally, the line “mode = 0700″ provides the permissions mode to write the file in. 0700 gives full access to the owner and no access to anybody else.
Summarising all that in plain English it: checks which system user should own the email, determines what file to write the email to, checks which group the owner is in, specifies the permissions on the file to be 0700 (-rwx–).
This is enough for our transport configuration. However, the transport will never actually be used until a router points to it so we now need to create a router.
The router configuration
Essentially a router is just a set of conditions which, if evaluate true cause Exim to honour the transport it points to. Our router will need to check if the user and domain are valid for our virtual configuration. This is a simple case of looking to see if a directory exists for the domain in /etc/vmail and if the user line can be seen in the passwd file for that domain.
If you use a single exim.conf file, scroll to the top of routers section. If you use a conf.d layout, create a new file at /etc/exim4/router/virtual_user. Exim actually loads files in alphabetical order so you might want to prepend the filename with a number to cause it to be loaded before the other files in a conf.d setup.
Add the following to your configration:
virtual_user:
driver = accept
domains = dsearch;/etc/vmail
condition = ${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}\
{yes}{no}}
The first line, “virtual_user:” declares that we are creating a new router. You can call this whatever you like, the name is irrelevant provided it’s unique.
The line “driver = accept” specifies that this router can only be used if the email was accepted by the ACLs (we’ll look at this later).
domains = dsearch;/etc/vmail
This line specifies which domains this router applies to. You could hard-code a list by writing “domain1.com : domain1.org” etc etc, but that would not be easily extendible. “dsearch;” is an expression which returns all files in the directory following the semi-colon. In our case this line evaluates to all domains we virtual host for because that’s what the directories in /etc/vmail are.
condition = ${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}\
{yes}{no}}
This line must evaluate to “true” (or “yes”) if the router is to be used. It looks for a line for the user in the passwd file. If one is found then the condition returns “yes”. If none is found then the condition returns “no” and this router will not be used.
transport = virtual_mail_spool
Finally, this specifies the transport we created previously. It tells Exim that if the conditions are true our new transport should be used. If you used a different name for the transport remember to modify this line accordingly.
Summarising this in plain English it says: Only use this router if the mail was already accepted and the domain is a virtual domain in /etc/vmail. If the user cannot be found in /etc/vmail/example.com/passwd, this router should not be used (unroutable address). The transport this router directs to is “virtual_mail_spool”.
Nearly done! Just one quick test
Start (or restart) the Exim4 daemon running from /etc/init.d or rc.d you should be able to telnet to it on port 25. Issue the following commands:
EHLO example.com
MAIL FROM:
RCPT TO:
DATA
Something
.
QUIT
You should see something like this:
w3style.co.uk:~# telnet localhost 25
Trying 127.0.0.1…
Connected to mail.w3style.co.uk.
Escape character is ‘^]’.
220 mail.w3style.co.uk ESMTP Exim 4.63 Sun, 27 May 2007 17:54:20 +0100
EHLO example.com
250-mail.w3style.co.uk Hello localhost [127.0.0.1]
250-SIZE 52428800
250-PIPELINING
250-AUTH CRAM-MD5
250 HELP
MAIL FROM:
250 OK
RCPT TO:
250 Accepted
DATA
354 Enter message, ending with “.” on a line by itself
Something
.
250 OK id=1HsM1G-0008Tg-5c
QUIT
221 mail.w3style.co.uk closing connection
Connection closed by foreign host.
Assuming everything’s working you should get something similar to that above. If you’re not getting this then something has gone wrong.
If it worked, you should see a file at /home/fred/mail/example.com/pete/inbox. If you open this file it will contain the email.
This is great, it works! If you have MX records for example.com pointing to this server the server will happily accept mail for the virtual user pete@example.com. Of course, there’s no way to read these emails remotely as yet, unless of course you SSH to the server and open the inbox file directly.
Wait, there’s something else we need to do. If someone tries to email “no-such-user@example.com” they will get the same result during SMTP, but they’ll later get a bounced email because no router was found for “no-such-user” after it was accepted. This is quite normal and I’m sure we’ve all had lots these emails in our inboxes over time. However, if you’re providing email for a lot of domains this would be wasting valuable bandwidth.
Defining a new ACL
It’s possible to deny a request at the “RCPT TO:” phase in SMTP. In order to do this we create a new ACL (access control list). Our ACL will check if the domain is a vitual domain, and will then check that it can find a user for that domain. If it is a virtual domain but no user exists, the ACL will deny the command, thus giving a 550 response.
ACLs are a series of rules. Each ACL ruleset is evaluated in order until a conclusive result has been decided (accept, or deny). In reality there are other results such as “warn” and “require” but we don’t need to delve into them here.
If you’re using the single exim.conf file scroll to the ACL section. For the conf.d style layout, create a new file at /etc/exim4/conf.d/acl/check_virtual_rcpt. Now add the following to the configuration file:
acl_check_virtual_rcpt:
# deny RCPT TO if it’s a virtual domain and no user is found
deny
message = Unknown Recipient
domains = dsearch;/etc/vmail
!condition = ${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}\
{yes}{no}}
# accept otherwise
accept
The first line, “acl_check_virtual_rcpt:” defines that you are creating a new ACL ruleset.
The line “deny” specifies that the conditions which follow are assessing whether to deny the request. If all the following conditions evaluate true the request will be denied (our 550 response in RCPT).
message = Unknown Recipient
This is simply a message which will be displayed in SMTP. You can change it to something like “Sorry, I don’t know about this user” if you really wanted to. If the request is denied the error will say “550 Unknown Recipient” in our case. I think this is quite sensible
domains = dsearch;/etc/vmail
As with our router configuration, this line indicates that the ruleset only applies if the domain is a virtual domain found in /etc/vmail.
!condition = ${lookup{$local_part}lsearch{/etc/vmail/$domain/passwd}\
{yes}{no}}
This is actually the same line I used in the router, except for the leading “!’ character. It looks to see if the user is found in the passwd file. If the user is in the passwd file the condition returns true. However, the “!” before the “condition” keyword indicates that the response should be negated, so if no such user is found, the line evaluates to “yes” rather than “no” and therefore all conditions were satisfied which concludes that the request should be denied.
The final line “accept” simply indicates that if the request was not denied, our ACL ruleset can accept the email. However, another ruleset somewhere else may still deny it
As it stands, Exim does not know to run our ACL yet. In order to do that we need to specify it with the existing ACL for “acl_smtp_rcpt”. Go to the general configuration section and find the line starting with “acl_smtp_rcpt = “. If no such line exists, create it, but before any “begin” sections in the configuration file. For example, the line might look like this:
acl_smtp_rcpt = acl_check_rcpt
This line basically tells Exim that during the RCPT phases in SMTP it should carry out the ACL checks defined in the “acl_check_rcpt” ruleset. If there isn’t a line already there then simply set “acl_smtp_rcpt = acl_check_virtual_rcpt” and nothing more is needed. If there is a line there, look at what is assigned to it (acl_check_rcpt for example), then find that acl ruleset. At the top of the ruleset it points to, add these lines:
deny
!acl = acl_check_virtual_rcpt
That basically gets the other ACL to run the checks in your ACL too.
Restart exim and try running those SMTP commands over telnet again, this time replacing the address in RCPT TO with “nobody-here@example.com” You should get an immediate “550″ response. This is good. It will cut out a lot of wated bandwidth from bounced messages when spammers start sending emails to addresses that don’t really exist.
That’s it for the Exim configuration. Phew! Now we need some way to allow our users to open up their inboxes remotely. Thankfully the Dovecot configuration is far less complicated than the Exim configuration
Setting up Dovecot
Make sure you have uninstalled any other imap servers which may be present on your system. Remember to check in inetd or xinetd if you’ve never checked there before
Unlike Exim which is seriously complicated to configure unless you’re a programmer (like myself), Dovecot is a lot friendlier. It’s still flexible enough for our needs but for the most part, getting it to do what we want is trivial. The main dovecot configuration is stored in a single file at /etc/dovecot/dovecot.conf.
Making dovecot listen for IMAP requests
The first thing you want to do is edit the line starting with “protocols =”:
Now we’ll just run a quick test to make sure it’s working. Start (or restart) dovecot from /etc/init.d or rc.d. The telnet to localhost, port 143. You should get a Dovecot greeting. Type “a01 LOGOUT” to end the session:
w3style.co.uk:~# telnet localhost 143
Trying 127.0.0.1…
Connected to mail.w3style.co.uk.
Escape character is ‘^]’.
* OK Dovecot ready.
a01 LOGOUT
* BYE Logging out
a01 OK Logout completed.
Connection closed by foreign host.
If telnet cannot connect, or if you get some sort of error message then your imap installation is not working correctly. You’ll need to consult the dovecot documentation for further help before continuing with the configuration.
Specifying the mail location
Like Exim, Dovecot provides some variables for us to use. Scroll down to the section “Mailbox locations and namespaces” then edit the line starting with “mail_location = “:
mail_location = mbox:%h/mail/%d/%n
This is actually pretty straightforward. “mbox:” defines that we’re using mbox files rather than maildir. If you use maildir you’re change it to “maildir:”. %h is the variable for $HOME of the domain owner. This is /home/fred in our example. %d is the domain name and %n is the local part of the address (i.e. for pete@example.com; %n = pete, %d = example.com).
Make sure Dovecot is going to use the correct file permissions (0700, the same as Exim) by specifying, in the “Mail processes” section:
Configuring the login process
We’ve already got a passwd file for pete@example.com which we created earlier. Dovecot can use this just like Exim can. Dovecot does however need to know what pete’s password is. Up until now we haven’t considered what goes in the password part of the passwd file. The official passwd/shadow format expects a hashed password as returned by crypt() with a salt. Dovecot allows you to use plain text passwords however. I’m not condoning the use of plain text passwords, but for the sake of simplicity in this article I’ll show you how it’s done.
Open up /etc/vmail/example.com/passwd.
Edit the “xxx” part where the password should be to say:
pete:{PLAIN}petes-password:1001:100::/home/fred::
The {PLAIN} part at the start of the password specifies that the password is stored in plain text. In this case, pete can log in with the username “pete@example.com” and the password “petes-password”. If you don’t specify the {PLAIN} part then the password will be assumed to be hashed with crypt(). There are tools available for producing such hashes and in the interest of security it would be advisable to do this.
Now scroll down the the “Authentication processes” section in dovecot.conf. Edit the line starting with “auth_username_format”.
auth_username_format = %Lu
This causes usernames to always be in lowercase.
Inside the block starting with “auth default {” make sure “plain” is in the list of mechanisms.
auth default {
# Space separated list of wanted authentication mechanisms:
# plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi
mechanisms = plain
Without this our {PLAIN} part in the passwd file will not work.
There are two phases in authentication: Password verification, and user lookup. These are called “passdb” and “userdb” in dovecot. Scroll a little further down in the configuration and change the section “passdb passwd-file {”.
passdb passwd-file {
# File contains a list of usernames, one per line
args = /etc/vmail/%d/passwd
deny = no
}
This specifies that for verifying passwords, dovecot can look in our passwd file for the virtual domain. Notice the %d variable in there again. “deny = no” specifies that dovecot should grant access if the password matches.
Now scroll a little further and edit the section “userdb passwd-file {”:
userdb passwd-file {
# Path for passwd-file
args = /etc/vmail/%d/passwd
}
This simply allows dovecot to use the same passwd file to find the user information after verifying the password. We should be done now
A final test!
Restart dovecot. Now try to telnet to localhost on port 143 again. This time logging in as “pete@example.com” with the password “petes-password”. Then we’ll try selecting pete’s inbox. The command sequence is:
a01 LOGIN pete@example.com petes-password
a02 SELECT inbox
a03 LOGOUT
You should see something like this:
w3style.co.uk:~# telnet localhost 143
Trying 127.0.0.1…
Connected to mail.w3style.co.uk.
Escape character is ‘^]’.
* OK Dovecot ready.
a01 LOGIN pete@example.com petes-password
a01 OK Logged in.
a02 SELECT inbox
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft NonJunk $NotJunk $Junk JunkRecorded $MDNSent $Forwarded)
* OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft NonJunk $NotJunk $Junk JunkRecorded $MDNSent $Forwarded \*)] Flags permitted.
* 1 EXISTS
* 0 RECENT
* OK [UIDVALIDITY 1164274548] UIDs valid
* OK [UIDNEXT 2] Predicted next UID
a02 OK [READ-WRITE] Select completed.
a03 LOGOUT
* BYE Logging out
a03 OK Logout completed.
Connection closed by foreign host.
Notice the line “* 1 EXISTS”. This is the email we sent when we were testing Exim. This means it worked! If you see “0 EXISTS” or you cannot actually log in then something has gone wrong and Dovecot is looking in the wrong place for the emails and/or login files.
If you were to try setting up your email client such as thunderbird or evolution using your server as an imap server with those credentials you should be able to read your emails successfully. If someone sends an email to that email address, assuming there’s a MX record pointing it to your server then you should be able to access the email over IMAP.
To add new domains simply create new passwd files in /etc/vmail/domain.tld. To change passwords or add users simply edit the contents of the relevant passwd file.
Good luck!
Recent Comments