# postfix, virtual domains - hows everyone do it?



## BobBilly5 (Apr 16, 2010)

Hello,

   Currently I have qmail + vpopmail for all my email servers.  Also I have 'qmailadmin' which lets users completely manage their own domains.  No database, or ldap server is required [KISS] They can point and click create users, delete users, change passwords, etc - not bothering me.  This setup works great except for when you throw in the smtp auth, tls/ssl, and other patches into qmail - It gets weird.

I'm looking to migrating everything to postfix, and read a ton of info on how to setup postfix with a DB backend, etc.etc - but none of those tell me how to manage that DB and allow users to manage their own domains with point and click...

 [ and yes I know about postfix + vpopmail + dovecot LDA , which would be minimal configuration change except to shutdown qmail-smtpd and fire up postfix, but not something I want to do ]

Question/Survey:
  How does everyone else do postfix, virtual domains/users, and then allowing [non technical] users to manage their own domains?

  I'm also thinking more and more about ldap to have the same login info work for ftp/ssh/smtp-auth/imap, but what [web based] interface can users use to easily manage their domains?

  So far I've only been able to come up with postfixadmin [which requires an SQL server aka another point of failure]

So hows everyone doing virtual domains in a small medium environment?

enterprise environment ?


----------



## hydra (Apr 16, 2010)

Postfix + Dovecot + LDAP + (phpLDAPadmin | Phamm | Jamm) maybe ?

I'm currently migrating a Postfixadmin solution to LDAP. Most likely, I will use phpLDAPadmin, however it is just for a single company and no users are to update the domain list. The password can be changed with a Webmail for example. This is not a recommendation, just the place I got when searching for a similar solution.


----------



## cajunman4life (Apr 17, 2010)

I use postfix in a fairly standard manner (no db backend). I've written a few shell scripts that users can run that allows them to make modifications to their email domains. New domains are added and old domains removed by me only. Once they are added, however, they can add/remove/change password for their own accounts on that domain.

Quite obviously this will only work if you allow shell access to your server. Otherwise it wouldn't take much (if you have a cursory perl knowledge) to write a simple web front-end to accomplish the same.

If you're not in to doing that work yourself, someone else may know of a good pre-packaged solution


----------



## BobBilly5 (Apr 17, 2010)

cajunman4life said:
			
		

> I use postfix in a fairly standard manner (no db backend). I've written a few shell scripts that users can run that allows them to make modifications to their email domains. New domains are added and old domains removed by me only. Once they are added, however, they can add/remove/change password for their own accounts on that domain.



With no db backend are all the users info stored in /etc/passwd, text file, ?
  If /etc/passwd: What about duplicate users like bob@.com and bob@.net, do you just have a mapping for emails to bob@.com = bob/Maildir/ and bob@.net = bob1/Maildir/ ?

With vpopmail all the users are virtual and then again, most will be confused on having to use something except a web interface.


----------



## cajunman4life (Apr 17, 2010)

BobBilly5 said:
			
		

> With no db backend are all the users info stored in /etc/passwd, text file, ?
> If /etc/passwd: What about duplicate users like bob@.com and bob@.net, do you just have a mapping for emails to bob@.com = bob/Maildir/ and bob@.net = bob1/Maildir/ ?
> 
> With vpopmail all the users are virtual and then again, most will be confused on having to use something except a web interface.



I have a setup whereby each domain has it's own folder under /usr/local/etc/vmail.

From there, there would be a folder, let's say foo.com, which would be for that domain. Inside that folder are 2 files, auth and passwd. The passwd file is what is authenticated against by dovecot for pop/imap login, and the auth file is used by the shell scripts to tell which user can modify the accounts for this particular domain (the auth file also includes some custom information in my setup, like how many accounts and forwarders the domain is authorized, etc).

Now that I think about it, it's quite an intricate setup. Surprised I wasn't too lazy to accomplish it lol.

Edit: I realized now exactly why I went to so much trouble to set it up this way. I seem to remember that if I wanted to do the db back end I was going to have to make every account a virtual account. I didn't want to do that as I didn't want to mess with the local system accounts' mail. I also managed to accomplish a crazy method of local system users being able to use the webmail via ACL's and symlinks. If you're really interested, I can post code/configs that shows how I've managed to make this work.


----------



## BobBilly5 (Apr 17, 2010)

cajunman4life said:
			
		

> If you're really interested, I can post code/configs that shows how I've managed to make this work.



Yeah would like to see under the hood....

But this really sounds like it would be easier to just use vpopmail [it does work with postfix, but still requires the qmail binary...]

It would  be the same thing, auth via dovecot -> vpopmail and with vpopmail I'd have all the fancy domain management stuff I have now and wouldn't need to write any scripts and would be minimal configuration change except to shutdown qmail-smtpd and fire up postfix smtpd.


----------



## cajunman4life (Apr 17, 2010)

BobBilly5 said:
			
		

> Yeah would like to see under the hood....



Here goes nothing.

This is the shell script used to modify domains (mkvmail):

```
#!/bin/sh

echo "**** Virtual Email manager"
echo
printf "Enter domain (or 'quit'): "
read _domain
#if [ "$_domain" = "quit" ]; then
if [ `printf "$_domain" | tr '[a-z]' '[A-Z]'` = "QUIT" ]; then
 exit 0
fi

# If no domain, exit. 
if [ -z $_domain ]; then
 echo "No domain, exiting."
 exit 1
fi

# If domain does not already exist, we need sysadmin to create it.
if [ ! -d "/etc/vmail/$_domain" ]; then
 echo "Error: Domain $_domain does not seem to exist on this system."
 echo "Contact the system administrator (sysadmin@****)"
 exit 1
fi

# Check for auth file
if [ ! -f "/etc/vmail/$_domain/auth" ]; then
 echo "Authority file does not exist for domain $_domain."
 echo "Unable to authenticate. Please alert the system administrator."
 exit 1
fi

# Check for passwd file
if [ ! -f "/etc/vmail/$_domain/passwd" ]; then
 touch /etc/vmail/$_domain/passwd
 chmod 644 /etc/vmail/$_domain/passwd
 echo "Domain passwd file created."
fi

# Verify domain authority
_me=`id -urn`
_auth=`cat /etc/vmail/$_domain/auth | grep "admin" | awk '{print $2}'`
_authl=`echo $_auth | sed "s/,/ /g"`
_can_admin=`echo $_authl | grep "$_me"`
if [ -z "$_can_admin" ]; then
 echo "Error: You are not a domain administrator for $_domain."
 exit 1
fi

# Now the meaty stuff. Edit the domain config.
# Get maximum email accounts
_max=`/bin/cat /etc/vmail/$_domain/auth | grep "max" | awk '{print $2}'`
_forward=`/bin/cat /etc/vmail/$_domain/auth | grep "forward" | awk '{print $2}'`
_fcount=`/bin/cat /etc/postfix/virtual | grep "@$_domain" | wc -l`
while [ true ]
do
echo "vmail editor - $_domain" | tr '[a-z]' '[A-Z]'
echo
printf "You are using %s of %s addresses, and %s of %s forwarders.\n" `cat /etc/vmail/$_domain/passwd | wc -l` $_max $_fcount $_forward
echo
printf "(1)\tList email addresses\n"
printf "(2)\tNew email account\n"
printf "(3)\tRemove email account\n"
printf "(4)\tChange password\n"
printf "(0)\tExit\n\n"
printf "What would you like to do? "
read _command
case $_command
in
 0) # Exit
  echo "Closing editor."
  exit 0
 ;;
 1) # List
  echo "Email address listing for $_domain"
  echo
  _elist=`/bin/cat /etc/vmail/$_domain/passwd | sed "s/:/ /g" | awk '{print $1}'`
  for i in $_elist
  do
   printf "$i@$_domain\n"
  done
  printf "\nPress enter to continue..."
  read _anykey
 ;;
 2) # Add
  # If we're at max, don't let them add!
  if [ `cat /etc/vmail/$_domain/passwd | wc -l` -ge $_max ]; then
   echo "You are at your maximum allowed email accounts. Sorry."
   echo "Try removing some unused accounts first."
   echo "If you need more accounts, contact sysadmin@****"
   printf "Press enter to continue..."
   read _who_cares
   continue
  fi
  echo "Adding email address for $_domain"
  echo
  printf "Enter the new email address, WITHOUT the domain: "
  read _new_email
  # Just in case some bonehead typed in domain anyways...
  _t=`echo $_new_email | sed "s/ //g" | sed "s/@/ /g" | awk '{print $1}'`
  _new_email=$_t
  printf "Adding $_new_email@$_domain... "
  _pwd=`dovecotpw -s ssha`
  # Check what domain we're in
  if [ "$_domain" != "****" ]; then
   touch /var/vmail/$_domain/$_new_email
   chown vmail:vmail /var/vmail/$_domain/$_new_email
   ## Send to runq
   printf "$_new_email@$_domain\n" >> /etc/vmail/runq.add
  fi
  ##  
  ## **** LOCKS! Oh my God we need locks.
  ##  
  if [ -f /etc/vmail/$_domain/dotlock ]; then
   printf "The passwd database is currently locked. Please try again later.\n"
   printf "Press enter to continue..."
   read _whocares
   continue
  fi  
  echo "`id -urn`" > /etc/vmail/$_domain/dotlock        # Set lock
  echo "$_new_email:$_pwd" >> /etc/vmail/$_domain/passwd
  rm -rf /etc/vmail/$_domain/dotlock                    # Remove lock
  echo "Email account $_new_email@$_domain added."
  echo "Please note that it may take up to 15 minutes to syncronize the system."
  echo "During this time, you may not receive emails addressed to you."
 ;;
 3) # Remove
  printf "Enter email you wish to remove (WITHOUT the domain): "
  read _remove
  # Just in case some bonehead typed in domain anyways...
  _t=`echo $_remove | sed "s/ //g" | sed "s/@/ /g" | awk '{print $1}'`
  _remove=$_t
  echo "This will remove $_remove@$_domain, are you sure you want to do this?"
  printf "(Y/N)? "
  read _confirm
  if [ "$_confirm" = "Y" ] || [ "$_confirm" = "y" ]; then
   _list=`/bin/cat /etc/vmail/$_domain/passwd | grep -v ^$_remove` #| tr ' ' '\n'`
   #echo $_list
   #exit 0
   printf $_list | tr ' ' '\n' > /etc/vmail/$_domain/passwd
   # We need to remove the mailboxes also
   if [ -f /var/vmail/$_domain/$_remove ]; then
    rm -rf /var/vmail/$_domain/$_remove
   fi
   # IMAP
   if [ -d /var/vmail/home/$_remove@$_domain ]; then
    rm -rf /var/vmail/home/$_remove@$_domain
   fi
   # Now, add to runq to remove account
   echo "$_remove@$_domain" >> /etc/vmail/runq.remove
   echo "Email address removed."
   echo "Please note that it may take up to 15 minutes to syncronize the system."
  fi
 ;;
 4) # Change password
  printf "Enter email you wish to change password (WITHOUT the domain): "
  read _passwd
  # Just in case some bonehead typed in domain anyways...
  _t=`echo $_passwd | sed "s/ //g" | sed "s/@/ /g" | awk '{print $1}'`
  _passwd=$_t
  /usr/local/libexec/pop3pass $_passwd@$_domain
 ;;
 *)
  printf "Invalid selection, please try again\n\n"
  #read _bs
 ;; 
esac
done
```

All the references above to /etc are softlinked to /usr/local/etc, I was too lazy to keep typing everything out long-wise. Believe it or not, it's still a work in progress, as there is no way for a user to add a forwarder yet.

Here's pop3pass, the utility used to set/reset a password:

```
#!/bin/sh

# For debug mode only
#if [ `id -urn` != "ajgraves" ]; then
# echo "pop3pass is in maintenance. Please try later."
# exit 1
#fi
#set -x
#echo `id -urn`
# End debug mode

_ulogin=`id -urn`

# Check ability to access
_acl1=`groups $_ulogin | tr ' ' '\n' | grep "members"`
_acl2=`groups $_ulogin | tr ' ' '\n' | grep "users"`

if [ -z $_acl1 ]; then
 if [ -z $_acl2 ]; then
  echo "You must be a member of at least the 'users' group to set/change a POP3 password."
  echo "Please consider upgrading your account."
  exit 1
 fi
fi

if [ -z $1 ]; then  # *** No argument, go interactive
 # Get email address
 printf "Enter email address to edit\n(or press enter for default of `id -urn`@****)\n"
 printf "> "
 read _email
 if [ "$_email" = "" ]; then
  _email="`id -urn`@****"
 fi
else
 _email=$1
fi

# Split email
_user=`echo $_email | sed "s/@/ /g" | awk '{print $1}'`
_domain=`echo $_email | sed "s/@/ /g" | awk '{print $2}'`
_me=`id -urn`

# Domain exists?
if [ ! -d "/etc/vmail/$_domain" ]; then
 echo "Domain $_domain does not exist on this system."
 echo "Contact your system administrator."
 exit 1
fi

# Nitty gritty
if [ "$_domain" != "****" ]; then
 # Check domain authority
 _auth=`/bin/cat /etc/vmail/$_domain/auth | grep ^admin | awk '{print $2}' | sed "s/,/ /g" | grep "$_me"`
 if [ "$_auth" = "" ]; then
  echo "You are not a domain administrator for $_domain. Sorry"
  exit 1
 fi
fi

# Technically for vmail domains we can only change passwords, as an account actually has to be created through mkvmail
_exists=`grep "$_user" < /usr/local/etc/vmail/$_domain/passwd`
# If domain = **** and no _exists, create user.
if [ "$_domain" = "****" ]; then
 if [ ! $_exists ]; then
  # ** CHECK DOTLOCK
  if [ -f "/usr/local/etc/vmail/$_domain/dotlock" ]; then
   echo "Sorry, the database is currently locked. Please try back in a few seconds."
   echo "If this error persists, contact the system administrator."
   exit 1
  fi
  # ** SET DOTLOCK
  echo `id -urn` > /usr/local/etc/vmail/$_domain/dotlock
  echo "$_user:ThisWillBeChangedSoon" >> /usr/local/etc/vmail/$_domain/passwd
  # ** REMOVE DOTLOCK
  rm -rf /usr/local/etc/vmail/$_domain/dotlock
  _exists="yes"
 fi
fi
# End of domain = **** necessary
if [ ! $_exists ]; then
 echo "User $_user does not exist in domain $_domain."
 echo "Please use 'mkvmail' to create the account."
 exit 1
fi
# ** CHECK DOTLOCK
if [ -f "/usr/local/etc/vmail/$_domain/dotlock" ]; then
 echo "Sorry, the database is currently locked. Please try back in a few seconds."
 echo "If this error persists, contact the system administrator."
 exit 1
fi
echo "Changing POP3 password for $_user@$_domain..."
echo "Press ^c (control + c) to cancel..."
_new_pwd=`/usr/local/sbin/dovecotpw -s ssha`
# ** SET DOTLOCK
echo `id -urn` > /usr/local/etc/vmail/$_domain/dotlock
_file_list=`/bin/cat /usr/local/etc/vmail/$_domain/passwd | grep -v ^$_user | tr ' ' '\n'`
printf "$_file_list" > /usr/local/etc/vmail/$_domain/passwd
if [ ! -z "$_file_list" ]; then         # If previous entries, add newline
 printf "\n" >> /usr/local/etc/vmail/$_domain/passwd
fi
printf "$_user:$_new_pwd\n" >> /usr/local/etc/vmail/$_domain/passwd
printf "Password successfully changed.\n"
# ** CLEAR DOTLOCK
rm -rf /usr/local/etc/vmail/$_domain/dotlock
# Set ACL if ****
if [ "$_domain" = "****" ]; then
 _acl_set=`getfacl /var/mail/$_user | grep "user:vmail:rw-"`
 if [ -z $_acl_set ]; then
  setfacl -m u:vmail:rw /var/mail/$_user
  echo "Mailbox permissions set."
 fi
fi
#exit 0
```

Continued below...


----------



## cajunman4life (Apr 17, 2010)

This is vmailrun, which runs via cron every 15 minutes to activate the changes performed by mkvmail (I was having nightmares about file locks if X number of users were attempting modifications all at once directly to the postfix config, so this was my compromise).


```
#!/bin/sh

# vmailrun - processes the file /etc/vmail/runq
# sort -n -u < /etc/vmail/runq

#_runq=`/bin/cat /etc/vmail/runq | /usr/bin/sort | /usr/bin/uniq`
#printf "" > /etc/vmail/runq
#printf "$_runq"

_needadd=`cat /etc/vmail/runq.add | wc -l`
_needremove=`cat /etc/vmail/runq.remove | wc -l`

if [ "$_needadd" -ne "0" ]; then
  _add=`/bin/cat /etc/vmail/runq.add | /usr/bin/sort | /usr/bin/uniq`
  printf "" > /etc/vmail/runq.add
  #printf "I am adding:\n$_add\n"
fi
if [ "$_needremove" -ne "0" ]; then
  _remove=`/bin/cat /etc/vmail/runq.remove | /usr/bin/sort | /usr/bin/uniq`
  printf "" > /etc/vmail/runq.remove
  #printf "I am removing:\n$_remove\n"
fi

#printf "I am adding...\n"
#printf "$_add"
#printf "And I am removing...\n"
#printf "$_remove"

# If anything, let us know!
if [ ! -z "$_add" ] || [ ! -z "$_remove" ]; then
 printf "Adding/removing virtual mail accounts:\n\n"
fi

# if adding
if [ ! -z "$_add" ]; then
 for i in $_add
 do
  printf "Adding %s... " $i
  _u=`echo $i | sed "s/@/ /" | awk '{print $1}'`
  _d=`echo $i | sed "s/@/ /" | awk '{print $2}'`
  printf "$i\t$_d/$_u\n" >> /etc/postfix/vmail
  echo "done"
 done
 /usr/local/sbin/postmap /etc/postfix/vmail
 /usr/local/sbin/postfix reload
fi

# if removing
if [ ! -z "$_remove" ]; then
 _list=`/bin/cat /etc/postfix/vmail`
 for i in $_remove
 do
  printf "Removing %s... " $i
  _list=`printf "$_list" | grep -v ^$i`
  #printf "$_list\n"
  echo "done" 
 done
 printf "$_list\n" > /etc/postfix/vmail
 /usr/local/sbin/postmap /etc/postfix/vmail
 /usr/local/sbin/postfix reload
fi
```

Relevant section from "auth default" area of Dovecot config (this was the toughest to get everything to work nicely):

```
passdb passwd-file {
    # File contains a list of usernames, one per line
    args = username_format=%n /usr/local/etc/vmail/%d/passwd
    #deny = yes
  }
  userdb static {
    #args = uid=vmail gid=vmail home=/var/vmail/%d/%n
    args = uid=vmail gid=vmail home=/var/vmail/home/%u/
    #args = uid=vmail gid=vmail home=/home/vmail/%d/%n
  }
```

I think that's about everything. Everywhere in code where you see 4 *'s is where my system's primary domain is shown. I've redacted that.

Also, seeing as how I've loaded up a bunch of code I've written myself, with no license, I guess I'll say my standard: The 2-clause BSD license hereby covers all code I've posted. If you happen to think it's useful and want to use it, be my guest. Just don't go gettin all rich without having my name somewhere :e

I know I've inundated you with a bunch of stuff. Any questions feel free to ask.

Almost forgot, I have these scripts loaded in /usr/local/libexec, and I wrote a generic C program that calls these scripts. The C program was cc'd, moved to /usr/local/bin, and setuid properly to be able to execute the scripts.


----------



## BobBilly5 (Apr 17, 2010)

What can I say except 'nice!' - but...

You really should try using vpopmail + qmailadmin. 
  [with postfix as smtpd, dovecot as LDA]

Half my domains would be a 15 minute phone call each time on 'how do I ssh into the server again?' - but this is definitely a great start and something I can use for myself.

I like the simplicity of no DB, no LDAP in my current setup, and ease of domain management delegation - I guess no such thing for postfix.

I guess I'll work on just replacing qmail-smtpd with postfix but leaving everything else as it is.

Thanks for the examples, this gives me a MUCH better understanding of why all the howtos just mention the setup part, but never mention user/domain management.


----------



## cajunman4life (Apr 17, 2010)

BobBilly5 said:
			
		

> What can I say except 'nice!'



Thanks



			
				BobBilly5 said:
			
		

> You really should try using vpopmail + qmailadmin.
> [with postfix as smtpd, dovecot as LDA]



I might look into this, I'm hesitant however as I don't really like to fix anything that isn't broken. I get nervous about these sorts of things.



			
				BobBilly5 said:
			
		

> Half my domains would be a 15 minute phone call each time on 'how do I ssh into the server again?'



Ahh ok, I understand better now. All my users start with the shell first and go from there. 

Hope it helped a bit


----------

