Re: Introducing SNI in TLS handshake for SSL connections

Started by Pablo Iranzo Gómezabout 7 years ago8 messages
#1Pablo Iranzo Gómez
Pablo.Iranzo@redhat.com

Hi,

On 4/24/17 22:26, Florin Asavoaie wrote:

If there's nobody against this, I can try to do the patch myself,
doesn't look too difficult (I expect it to simply work by
calling SSL_set_tlsext_host_name(SSL_context, PQhost(conn))) somewhere
in initialize_SSL in fe-secure-openssl.c.

I had to look up what SNI is:
https://en.wikipedia.org/wiki/Server_Name_Indication

This seems useful.

If you have a patch, please add it here:
https://commitfest.postgresql.org/14/

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

I came to this old thread while trying to figure out on how to setup postgres replication behind OpenShift/Kubernetes behind a route (which only forwards 80 or 443 traffic), but could work if SNI is supported on the client using it.

I haven't found any further follow-up on this, but based on the number of posts and questions on many sites on accessing postgres on OpenShift/Kubernetes it could be something good to have supported.

Any further information or plans?

Thanks,
Pablo

--

Pablo Iranzo Gómez (Pablo.Iranzo@redhat.com) GnuPG: 0x5BD8E1E4
Senior Software Engineer - Solutions Engineering iranzo @ IRC
RHC{A,SS,DS,VA,E,SA,SP,AOSP}, JBCAA #110-215-852 RHCA Level V

Blog: https://iranzo.github.io https://citellus.org

#2Andreas Karlsson
andreas@proxel.se
In reply to: Pablo Iranzo Gómez (#1)

On 12/11/18 3:52 PM, Pablo Iranzo G�mez wrote:> I came to this old
thread while trying to figure out on how to setup

postgres replication behind OpenShift/Kubernetes behind a route (which
only forwards 80 or 443 traffic), but could work if SNI is supported on
the client using it.

I haven't found any further follow-up on this, but based on the number
of posts and questions on many sites on accessing postgres on
OpenShift/Kubernetes it could be something good to have supported.

Any further information or plans?

I am pretty sure nobody is working on this.

It seems like it would be easy to implement (basically just call
SSL_set_tlsext_host_name() with the right hostname) with the only issue
being that we may need to add a new connection string parameter[1]
because I doubt all users would want SNI enabled by default since
PostgreSQL itself cannot do anything useful with the hostname, only some
kind of TLS proxy can. Hopefully there wont be much bike shedding about
the new connection parameter. :)

Feel free to write a patch if you have the time and submit it to the
next commitfest[2] for review.

Notes:

1. List of current options:
https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
2. https://wiki.postgresql.org/wiki/CommitFest

Andreas

#3Pablo Iranzo Gómez
Pablo.Iranzo@redhat.com
In reply to: Andreas Karlsson (#2)
+++ Andreas Karlsson [11/12/18 18:18 +0100]:

On 12/11/18 3:52 PM, Pablo Iranzo Gómez wrote:> I came to this old
thread while trying to figure out on how to setup

postgres replication behind OpenShift/Kubernetes behind a route
(which only forwards 80 or 443 traffic), but could work if SNI is
supported on the client using it.

I haven't found any further follow-up on this, but based on the
number of posts and questions on many sites on accessing postgres on
OpenShift/Kubernetes it could be something good to have supported.

Any further information or plans?

I am pretty sure nobody is working on this.

It seems like it would be easy to implement (basically just call
SSL_set_tlsext_host_name() with the right hostname) with the only
issue being that we may need to add a new connection string
parameter[1] because I doubt all users would want SNI enabled by
default since PostgreSQL itself cannot do anything useful with the
hostname, only some kind of TLS proxy can. Hopefully there wont be
much bike shedding about the new connection parameter. :)

Feel free to write a patch if you have the time and submit it to the
next commitfest[2] for review.

Unfortunately I do not consider myself a coder, so if there is any way
to 'list' this as a 'nice to have' thing so that someone can take the
task and move it forward.

Thanks,
Pablo

Notes:

1. List of current options: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
2. https://wiki.postgresql.org/wiki/CommitFest

Andreas

--

Pablo Iranzo Gómez (Pablo.Iranzo@redhat.com) GnuPG: 0x5BD8E1E4
Senior Software Engineer - Solutions Engineering iranzo @ IRC
RHC{A,SS,DS,VA,E,SA,SP,AOSP}, JBCAA #110-215-852 RHCA Level V

Blog: https://iranzo.github.io https://citellus.org

#4Andreas Karlsson
andreas@proxel.se
In reply to: Pablo Iranzo Gómez (#1)

On 12/11/18 3:52 PM, Pablo Iranzo G�mez wrote:

I came to this old thread while trying to figure out on how to setup
postgres replication behind OpenShift/Kubernetes behind a route (which
only forwards 80 or 443 traffic), but could work if SNI is supported on
the client using it.

Hm ... while hacking at a patch for this I gave your specific problem
some more thought.

I am not familiar with OpenShift or Kubernetes but I want you to be
aware of that whatever proxy you are going to use will still need to be
aware of, at least a subset of, the PostgreSQL protocol, since similar
to SMTP's STARTTLS command the PostgreSQL client will start out using
the plain text PostgreSQL protocol and then request the server to switch
over to SSL[1]. So it would be necessary to add support for this to
whatever proxy you intend to use.

Do you know if adding such custom protocol support is easy to do to the
proxies you refer to? And do you have any links to documentation for
these solutions?

Notes

1. https://www.postgresql.org/docs/11/protocol-flow.html#id-1.10.5.7.11

Andreas

#5Pablo Iranzo Gómez
Pablo.Iranzo@redhat.com
In reply to: Andreas Karlsson (#4)

Hi Andreas

+++ Andreas Karlsson [13/12/18 01:30 +0100]:

On 12/11/18 3:52 PM, Pablo Iranzo Gómez wrote:

I came to this old thread while trying to figure out on how to setup
postgres replication behind OpenShift/Kubernetes behind a route
(which only forwards 80 or 443 traffic), but could work if SNI is
supported on the client using it.

Hm ... while hacking at a patch for this I gave your specific problem
some more thought.

Thanks for this!

I am not familiar with OpenShift or Kubernetes but I want you to be
aware of that whatever proxy you are going to use will still need to

haproxy is what is used behind, the idea is that haproxy by default when
enabled via a 'route' does allow http or https protocol ONLY, BUT
(https://docs.openshift.com/container-platform/3.9/architecture/networking/routes.html),
routers do support TLS with SNI.

As PSQL by default tries TLS and fallbacks to plain psql protocol the
idea behind is that we tell OpenShift route to be 'Secure' and
'passtrough', in this way, when PSQL does speak to '443' port in the
route that goes to the 'pod' running postgres using TLS and SNI, the
connection goes thru without any special protocol change.

be aware of, at least a subset of, the PostgreSQL protocol, since
similar to SMTP's STARTTLS command the PostgreSQL client will start
out using the plain text PostgreSQL protocol and then request the
server to switch over to SSL[1]. So it would be necessary to add
support for this to whatever proxy you intend to use.

Do you know if adding such custom protocol support is easy to do to
the proxies you refer to? And do you have any links to documentation
for these solutions?

I found some diagrams and other links to SSL and HAProxy in:

https://www.haproxy.com/fr/blog/enhanced-ssl-load-balancing-with-server-name-indication-sni-tls-extension/

Probably: https://tools.ietf.org/html/rfc6066#page-6

Let me know if this is not helpful, and thanks again for your time on
this.

Pablo

Notes

1. https://www.postgresql.org/docs/11/protocol-flow.html#id-1.10.5.7.11

Andreas

--

Pablo Iranzo Gómez (Pablo.Iranzo@redhat.com) GnuPG: 0x5BD8E1E4
Senior Software Engineer - Solutions Engineering iranzo @ IRC
RHC{A,SS,DS,VA,E,SA,SP,AOSP}, JBCAA #110-215-852 RHCA Level V

Blog: https://iranzo.github.io https://citellus.org

#6Andreas Karlsson
andreas@proxel.se
In reply to: Pablo Iranzo Gómez (#5)
1 attachment(s)

On 12/13/18 7:43 AM, Pablo Iranzo G�mez wrote:

haproxy is what is used behind, the idea is that haproxy by default when
enabled via a 'route' does allow http or https protocol ONLY, BUT
(https://docs.openshift.com/container-platform/3.9/architecture/networking/routes.html),

routers do support TLS with SNI.

As PSQL by default tries TLS and fallbacks to plain psql protocol the
idea behind is that we tell OpenShift route to be 'Secure' and
'passtrough', in this way, when PSQL does speak to '443' port in the
route that goes to the 'pod' running postgres using TLS and SNI, the
connection goes thru without any special protocol change.

Sadly that confirms what I feared. Adding SNI to the PostgreSQL protocol
wont help with solving your use case because the PostgreSQL protocol has
its own handshake which happens before the SSL handshake so the session
will not look like SSL to HA Proxy.

Just like HA Proxy does not support STARTTLS for IMAP[1] I do not think
that it will ever support SSL for the PostgreSQL protocol, SNI or not.

To solve your use case I recommend using something like stunnel, which
does support SNI, to wrap the unencrypted PostgreSQL protocol in SSL.

This all makes me very skeptical to if there is any realistic use case
for adding SNI support to libpq, and just adding SNI support feels like
adding a trap to people who do not know that they should rather use
stunnel than PostgreSQL's built-in SSL support of they want to route on
SNI with HA Proxy.

But I will attach my small patch for this, which I am now opposed to,
anyway so the code exists if a use case turns up in the future (or if it
turns out my reasoning above is incorrect).

Notes

1.
https://www.haproxy.com/documentation/haproxy/deployment-guides/exchange-2010/imap4/

Andreas

Attachments:

ssl-sni-v1.patchtext/x-patch; name=ssl-sni-v1.patchDownload
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index d2e5b08541e..528757f775d 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1460,6 +1460,23 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslsni" xreflabel="sslsni">
+      <term><literal>sslcompression</literal></term>
+      <listitem>
+       <para>
+        If set to 1, the host name is sent to the server using SSL's
+        <acronym>SNI</acronym> (Server Name Indication) extension.  If set
+        to 0, no <acronym>SNI</acronym> extension will be sent.  The default is
+        0.  This parameter is ignored if a connection without SSL is made.
+       </para>
+
+       <para>
+        The PostgreSQL server ignores the <acronym>SNI</acronym> extension,
+        but it can be used by SSL-aware proxy software.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslcert" xreflabel="sslcert">
       <term><literal>sslcert</literal></term>
       <listitem>
@@ -7373,6 +7390,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLSNI</envar></primary>
+      </indexterm>
+      <envar>PGSSLSNI</envar> behaves the same as the <xref
+      linkend="libpq-connect-sslsni"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index bc456fec0c2..4587e5ebb5a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -278,6 +278,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"SSL-Compression", "", 1,
 	offsetof(struct pg_conn, sslcompression)},
 
+	{"sslsni", "PGSSLSNI", "0", NULL,
+		"SSL-SNI", "", 1,
+	offsetof(struct pg_conn, sslsni)},
+
 	{"sslcert", "PGSSLCERT", NULL, NULL,
 		"SSL-Client-Cert", "", 64,
 	offsetof(struct pg_conn, sslcert)},
@@ -3690,6 +3694,8 @@ freePGconn(PGconn *conn)
 		free(conn->sslcrl);
 	if (conn->sslcompression)
 		free(conn->sslcompression);
+	if (conn->sslsni)
+		free(conn->sslsni);
 	if (conn->requirepeer)
 		free(conn->requirepeer);
 	if (conn->connip)
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index beca3492e8d..fdae2eac74f 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -781,6 +781,7 @@ initialize_SSL(PGconn *conn)
 	char		homedir[MAXPGPATH];
 	char		fnbuf[MAXPGPATH];
 	char		sebuf[PG_STRERROR_R_BUFLEN];
+	char	   *host;
 	bool		have_homedir;
 	bool		have_cert;
 	bool		have_rootcert;
@@ -1183,6 +1184,11 @@ initialize_SSL(PGconn *conn)
 #endif
 #endif
 
+	host = conn->connhost[conn->whichhost].host;
+
+	if (conn->sslsni && conn->sslsni[0] == '1' && host)
+		SSL_set_tlsext_host_name(conn->ssl, host);
+
 	return 0;
 }
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 66fd317b949..9f69fbdf5fc 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -353,6 +353,7 @@ struct pg_conn
 									 * retransmits */
 	char	   *sslmode;		/* SSL mode (require,prefer,allow,disable) */
 	char	   *sslcompression; /* SSL compression (0 or 1) */
+	char	   *sslsni;			/* SSL SNI extension (0 or 1) */
 	char	   *sslkey;			/* client key filename */
 	char	   *sslcert;		/* client certificate filename */
 	char	   *sslrootcert;	/* root certificate filename */
#7Chapman Flack
chap@anastigmatix.net
In reply to: Andreas Karlsson (#6)
1 attachment(s)

On 12/13/18 08:07, Andreas Karlsson wrote:

But I will attach my small patch for this, which I am now opposed to, anyway
so the code exists if a use case turns up in the future (or if it turns out
my reasoning above is incorrect).

Here's the same patch with one small copy-pasto fixed.

-Chap

Attachments:

ssl-sni-v1a.patchtext/x-patch; name=ssl-sni-v1a.patchDownload
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index d2e5b08541e..528757f775d 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1460,6 +1460,23 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslsni" xreflabel="sslsni">
+      <term><literal>sslsni</literal></term>
+      <listitem>
+       <para>
+        If set to 1, the host name is sent to the server using SSL's
+        <acronym>SNI</acronym> (Server Name Indication) extension.  If set
+        to 0, no <acronym>SNI</acronym> extension will be sent.  The default is
+        0.  This parameter is ignored if a connection without SSL is made.
+       </para>
+
+       <para>
+        The PostgreSQL server ignores the <acronym>SNI</acronym> extension,
+        but it can be used by SSL-aware proxy software.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslcert" xreflabel="sslcert">
       <term><literal>sslcert</literal></term>
       <listitem>
@@ -7373,6 +7390,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLSNI</envar></primary>
+      </indexterm>
+      <envar>PGSSLSNI</envar> behaves the same as the <xref
+      linkend="libpq-connect-sslsni"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index bc456fec0c2..4587e5ebb5a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -278,6 +278,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"SSL-Compression", "", 1,
 	offsetof(struct pg_conn, sslcompression)},
 
+	{"sslsni", "PGSSLSNI", "0", NULL,
+		"SSL-SNI", "", 1,
+	offsetof(struct pg_conn, sslsni)},
+
 	{"sslcert", "PGSSLCERT", NULL, NULL,
 		"SSL-Client-Cert", "", 64,
 	offsetof(struct pg_conn, sslcert)},
@@ -3690,6 +3694,8 @@ freePGconn(PGconn *conn)
 		free(conn->sslcrl);
 	if (conn->sslcompression)
 		free(conn->sslcompression);
+	if (conn->sslsni)
+		free(conn->sslsni);
 	if (conn->requirepeer)
 		free(conn->requirepeer);
 	if (conn->connip)
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index beca3492e8d..fdae2eac74f 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -781,6 +781,7 @@ initialize_SSL(PGconn *conn)
 	char		homedir[MAXPGPATH];
 	char		fnbuf[MAXPGPATH];
 	char		sebuf[PG_STRERROR_R_BUFLEN];
+	char	   *host;
 	bool		have_homedir;
 	bool		have_cert;
 	bool		have_rootcert;
@@ -1183,6 +1184,11 @@ initialize_SSL(PGconn *conn)
 #endif
 #endif
 
+	host = conn->connhost[conn->whichhost].host;
+
+	if (conn->sslsni && conn->sslsni[0] == '1' && host)
+		SSL_set_tlsext_host_name(conn->ssl, host);
+
 	return 0;
 }
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 66fd317b949..9f69fbdf5fc 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -353,6 +353,7 @@ struct pg_conn
 									 * retransmits */
 	char	   *sslmode;		/* SSL mode (require,prefer,allow,disable) */
 	char	   *sslcompression; /* SSL compression (0 or 1) */
+	char	   *sslsni;			/* SSL SNI extension (0 or 1) */
 	char	   *sslkey;			/* client key filename */
 	char	   *sslcert;		/* client certificate filename */
 	char	   *sslrootcert;	/* root certificate filename */
#8Pablo Iranzo Gómez
Pablo.Iranzo@redhat.com
In reply to: Andreas Karlsson (#4)

Hi,

+++ Andreas Karlsson [13/12/18 01:30 +0100]:

On 12/11/18 3:52 PM, Pablo Iranzo Gómez wrote:

I came to this old thread while trying to figure out on how to setup
postgres replication behind OpenShift/Kubernetes behind a route
(which only forwards 80 or 443 traffic), but could work if SNI is
supported on the client using it.

Hm ... while hacking at a patch for this I gave your specific problem
some more thought.

I am not familiar with OpenShift or Kubernetes but I want you to be
aware of that whatever proxy you are going to use will still need to
be aware of, at least a subset of, the PostgreSQL protocol, since
similar to SMTP's STARTTLS command the PostgreSQL client will start
out using the plain text PostgreSQL protocol and then request the
server to switch over to SSL[1]. So it would be necessary to add
support for this to whatever proxy you intend to use.

Do you know if adding such custom protocol support is easy to do to
the proxies you refer to? And do you have any links to documentation
for these solutions?

I saw that they did incorporate some changes like SPDY support and other
http related things.

Let me try to find an answer (now sure how long will it take) and come
back.

I've did some basic search at
https://git.haproxy.org/?p=haproxy.git;a=summary but nothing evident
(for me).

I'll keep you updated.
Pablo

Notes

1. https://www.postgresql.org/docs/11/protocol-flow.html#id-1.10.5.7.11

Andreas

--

Pablo Iranzo Gómez (Pablo.Iranzo@redhat.com) GnuPG: 0x5BD8E1E4
Senior Software Engineer - Solutions Engineering iranzo @ IRC
RHC{A,SS,DS,VA,E,SA,SP,AOSP}, JBCAA #110-215-852 RHCA Level V

Blog: https://iranzo.github.io https://citellus.org