Dovecot/postfix multiple ssl domains

11 posts / 0 new
Last post
#1 Sun, 04/16/2017 - 17:03

Dovecot/postfix multiple ssl domains

Hi, I have a new server (Ubuntu 16.04, VIrtualmin 5.07) with multiple IP addresses. The server has a main IP address which I use for general administration, and then it has a block of 16 IP addresses each of which I can use to host a separate SSL domain.

I am using Let's Encrypt as my CA and use the standard Virtualmin SSL Certificate management to request these certificates. This all works perfectly.

I have created 2 domains on the server and have the SSL certificates installed.

I am now trying to get dovecot and postfix working with multiple certificates.

If I use the per-domain configurator to manage SSL certificates and elect to copy the certificate to Dovecot, it appears as though a pem is created and copied into the root directory of the server, and this is used as the global ssl certificate for dovecot. In my /etc/dovecot/conf.d I see in 10.ssl.conf ssl_cert = </dovecot.cert.pem and ssl_key = </dovecot.key.pem.

If I attempt to set up a second domain for SSL and copy the certificate to dovecot, it overwrites the existing certificate in the root directory.

I have experimented with the configuration a bit and it appears as though it is quite simple to set up multiple certificates for dovecot on a per-domain basis simply by using the local directive in the 10.ssl.conf file as follows:

local a.b.c.d {
       ssl_cert = </home/domain0/ssl.cert
       ssl_key = </home/domain0/ssl.key

local e.f.g.h {
       ssl_cert = </home/domain1/ssl.cert
       ssl_key = </home/domain2/ssl.key

After restarting dovecot, I can use the command:

openssl s_client -connect domain0:imaps

to verify the configuration, and the correct certificate is presented. If I run the same command with domain1, likewise, the correct certificate is presented.

There is similar behaviour with postfix, and there is also a similar mechanism to overcome this issue in postfix as well.

Is there any possibility of incorporating this advanced behaviour into the default Virtualmin?

Wed, 08/09/2017 - 14:06

Any update on this feature request?

I have about 5 different virtual servers running and I would like to have Virtualmin managing separate dovecot/postfix SSL certificates for each virtual server.

Is this going to be available somewhere soon?

Mon, 09/18/2017 - 21:07
stom's picture

I've also found myself needing this feature. I have multiple domains on a single machine (although on a single IP) and would like to be able to have them all have their own SSL cert for Postfix. All to stop that little ? popping up in Gmail...

Tue, 09/19/2017 - 02:51 (Reply to #4)
Joe's picture

It'll never be possible on a single IP. Nothing Virtualmin can do about that; the protocols aren't name-based. You'll need to just let all of your clients connect to the name that matches your certificate.


Check out the forum guidelines!

Tue, 09/19/2017 - 02:55
Joe's picture

This hasn't really been on our radar. It's pretty rarely come up. We have per-domain outgoing IP address configuration, but it's not been exceedingly popular (and I kinda consider it some point, you have to start thinking, "OK maybe this client needs a virtual machine of their own, with Virtualmin on it").

I'll confer with Jamie on how difficult it would be. The Dovecot example above looks pretty reasonable, but I don't think Postfix is anywhere near that simple.

I'd thought that to do it with Postfix you had to have multiple servers running, but the comment above seems to indicate that's not true. Can someone post what the Postfix configuration looks like for having different IP addresses and different certificates?


Check out the forum guidelines!

Sat, 08/04/2018 - 10:34 (Reply to #6)

Hi Joe,

Sorry for the extremely long delay in replying to this thread. Just a few weeks ago I worked on this again and managed to get it working properly for postfix as well, and, it is actually not that difficult, but it is quite longwinded.

I will post anonymised versions of both my and, although for the purposes of getting postfix to use separate certificates for each domain, the only changes required are in the file.

The server in this example has a main IP address, which was the original IP address provided with the server, I will refer to this server as xxx.yyy.zzz.1, and the hostname server.tld, I then illustrate 2 additional domains, with IP addresses xxx.yyy.zzz.2 and xxx.yyy.zzz.3, with names domain1.tld and domain2.tld.

With this configuration, the server will accept connections on port 25, 465 and 587, with port 465 used for SMTPS connection and 587 for STARTTLS. In addition, the SMTP server will announce itself with the domain name of the domain associated with the IP address, and when certificates are used, the correct certificate will be presented for the individual domain.

The is as follows (although this can really be the default of the installed server):

# See /usr/share/postfix/ for a commented, more complete version

# Debian specific:  Specifying a file name will cause the first
# line of that file to be used as the name.  The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname

smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h

readme_directory = no

# TLS parameters
smtpd_tls_cert_file = /etc/postfix/postfix.cert.pem
smtpd_tls_key_file = /etc/postfix/postfix.key.pem
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

message_size_limit = 104857600

# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.

smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination reject_rbl_client
myhostname = server.tld
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = $myhostname,, , localhost
relayhost =
mynetworks = [::ffff:]/104 [::1]/128
mailbox_command = /usr/bin/procmail-wrapper -o -a $DOMAIN -d $LOGNAME
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
virtual_alias_maps = hash:/etc/postfix/virtual
sender_bcc_maps = hash:/etc/postfix/bcc
sender_dependent_default_transport_maps = hash:/etc/postfix/sender_transport
home_mailbox = Maildir/
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtpd_recipient_restrictions = permit_mynetworks permit_sasl_authenticated reject_unauth_destination reject_rbl_client check_policy_service inet:
allow_percent_hack = no
smtpd_tls_CAfile = /etc/postfix/
smtpd_tls_security_level = may
smtpd_tls_mandatory_ciphers = high
milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
smtp_tls_security_level = dane

Now we come to the file. What one must bear in mind is that any setting in the file can be overridden in the file, and it can be done on a per IP and protocol version. There are probably better ways of doing this, I am not a postfix expert, and I have not done much additional experimentation, and the posted configuration 'works for me'!

# Postfix master process configuration file.  For details on the format
# of the file, see the master(5) manual page (command: "man 5 master" or
# on-line:
# Do not forget to execute "postfix reload" after editing this file.
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (no)    (never) (100)
# ==========================================================================
xxx.yyy.zzz.1:smtp    inet    n       -       y       -       -       smtpd
#smtp      inet  n       -       y       -       1       postscreen
#smtpd     pass  -       -       y       -       -       smtpd
#dnsblog   unix  -       -       y       -       0       dnsblog
#tlsproxy  unix  -       -       y       -       0       tlsproxy
#submission inet n       -       y       -       -       smtpd
#  -o syslog_name=postfix/submission
#  -o smtpd_tls_security_level=encrypt
#  -o smtpd_sasl_auth_enable=yes
#  -o smtpd_reject_unlisted_recipient=no
#  -o smtpd_client_restrictions=$mua_client_restrictions
#  -o smtpd_helo_restrictions=$mua_helo_restrictions
#  -o smtpd_sender_restrictions=$mua_sender_restrictions
#  -o smtpd_recipient_restrictions=
#  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
#  -o milter_macro_daemon_name=ORIGINATING
#smtps  inet    n       -       y       -       -       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_recipient_restrictions= -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING
#628       inet  n       -       y       -       -       qmqpd
pickup    unix  n       -       y       60      1       pickup
cleanup   unix  n       -       y       -       0       cleanup
qmgr      unix  n       -       n       300     1       qmgr
#qmgr     unix  n       -       n       300     1       oqmgr
tlsmgr    unix  -       -       y       1000?   1       tlsmgr
rewrite   unix  -       -       y       -       -       trivial-rewrite
bounce    unix  -       -       y       -       0       bounce
defer     unix  -       -       y       -       0       bounce
trace     unix  -       -       y       -       0       bounce
verify    unix  -       -       y       -       1       verify
flush     unix  n       -       y       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       y       -       -       smtp
relay     unix  -       -       y       -       -       smtp
#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq     unix  n       -       y       -       -       showq
error     unix  -       -       y       -       -       error
retry     unix  -       -       y       -       -       error
discard   unix  -       -       y       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       y       -       -       lmtp
anvil     unix  -       -       y       -       1       anvil
scache    unix  -       -       y       -       1       scache
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
# Many of the following services use the Postfix pipe(8) delivery
# agent.  See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in maildrop_destination_recipient_limit=1
maildrop  unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
# ====================================================================
# Recent Cyrus versions can use the existing "lmtp" entry.
# Specify in cyrus.conf:
#   lmtp    cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
# Specify in one or more of the following:
#  mailbox_transport = lmtp:inet:localhost
#  virtual_transport = lmtp:inet:localhost
# ====================================================================
# Cyrus 2.1.5 (Amos Gouaux)
# Also specify in cyrus_destination_recipient_limit=1
#cyrus     unix  -       n       n       -       -       pipe
#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
# ====================================================================
# Old example of delivery via Cyrus.
#old-cyrus unix  -       n       n       -       -       pipe
#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
# ====================================================================
# See the Postfix UUCP_README file for configuration details.
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
# Other external delivery methods.
ifmail    unix  -       n       n       -       -       pipe
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp     unix  -       n       n       -       -       pipe
  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix  -       n       n       -       2       pipe
  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman   unix  -       n       n       -       -       pipe
  flags=FR user=list argv=/usr/lib/mailman/bin/
  ${nexthop} ${user}

# localhost  inet    n       -       y       -       -       smtpd    inet    n       -       y       -       -       smtpd

# server ip
xxx.yyy.zzz.1:submission      inet    n       -       y       -       -       smtpd

# domain1.tld
xxx.yyy.zzz.2:smtp     inet    n       -       y       -       -       smtpd
-o smtpd_tls_cert_file=/home/domain1/ssl.cert
-o smtpd_tls_key_file=/home/domain1/ssl.key
-o smtpd_tls_CAfile=/home/domain1/
-o myhostname=domain1.tld
xxx.yyy.zzz.2:smtps    inet    n       -       y       -       -       smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_cert_file=/home/domain1/ssl.cert
-o smtpd_tls_key_file=/home/domain1/ssl.key
-o smtpd_tls_CAfile=/home/domain1/
-o smtpd_tls_wrappermode=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_recipient_restrictions=
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
-o myhostname=domain1.tld
xxx.yyy.zzz.2:submission       inet    n       -       y       -       -       smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_tls_cert_file=/home/domain1/ssl.cert
-o smtpd_tls_key_file=/home/domain1/ssl.key
-o smtpd_tls_CAfile=/home/domain1/
-o milter_macro_daemon_name=ORIGINATING
-o myhostname=domain1.tld

# domain2.tld
xxx.yyy.zzz.3:smtp     inet    n       -       y       -       -       smtpd
-o smtpd_tls_cert_file=/home/domain2/ssl.cert
-o smtpd_tls_key_file=/home/domain2/ssl.key
-o smtpd_tls_CAfile=/home/domain2/
-o myhostname=domain2.tld
xxx.yyy.zzz.3:smtps    inet    n       -       y       -       -       smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_cert_file=/home/domain2/ssl.cert
-o smtpd_tls_key_file=/home/domain2/ssl.key
-o smtpd_tls_CAfile=/home/domain2/
-o smtpd_tls_wrappermode=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_recipient_restrictions=
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
-o myhostname=domain2.tld
xxx.yyy.zzz.3:submission       inet    n       -       y       -       -       smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_tls_cert_file=/home/domain2/ssl.cert
-o smtpd_tls_key_file=/home/domain2/ssl.key
-o smtpd_tls_CAfile=/home/domain2/
-o milter_macro_daemon_name=ORIGINATING
-o myhostname=domain2.tld

On the original server I have 11 secure domains, what is also great is that if one of them happens to be injected with some sort of spambot, and it starts spamming (although this has never happened to me because I have great methods of checking for security breaches on my servers - using git by for this by the way!), it does not affect any of the other domains on the same server. It would also be possible to host insecure sites on the base IP address, and in this case, it would also be possible to set up a common mail server for clients on the base IP address, but in this instance I do not - I offer a premium service to clients on this particular server.

From the you will also note I am using spamhaus which I find extremely effective in combination with greylisting to combat spam, I very seldom get any clients complaining of being spammed, and certainly with my spam filtering techniques, I am unaware of any mails being lost into spam filters.

This is clearly a solution for a server with multiple IP addresses, it is NOT a solution for hosting multiple servers on the same IP address.

I hope this is in some way useful, and could potentially enhance a wonderful product. I certainly hope if it finds it's way to inclusion in Virtualmin, along with the Dovecot solution.


Tue, 09/19/2017 - 03:31

I just tell my clients to use the server domain for sending and receiving rather than their own domain name. I know it's maybe better to use their domain but it's simple with one domain and only one SSL cert to worry about ;o)

Wed, 09/20/2017 - 12:18

There is an (admittedly quite crude) way to easily get all the customer(s) e-mail (or also other) sub-domains into one single certificate.

For Apache it would look like this (nginx should be able to do the same):

# Globally redirect some ACME challenges to /var/www/html
# We want certificates for those subdomains because they
# are used for Webmin, Usermin, Dovecot and Postfix.
<If "%{HTTP_HOST} =~ /^(admin|webmail|mail)[.].+/">
  Redirect /.well-known/acme-challenge/ https://${ServerName}/.well-known/acme-challenge/

That way I can force the validation root directory to /var/www/html and get a certificate with all the alternate names without having to add any additional vhosts (or rely on dns validation). image
The only thing missing for me is a "Copy new key and certificate to Webmin, Usermin, Dovecot and Postfix" button, so I'll have to that manually for now. (Also, the email-client auto-config for some domains doesn't use the mail subdomain, but imap/pop/smtp instead, but I haven't found a way to fix that yet)

Wed, 09/20/2017 - 16:24 (Reply to #9)
Joe's picture

Holy crap, that's a great idea! I have the same problem for our software mirror servers and didn't even think to use a redirect to do it. I wonder if it works across IP addresses...

Jamie and I talked about ways to automate this in Virtualmin in the past but didn't come up with a solution, because there's no space for hooks in the steps of certificate validation; i.e. the same command handles generating the ACME challenge and triggering the validation request, so there's no place in the workflow to make it copy the challenge to other locations (in my use case, I wanted to span multiple servers for the same domain, rather than having many domains share one certificate, but the solution seems similar). An option I did come up with was to use a proxy rule on the secondary servers so even if that get contacted by LE, they'd server the same data from the master server, but that isn't easy to make generic.

So, this is an area that's in flux, I think. We're open to adding something to Virtualmin to handle validation for multiple domains or for multiple servers sharing the same domain...just not really sure what the cleanest solution is, yet.


Check out the forum guidelines!

Sat, 12/30/2017 - 15:55

There is a more extensive redirect solution from 2016 (April) here:

However, one never knows, when one is looking at an "old solution" whether it still holds let alone is best practice under the current system.

Sat, 05/04/2019 - 02:17

Hi All. I know this an oldish thread, but thought this was the best place to raise this again. @Joe had mentioned that this wasn't really on the radar. However I would disagree, and say that this is a critical area to look at for Virtualmin. My usage case, which I'm sure a lot of other users face, is that I've ported over a lot of Cpanel accounts to a Virtualmin server. For Virtualmin we should not be playing second fiddle to Cpanel, and if we want to users to migrate away from that platform to Virtualmin, we need functionality that matches or exceeds those of Cpanel in critical areas. This area is critical. So when I move over a whole lot of Virtual Servers from Cpanel, most of those have mail.domain.x as their mx settings. So having something that would work out of the box, with accounts being transfered over will make matters much easier. To have to go and tell all the clients being moved over to change their mail settings is inefficient and is a waste of unnecessary time. I think Virtualmin should look at this carefully, as it also relates to scale. Say - if I had four different servers running Virtualmin, and I needed to move new users over to a different server, they would be required to change their mx records to match the "new" virtualmin server mail main settings. However if they were all running correctly on - this would not be a problem, and one could move clients between different servers without having clients change their mail server settings in their respective clients.

Anyway I'm rambling on, but I assume that everyone can get the gist of what I'm trying to say. Looking forward to comments and a hopeful solution to this.

Topic locked