How to use the new server TLS SNI feature (3.4.x)

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

How to use the new server TLS SNI feature (3.4.x)

Andrei Kovacs
Hi,

I've noticed the release of the new SNI feature in Postfix 3.4, but I cannot get a successful setup. My last attempt was to use tls_server_sni_maps, but I'm not sure about the correct format (I've tried encoding the certificate as base64 according to the documentation).

For reference, what I'm trying is to have a main certificate for the mail server and another certificate (letsencrypt) for a specific domain.

Thank you,
Andrei
Reply | Threaded
Open this post in threaded view
|

Re: How to use the new server TLS SNI feature (3.4.x)

Wietse Venema
Andrei Kovacs:
> Hi,
>
> I've noticed the release of the new SNI feature in Postfix 3.4, but I
> cannot get a successful setup. My last attempt was to
> use tls_server_sni_maps, but I'm not sure about the correct format (I've
> tried encoding the certificate as base64 according to the documentation).
>
> For reference, what I'm trying is to have a main certificate for the mail
> server and another certificate (letsencrypt) for a specific domain.

In addition to "it does not work" perhaps you can share concrete details:

- Output from
  postconf -n

- Output from
  openssl s_client -servername sni-name-here -starttls smtp -connect host:port

- Postfix logging for that connection.

        Wietse
Reply | Threaded
Open this post in threaded view
|

Re: How to use the new server TLS SNI feature (3.4.x)

Viktor Dukhovni
In reply to this post by Andrei Kovacs
On Sun, Mar 31, 2019 at 01:52:52PM +0300, Andrei Kovacs wrote:

> I've noticed the release of the new SNI feature in Postfix 3.4, but I
> cannot get a successful setup.

Sorry about that.  I assume you've read:

    http://www.postfix.org/postconf.5.html#tls_server_sni_maps

    The mapping from an SNI domain name to a certificate chain is
    typically indirect. In the input source files for "cdb", "hash",
    "btree" or other tables that are converted to on-disk indexed
    files via postmap(1), the value specified for each key is a
    list of filenames. When postmap(1) is used with the -F option,
    the generated table stores for each lookup key the base64-encoded
    contents of the associated files. When querying tables via
    postmap -Fq, the table value is decoded from base64, yielding
    the original file content, plus a new line.

    With "regexp", "pcre", "inline", "texthash", "static" and similar
    tables that are interpreted at run-time, and don't have a
    separate source format, the table value is again a list files,
    that are read-in when the table is opened.

> My last attempt was to use tls_server_sni_maps,

There is no other mechanism, so not clear what you might have tried
first.

> but I'm not sure about the correct format (I've tried encoding the
> certificate as base64 according to the documentation).

The documentation says:

                       When postmap(1) is used with the -F option,
    the generated table stores for each lookup key the base64-encoded
    -------------------
    contents of the associated files.  When querying tables via
    postmap -Fq, the table value is decoded from base64, yielding
    the original file content, plus a new line.

That is, the base64 encoding/decoding is done *internally*, you are
not expected to (i.e. required to not) pre-encode the certificate
chain file(s).  The only case in you need to do base64 encoding is
when you want (likely unwisely) to store the chain files in an SQL
database:

    With tables whose content is managed outside of Postfix, such
    as LDAP, MySQL, PostgreSQL, socketmap and tcp, the value must
    be a concatenation of the desired PEM keys and certificate
    chains, that is then further encoded to yield a single-line
    base64 string. Creation of such tables and secure storage are
    outside the responsibility of Postfix. With "socketmap" and
    "tcp" the data would be transmitted in the clear. With LDAP and
    SQL, you should generally use TLS to protect the sensitive data
    in transit.

What's missing from the "tls_server_sni_maps" documentation (sorry
about that) is a reference to the

        http://www.postfix.org/postconf.5.html#smtpd_tls_chain_files

documentation, which covers the expected file format.  The syntax
for the RHS values of the (non-IPC) lookup tables used with
tls_server_sni_maps is the same as with the "chain files" parameter.

--
        Viktor.
Reply | Threaded
Open this post in threaded view
|

Re: How to use the new server TLS SNI feature (3.4.x)

Andrei Kovacs
So, if I understand correctly, the configuration for the SNI file if it is in hash format should be:

domain1.com static:/path/to/pem1.pem
domain2.com static:/path/to/pem2.pem

Is it correct? And if so, the pem1.pem and pem2.pem files should contain in the exact order the PEMs for Private Key, Certificate and Chain, right? In case of Let's Encrypt that would be privkey.pem + cert.pem + chain.pem.

And... what about:

"Note that the SNI lookup tables should also have entries for the domains that correspond to the Postfix SMTP server's default certificate(s). This ensures that the remote SMTP client's TLS SNI extension gets a positive response when it specifies one of the Postfix SMTP server's default domains, and ensures that the Postfix SMTP server will not log an SNI name mismatch for such a domain. The Postfix SMTP server's default certificates are then only used when the client sends no SNI or when it sends SNI with a domain that the server knows no certificate(s) for."

... how should this be speficifed? Like "default static:/path/to/default.pem"?

Thank you,
Andrei

On Mon, Apr 1, 2019 at 12:31 AM Viktor Dukhovni <[hidden email]> wrote:
On Sun, Mar 31, 2019 at 01:52:52PM +0300, Andrei Kovacs wrote:

> I've noticed the release of the new SNI feature in Postfix 3.4, but I
> cannot get a successful setup.

Sorry about that.  I assume you've read:

    http://www.postfix.org/postconf.5.html#tls_server_sni_maps

    The mapping from an SNI domain name to a certificate chain is
    typically indirect. In the input source files for "cdb", "hash",
    "btree" or other tables that are converted to on-disk indexed
    files via postmap(1), the value specified for each key is a
    list of filenames. When postmap(1) is used with the -F option,
    the generated table stores for each lookup key the base64-encoded
    contents of the associated files. When querying tables via
    postmap -Fq, the table value is decoded from base64, yielding
    the original file content, plus a new line.

    With "regexp", "pcre", "inline", "texthash", "static" and similar
    tables that are interpreted at run-time, and don't have a
    separate source format, the table value is again a list files,
    that are read-in when the table is opened.

> My last attempt was to use tls_server_sni_maps,

There is no other mechanism, so not clear what you might have tried
first.

> but I'm not sure about the correct format (I've tried encoding the
> certificate as base64 according to the documentation).

The documentation says:

                       When postmap(1) is used with the -F option,
    the generated table stores for each lookup key the base64-encoded
    -------------------
    contents of the associated files.  When querying tables via
    postmap -Fq, the table value is decoded from base64, yielding
    the original file content, plus a new line.

That is, the base64 encoding/decoding is done *internally*, you are
not expected to (i.e. required to not) pre-encode the certificate
chain file(s).  The only case in you need to do base64 encoding is
when you want (likely unwisely) to store the chain files in an SQL
database:

    With tables whose content is managed outside of Postfix, such
    as LDAP, MySQL, PostgreSQL, socketmap and tcp, the value must
    be a concatenation of the desired PEM keys and certificate
    chains, that is then further encoded to yield a single-line
    base64 string. Creation of such tables and secure storage are
    outside the responsibility of Postfix. With "socketmap" and
    "tcp" the data would be transmitted in the clear. With LDAP and
    SQL, you should generally use TLS to protect the sensitive data
    in transit.

What's missing from the "tls_server_sni_maps" documentation (sorry
about that) is a reference to the

        http://www.postfix.org/postconf.5.html#smtpd_tls_chain_files

documentation, which covers the expected file format.  The syntax
for the RHS values of the (non-IPC) lookup tables used with
tls_server_sni_maps is the same as with the "chain files" parameter.

--
        Viktor.
Reply | Threaded
Open this post in threaded view
|

Re: How to use the new server TLS SNI feature (3.4.x)

Viktor Dukhovni
On Mon, Apr 01, 2019 at 02:09:25PM +0300, Andrei Kovacs wrote:

> So, if I understand correctly, the configuration for the SNI file if it is
> in hash format should be:
>
> domain1.com static:/path/to/pem1.pem
> domain2.com static:/path/to/pem2.pem

That makes no sense.  You're putting tables inside tables.  It is
not turtles all the way down.  The correct syntax is:

    main.cf:
        indexed = ${default_database_type}:${config_directory}/
        tls_server_sni_maps = ${indexed}sni-chains

    sni-chains:
        domain1.example /etc/postfix/sni/domain1.example.pem
        domain2.example /etc/postfix/sni/domain2.example.pem

The "sni-chains" table MUST be created with "postmap -F". It holds
a snapshot of the *content* of the listed files, not their names!

If you later change the file content, but don't rebuild the table,
the data in the table will remain stale.  You'll need to rebuild
the table (again with "postmap -F") whenever you want to start using
new certificates, even if the file names are unchanged.

The files must always list the private key before the associated
certificate chain.  You can list a file with just the key first,
and then the certificate file.  An advanced use case is to list
multiple files for the same domain, each with a key and certificate
chain for a different algorithm.  Or you can concatenate multiple
chains into a single file (again one per algorithm, with the key
first).

> Is it correct? And if so, the pem1.pem and pem2.pem files should contain in
> the exact order the PEMs for Private Key, Certificate and Chain, right?

Yes, but without those "static:" prefixes.

> In case of Let's Encrypt that would be privkey.pem + cert.pem +
> chain.pem.

Let's Encrypt certbot provides a 'fullchain.pem' for the last two.
Putting everything in one file makes for better atomicity.  But
if you're not racing "postmap -F" against certbot, the chances
of running into trouble are low.

> "Note that the SNI lookup tables should also have entries for the domains
> that correspond to the Postfix SMTP server's default certificate(s). This
> ensures that the remote SMTP client's TLS SNI extension gets a positive
> response when it specifies one of the Postfix SMTP server's default
> domains, and ensures that the Postfix SMTP server will not log an SNI name
> mismatch for such a domain. The Postfix SMTP server's default certificates
> are then only used when the client sends no SNI or when it sends SNI with a
> domain that the server knows no certificate(s) for."
>
> ... how should this be speficifed? Like "default
> static:/path/to/default.pem"?

No.  The syntax is exactly the same as with any other domain:

    myhostname.example /etc/postfix/chain.pem

even when the default is:

    smtpd_tls_chain_files = /etc/postfix/chain.pem

As explained, this ensures a positive ACK to the client, and
suppresses logging of SNI mismatch.  We could perhaps suppress such
logging for $myhostname, for which presumably the default chain is
the right one to use.

--
        Viktor.
Reply | Threaded
Open this post in threaded view
|

Re: How to use the new server TLS SNI feature (3.4.x)

Viktor Dukhovni
On Mon, Apr 01, 2019 at 10:38:49AM -0400, Viktor Dukhovni wrote:

> On Mon, Apr 01, 2019 at 02:09:25PM +0300, Andrei Kovacs wrote:
>
> > So, if I understand correctly, the configuration for the SNI file if it is
> > in hash format should be:
> >
> > domain1.com static:/path/to/pem1.pem
> > domain2.com static:/path/to/pem2.pem
>
> That makes no sense.  You're putting tables inside tables.  It is
> not turtles all the way down.  The correct syntax is:
>
>     main.cf:
> indexed = ${default_database_type}:${config_directory}/
> tls_server_sni_maps = ${indexed}sni-chains
>
>     sni-chains:
> domain1.example /etc/postfix/sni/domain1.example.pem
> domain2.example /etc/postfix/sni/domain2.example.pem
>
> The "sni-chains" table MUST be created with "postmap -F". It holds
> a snapshot of the *content* of the listed files, not their names!
>
> If you later change the file content, but don't rebuild the table,
> the data in the table will remain stale.  You'll need to rebuild
> the table (again with "postmap -F") whenever you want to start using
> new certificates, even if the file names are unchanged.
>
> The files must always list the private key before the associated
> certificate chain.  You can list a file with just the key first,
> and then the certificate file.  An advanced use case is to list
> multiple files for the same domain, each with a key and certificate
> chain for a different algorithm.  Or you can concatenate multiple
> chains into a single file (again one per algorithm, with the key
> first).

It seems a documentation update is needed, but I'd like to hear
whether the above is sufficiently clear, or whether more remains
unclear.  This may need a new section in TLS_README.  Or should we
spin off a smaller SNI_README?

--
        Viktor.
Reply | Threaded
Open this post in threaded view
|

Re: How to use the new server TLS SNI feature (3.4.x)

Wietse Venema
Viktor Dukhovni:

> On Mon, Apr 01, 2019 at 10:38:49AM -0400, Viktor Dukhovni wrote:
>
> > On Mon, Apr 01, 2019 at 02:09:25PM +0300, Andrei Kovacs wrote:
> >
> > > So, if I understand correctly, the configuration for the SNI file if it is
> > > in hash format should be:
> > >
> > > domain1.com static:/path/to/pem1.pem
> > > domain2.com static:/path/to/pem2.pem
> >
> > That makes no sense.  You're putting tables inside tables.  It is
> > not turtles all the way down.  The correct syntax is:
> >
> >     main.cf:
> > indexed = ${default_database_type}:${config_directory}/
> > tls_server_sni_maps = ${indexed}sni-chains
> >
> >     sni-chains:
> > domain1.example /etc/postfix/sni/domain1.example.pem
> > domain2.example /etc/postfix/sni/domain2.example.pem
> >
> > The "sni-chains" table MUST be created with "postmap -F". It holds
> > a snapshot of the *content* of the listed files, not their names!
> >
> > If you later change the file content, but don't rebuild the table,
> > the data in the table will remain stale.  You'll need to rebuild
> > the table (again with "postmap -F") whenever you want to start using
> > new certificates, even if the file names are unchanged.
> >
> > The files must always list the private key before the associated
> > certificate chain.  You can list a file with just the key first,
> > and then the certificate file.  An advanced use case is to list
> > multiple files for the same domain, each with a key and certificate
> > chain for a different algorithm.  Or you can concatenate multiple
> > chains into a single file (again one per algorithm, with the key
> > first).
>
> It seems a documentation update is needed, but I'd like to hear
> whether the above is sufficiently clear, or whether more remains
> unclear.  This may need a new section in TLS_README.  Or should we
> spin off a smaller SNI_README?

If you can fix this for the stable release without refactoring
documentation that would be best. Refactoring can happen in the
development release.

        Wietse
Reply | Threaded
Open this post in threaded view
|

Re: How to use the new server TLS SNI feature (3.4.x)

Andrei Kovacs
In reply to this post by Viktor Dukhovni
Hi Viktor,

Thank you for your explanation, I've been able to configure the SNI functionality. Just one issue is left, but that's maybe for a further improvement of postfix: it would be nice if there were a "catch-all" entry in the SNI map table, like '* /path/to/cert.pem" or 'default /path/to/cert.pem', because otherwise there is no way of specifying a default certificate. Maybe this is not a so-needed feature, but maybe in some occasions some users might want a self signed certificate for the default and only use real certificates for one or some of the domains hosted on the same server.

As a suggestion, I'd recommend making the documentation a bit easier to understand by at least adding some examples. I think everybody would welcome this.

Thank you again for your support,
Andrei

On Mon, Apr 1, 2019 at 5:40 PM Viktor Dukhovni <[hidden email]> wrote:
On Mon, Apr 01, 2019 at 02:09:25PM +0300, Andrei Kovacs wrote:

> So, if I understand correctly, the configuration for the SNI file if it is
> in hash format should be:
>
> domain1.com static:/path/to/pem1.pem
> domain2.com static:/path/to/pem2.pem

That makes no sense.  You're putting tables inside tables.  It is
not turtles all the way down.  The correct syntax is:

    main.cf:
        indexed = ${default_database_type}:${config_directory}/
        tls_server_sni_maps = ${indexed}sni-chains

    sni-chains:
        domain1.example /etc/postfix/sni/domain1.example.pem
        domain2.example /etc/postfix/sni/domain2.example.pem

The "sni-chains" table MUST be created with "postmap -F". It holds
a snapshot of the *content* of the listed files, not their names!

If you later change the file content, but don't rebuild the table,
the data in the table will remain stale.  You'll need to rebuild
the table (again with "postmap -F") whenever you want to start using
new certificates, even if the file names are unchanged.

The files must always list the private key before the associated
certificate chain.  You can list a file with just the key first,
and then the certificate file.  An advanced use case is to list
multiple files for the same domain, each with a key and certificate
chain for a different algorithm.  Or you can concatenate multiple
chains into a single file (again one per algorithm, with the key
first).

> Is it correct? And if so, the pem1.pem and pem2.pem files should contain in
> the exact order the PEMs for Private Key, Certificate and Chain, right?

Yes, but without those "static:" prefixes.

> In case of Let's Encrypt that would be privkey.pem + cert.pem +
> chain.pem.

Let's Encrypt certbot provides a 'fullchain.pem' for the last two.
Putting everything in one file makes for better atomicity.  But
if you're not racing "postmap -F" against certbot, the chances
of running into trouble are low.

> "Note that the SNI lookup tables should also have entries for the domains
> that correspond to the Postfix SMTP server's default certificate(s). This
> ensures that the remote SMTP client's TLS SNI extension gets a positive
> response when it specifies one of the Postfix SMTP server's default
> domains, and ensures that the Postfix SMTP server will not log an SNI name
> mismatch for such a domain. The Postfix SMTP server's default certificates
> are then only used when the client sends no SNI or when it sends SNI with a
> domain that the server knows no certificate(s) for."
>
> ... how should this be speficifed? Like "default
> static:/path/to/default.pem"?

No.  The syntax is exactly the same as with any other domain:

    myhostname.example  /etc/postfix/chain.pem

even when the default is:

    smtpd_tls_chain_files = /etc/postfix/chain.pem

As explained, this ensures a positive ACK to the client, and
suppresses logging of SNI mismatch.  We could perhaps suppress such
logging for $myhostname, for which presumably the default chain is
the right one to use.

--
        Viktor.
Reply | Threaded
Open this post in threaded view
|

Re: How to use the new server TLS SNI feature (3.4.x)

Viktor Dukhovni
On Mon, Apr 01, 2019 at 10:09:13PM +0300, Andrei Kovacs wrote:

> Thank you for your explanation, I've been able to configure the SNI
> functionality. Just one issue is left, but that's maybe for a further
> improvement of postfix: it would be nice if there were a "catch-all" entry
> in the SNI map table, like '* /path/to/cert.pem" or 'default
> /path/to/cert.pem', because otherwise there is no way of specifying a
> default certificate.

Actually, the default certificate chain is, as previously, the one
specified in main.cf.  After all, many clients won't send SNI at
all, and many servers won't configure SNI support.

The reason to configure domains that use the default chain with an
explicitly SNI mapping to that chain, is to suppress warnings in
the logs about receiving SNI names that fail to match any entries
in the table.  Such warnings can be useful to identify misconfigured
clients and servers.  Setting up a widlcard default would completely
defeat the purpose of the mismatch logging.

--
        Viktor.
MK
Reply | Threaded
Open this post in threaded view
|

Re: How to use the new server TLS SNI feature (3.4.x)

MK
In reply to this post by Andrei Kovacs
The documentation on this is very convoluted, but through trial and error and reviewing code, I did figure it out.

----- main.cf -----
# provide the primary certificate for the server, to be used for outgoing connections
smtpd_tls_chain_files =
 /etc/letsencrypt/live/servername.serverdom.com/privkey.pem,
 /etc/letsencrypt/live/servername.serverdom.com/fullchain.pem

# provide the map to be used when SNI support is enabled
tls_server_sni_maps = hash:/etc/postfix/vmail_ssl.map
-----

----- /etc/postfix/vmail_ssl.map -----
# Compile with postmap -F hash:/etc/postfix/vmail_ssl.map when updating
# One host per line
servername.serverdom.com /etc/letsencrypt/live/servername.serverdom.com/privkey.pem /etc/letsencrypt/live/servername.serverdom.com/fullchain.pem
servername.otherdom.com /etc/letsencrypt/live/servername.otherdom.com/privkey.pem /etc/letsencrypt/live/servername.otherdom.com/fullchain.pem
-----

Then run
 $ postmap -F hash:/etc/postfix/vmail_ssl.map

Restart postfix as normal.

Run
 $openssl s_client -connect localhost:25 -servername servername.otherdom.com -starttls smtp
 $openssl s_client -connect localhost:25 -servername servername.serverdom.com -starttls smtp
to test- you'll find the hostname under the certificate details. It will match the default server name of the host if there is not a match. Be sure the server name of the host is in the map file for that reason.

Hope this helps.
-M

On Sunday, March 31, 2019, 6:54:07 AM EDT, Andrei Kovacs <[hidden email]> wrote:


Hi,

I've noticed the release of the new SNI feature in Postfix 3.4, but I cannot get a successful setup. My last attempt was to use tls_server_sni_maps, but I'm not sure about the correct format (I've tried encoding the certificate as base64 according to the documentation).

For reference, what I'm trying is to have a main certificate for the mail server and another certificate (letsencrypt) for a specific domain.

Thank you,
Andrei