Can I set a different certificate per listen port?

Paul Kudla (SCOM.CA Internet Services Inc.) paul at scom.ca
Thu Apr 28 10:48:21 UTC 2022


Technically yes (under 2.3.18 & SNI Support)

here is my sni.conf file it is imported at the end of my dovecot.conf

basically sni allows for multiple different certificates inside dovecot

please note each domain (or subdomain) config below

most of this was provided via

https://doc.dovecot.org/configuration_manual/dovecot_ssl_configuration/




in dovecot.conf (at the end)

#Addition ssl config
!include sni.conf

then make an sni.conf file adjust to your certificate / ssl needs etc


# cat  sni.conf
#sni.conf
ssl = yes
verbose_ssl = yes
ssl_dh =</usr/local/etc/dovecot/dh-4096.pem
ssl_prefer_server_ciphers = yes
#ssl_min_protocol = TLSv1.2

#Default *.scom.ca
ssl_key =</usr/local/etc/dovecot/scom.pem
ssl_cert =</usr/local/etc/dovecot/scom.pem
ssl_ca =</usr/local/etc/dovecot/scom.pem

local_name .scom.ca {
   ssl_key = /programs/common/getssl.cert -c *.scom.ca -q yes
   ssl_cert = /programs/common/getssl.cert -c *.scom.ca -q yes
   ssl_ca = /programs/common/getssl.cert -c *.scom.ca -q yes
}


local_name mail.clancyca.com {
   ssl_key = /programs/common/getssl.cert -c mail.clancyca.com -q yes
   ssl_cert = /programs/common/getssl.cert -c mail.clancyca.com -q yes
   ssl_ca = /programs/common/getssl.cert -c mail.clancyca.com -q yes
}

local_name secure.clancyca.com {
   ssl_key = /programs/common/getssl.cert -c secure.clancyca.com -q yes
   ssl_cert = /programs/common/getssl.cert -c secure.clancyca.com -q yes
   ssl_ca = /programs/common/getssl.cert -c secure.clancyca.com -q yes
}



Also note this is for incomming only connections

port should not matter sni matches the incoming dns name from the sni 
compatible client (thunderbird for example)

however do a telent to ip <port> to make sure there is an active 
listener on the port you are trying to use.

i already listen on 143 & 993 and this works for both.

Check for dovecot

# telnet localhost 143
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE 
LITERAL+ STARTTLS AUTH=PLAIN AUTH=LOGIN] SCOM.CA Internet Services Inc. 
- Dovecot ready


# imap service over SSL/TLS
openssl s_client -connect imap_dns_server_name_or_ip:993

note : imap_dns_server_name_or_ip is the actual name of the certificate 
you are trying to test for.


note when run you will/should get all the certificate stuff and it 
should then end with something like above :


read R BLOCK
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE 
LITERAL+ AUTH=PLAIN AUTH=LOGIN] SCOM.CA Internet Services Inc. - Dovecot 
ready


postfix outgoing test for reference.

# telnet localhost 465
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mail18.scom.ca ESMTP Postfix

// Send EHLO servername gives what the server can do.

EHLO scom.ca
250-mail18.scom.ca
250-PIPELINING
250-SIZE 1000000000
250-ETRN
250-STARTTLS
250-AUTH PLAIN LOGIN
250-AUTH=PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250 CHUNKING


sni only works if the mail client supports it so you need to run a mail 
client less then a few years old.

sni was developed with a lot of hit and misses and little documentation, 
and is still kinda being debugged but does work without issue for the 
most part inside postfix & dovecot.

i dont believe cyrus and others are fully supporting sni which makes 
this kinda a nice thing dovecot support wise - thanks to the developers.


Also note postfix also supports sni which needs to be setup seperately 
if used for outgoing emails.

main.cf

#SSL SNI
tls_server_sni_maps = hash:/usr/home/postfix/config/sni
smtpd_tls_chain_files = /etc/ssl/.scom.ca
smtpd_tls_CApath = /etc/ssl/certs

# cat sni.good
.scom.ca /etc/ssl/postfix.pem.scom
secure.mail.elirpa.com /etc/ssl/postfix.pem.elirpa

please note . at front indicates a *.scom.ca (for wildcard cert example 
above)

also note sni must be hashes into a sni.db file for postfix to be used

/usr/local/sbin/postmap -c /usr/home/postfix/config -F 
/usr/home/postfix/config/sni

will make the hash file from sni (text file)

please note i use pgsql database to store the certs etc hence the

/programs/common/getssl.cert -c mail.clancyca.com -q yes

it is from a django instance

text files would still be loaded with the < (pipe from file command)

both samples are noted above.


here is the python script i use to generate the dovecot ssl stuff

# cat /programs/common/getssl.cert
#!/usr/local/bin/python3
#update the ssl certificates for this mail server

import sys
import os
import string
import psycopg2

from optparse import OptionParser

USAGE_TEXT = '''\
usage: %%prog %s[options]
'''


parser = OptionParser(usage=USAGE_TEXT % '', version='0.4')
parser.add_option("-c", "--cert", dest="cert", help="Domain Certificate 
Requested")
parser.add_option("-k", "--key", dest="key", help="Domain Key Requested")
parser.add_option("-r", "--crt", dest="crt", help="Domain CRT Requested")
parser.add_option("-s", "--csr", dest="csr", help="Domain CSR Requested")
parser.add_option("-i", "--inter", dest="inter", help="Domain INTER 
Requested")
parser.add_option("-x", "--pem", dest="pem", help="Domain Pem Requested")
parser.add_option("-q", "--quiet", dest="quiet", help="Quiet")

options, args = parser.parse_args()

#print (options.quiet)

if options.cert != None :
         ssl = options.cert
         if options.quiet == None :
                 print ('\nGetting Full Pem Certificate : %s\n' 
%options.cert)


if options.key != None :
         ssl = options.key
         if options.quiet == None :
                 print ('\nGetting Key Certificate : %s\n' %options.key)


if options.crt != None :
         ssl = options.crt
         if options.quiet == None :
                 print ('\nGetting CRT Certificate : %s\n' %options.crt)

if options.csr != None :
         ssl = options.csr
         if options.quiet == None :
                 print ('\nGetting CSR Certificate : %s\n' %options.csr)

if options.inter != None :
         ssl = options.inter
         if options.quiet == None :
                 print ('\nGetting Inter Certificate : %s\n' %options.inter)

if options.pem != None :
         ssl = options.pem
         if options.quiet == None :
                 print ('\nGetting Pem Certificate : %s\n' %options.pem)



#sys.exit()


#from lib import *

#print ('Opening the Database ....')
conn = psycopg2.connect(host='localhost', port = 5433, 
database='db_table', user='pgsql', password='password')
pg = conn.cursor()

#print ('Connected !')

#Ok now go get the email keys
command = ("""select domain,ssl_key,ssl_cert,ssl_csr,ssl_chain from 
email_ssl_certificates where domain = $$%s$$ """ %ssl)
#print (command)

pg.execute(command)
certs = pg.fetchone()

#print (certs)

#ok from here we have to decide the output ?
domain = certs[0]

if options.cert != None :
         key = '#SSL Pem file (Key / Certificate / Intermediate) for 
%s\n\n#Key\n\n' %domain + certs[1] + '\n\n#Certificate\n' + certs[2] + 
'\n\n#Intermediate\n' + certs[4]

if options.key != None :
         key = '#SSL Key file for %s\n\n' %domain + certs[1]

if options.crt != None :
         key = '#SSL CERT file for %s\n\n' %domain + certs[2]

if options.csr != None :
         key = '#SSL CSR Request file for %s\n\n' %domain + certs[3]

if options.inter != None :
         key = '#SSL Intermediate file for %s\n\n' %domain + certs[4]

if options.pem != None :
         key = '#SSL Pem (Certificate / Intermediate) file for 
%s\n\n#Certificate\n\n' %domain + certs[2] + '\n\n#Intermediate\n' + 
certs[4]


key = key.replace('\r','')


print (key)


conn.close()
sys.exit()







Happy Thursday !!!
Thanks - paul

Paul Kudla


Scom.ca Internet Services <http://www.scom.ca>
004-1009 Byron Street South
Whitby, Ontario - Canada
L1N 4S3

Toronto 416.642.7266
Main 1.866.411.7266
Fax 1.888.892.7266

On 4/28/2022 4:01 AM, Kees van Vloten wrote:
> 
> 
> Op 28-04-2022 om 07:30 schreef Aki Tuomi:
>>> On 27/04/2022 22:14 Kees van Vloten <keesvanvloten at gmail.com> wrote:
>>>
>>> Hi all,
>>>
>>> I am trying to setup dovecot to listen to imaps on the local network and
>>> through haproxy from the internet.
>>>
>>> service imap-login {
>>>     inet_listener imaps {
>>>       port = 993
>>>       ssl = yes
>>>     }
>>>     inet_listener imaps_haproxy {
>>>       haproxy = yes
>>>       port = 10993
>>>       ssl = yes
>>>     }
>>> }
>>>
>>> Obviously the dns-name on the internet connection (10993) is different
>>> than on the lan (993).
>>>
>>> In the docs
>>> (https://doc.dovecot.org/configuration_manual/dovecot_ssl_configuration/) 
>>>
>>> I found multiple options, but unfortunately none of those have the
>>> option to distinguish per listen port.
>>>
>>> Is there a way to setup two different certificates for the two 
>>> listeners?
>>>
>>> - Kees
>> Hi!
>>
>> Currently port is not supported. What we usually recommend here is 
>> that you use haproxy to distribute connections to different local IP 
>> addresses and use
>>
>> local 127.0.0.5/32 {
>>    ssl_cert=</path
>>    ssl_key=</path
>> }
>>
>> Aki
> 
> Hi Aki,
> 
> Would it then look like this?
> 
> 
> Internet -> haproxy on dmz-server -> haproxy on mailserver -> dovecot on 
> 127.0.0.5
> 
> 
> - Kees
> 
> 
> 
> 


More information about the dovecot mailing list