From: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
To: pgsql-hackers@postgresql.org <pgsql-hackers@postgresql.org>
Cc: Jacob Champion <pchampion@vmware.com>
Subject: Re: SSL SNI
Date: Thu, 18 Mar 2021 12:27:02 +0100
Message-ID: <4eaa2b56-c3d0-67b6-4716-010d001c7601@enterprisedb.com> (raw)
In-Reply-To: <ecb6a57d176a396feb959fbb0c53b3c794e8d8e5.camel@vmware.com>
References: <7289d5eb-62a5-a732-c3b9-438cee2cb709@enterprisedb.com>
<ef3d4b460ae7da59a8811f4e8e8fde0cd38a6cc8.camel@vmware.com>
<9407b344-7342-b2b7-004f-d5250687be42@enterprisedb.com>
<ecb6a57d176a396feb959fbb0c53b3c794e8d8e5.camel@vmware.com>
On 25.02.21 19:36, Jacob Champion wrote:
> On Thu, 2021-02-25 at 17:00 +0100, Peter Eisentraut wrote:
>> Just as additional data points, it has come to my attention that both
>> the Go driver ("lib/pq") and the JDBC environment already send SNI
>> automatically. (In the case of JDBC this is done by the Java system
>> libraries, not the JDBC driver implementation.)
>
> For the Go case it's only for sslmode=verify-full, and only because the
> Go standard library implementation does it for you automatically if you
> request the builtin server hostname validation. (I checked both lib/pq
> and its de facto replacement, jackc/pgx.) So it may not be something
> that was done on purpose by the driver implementation.
Here is a new patch with an option to turn it off, and some
documentation added.
From 50bc10d11018883ad7a555e90231b6ef16e3fdf6 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 18 Mar 2021 12:25:13 +0100
Subject: [PATCH v3] libpq: Set Server Name Indication (SNI) for SSL
connections
By default, have libpq set the TLS extension "Server Name Indication" (SNI).
This allows an SNI-aware SSL proxy to route connections. (This
requires a proxy that is aware of the PostgreSQL protocol, not just
any SSL proxy.)
In the future, this could also allow the server to use different SSL
certificates for different host specifications. (That would require
new server functionality. This would be the client-side functionality
for that.)
Since SNI makes the host name appear in cleartext in the network
traffic, this might be undesirable in some cases. Therefore, also add
a libpq connection option "sslsni" to turn it off.
Discussion: https://www.postgresql.org/message-id/flat/7289d5eb-62a5-a732-c3b9-438cee2cb709%40enterprisedb.com
---
.../postgres_fdw/expected/postgres_fdw.out | 2 +-
doc/src/sgml/libpq.sgml | 31 +++++++++++++++++++
src/interfaces/libpq/fe-connect.c | 6 ++++
src/interfaces/libpq/fe-secure-openssl.c | 22 +++++++++++++
src/interfaces/libpq/libpq-int.h | 1 +
5 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.outindex 0649b6b81c..d0405cf4a7 100644--- a/contrib/postgres_fdw/expected/postgres_fdw.out+++ b/contrib/postgres_fdw/expected/postgres_fdw.out@@ -8946,7 +8946,7 @@ DO $d$
END;
$d$;
ERROR: invalid option "password"
-HINT: Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host, hostaddr, port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, sslcrldir, requirepeer, ssl_min_protocol_version, ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, fetch_size, batch_size+HINT: Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host, hostaddr, port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, sslcrldir, sslsni, requirepeer, ssl_min_protocol_version, ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, fetch_size, batch_size
CONTEXT: SQL statement "ALTER SERVER loopback_nopw OPTIONS (ADD password 'dummypw')"
PL/pgSQL function inline_code_block line 3 at EXECUTE
-- If we add a password for our user mapping instead, we should get a different
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgmlindex be674fbaa9..dbfc7add14 100644--- a/doc/src/sgml/libpq.sgml+++ b/doc/src/sgml/libpq.sgml@@ -1777,6 +1777,27 @@ <title>Parameter Key Words</title>
</listitem>
</varlistentry>
+ <varlistentry id="libpq-connect-sslsni" xreflabel="sslsni">+ <term><literal>sslsni</literal><indexterm><primary>Server Name Indication</primary></indexterm></term>+ <listitem>+ <para>+ By default, libpq sets the TLS extension <quote>Server Name+ Indication</quote> (SNI) on SSL-enabled connections. See <ulink+ url="https://tools.ietf.org/html/rfc6066#section-3">RFC 6066</ulink>+ for details. By setting this parameter to 0, this is turned off.+ </para>++ <para>+ The Server Name Indication can be used by SSL-aware proxies to route+ connections without having to decrypt the SSL stream. (Note that this+ requires a proxy that is aware of the PostgreSQL protocol handshake,+ not just any SSL proxy.) However, SNI makes the destination host name+ appear in cleartext in the network traffic, so it might be undesirable+ in some cases.+ </para>+ </listitem>+ </varlistentry>+
<varlistentry id="libpq-connect-requirepeer" xreflabel="requirepeer">
<term><literal>requirepeer</literal></term>
<listitem>
@@ -7757,6 +7778,16 @@ <title>Environment Variables</title>
</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.cindex 53b354abb2..f1551e796c 100644--- a/src/interfaces/libpq/fe-connect.c+++ b/src/interfaces/libpq/fe-connect.c@@ -303,6 +303,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"SSL-Revocation-List-Dir", "", 64,
offsetof(struct pg_conn, sslcrldir)},
+ {"sslsni", "PGSSLSNI", "1", NULL,+ "SSL-SNI", "", 1,+ offsetof(struct pg_conn, sslsni)},+
{"requirepeer", "PGREQUIREPEER", NULL, NULL,
"Require-Peer", "", 10,
offsetof(struct pg_conn, requirepeer)},
@@ -4094,6 +4098,8 @@ freePGconn(PGconn *conn)
free(conn->sslcrldir);
if (conn->sslcompression)
free(conn->sslcompression);
+ if (conn->sslsni)+ free(conn->sslsni);
if (conn->requirepeer)
free(conn->requirepeer);
if (conn->ssl_min_protocol_version)
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.cindex 9c2222c1d1..6f357dfbfe 100644--- a/src/interfaces/libpq/fe-secure-openssl.c+++ b/src/interfaces/libpq/fe-secure-openssl.c@@ -1082,6 +1082,28 @@ initialize_SSL(PGconn *conn)
SSL_CTX_free(SSL_context);
SSL_context = NULL;
+ /*+ * Set Server Name Indication (SNI), if enabled by connection parameters.+ * Per RFC 6066, do not set it if the host is a literal IP address (IPv4+ * or IPv6).+ */+ if (conn->sslsni && conn->sslsni[0] &&+ !(strspn(conn->pghost, "0123456789.") == strlen(conn->pghost) ||+ strchr(conn->pghost, ':')))+ {+ if (SSL_set_tlsext_host_name(conn->ssl, conn->pghost) != 1)+ {+ char *err = SSLerrmessage(ERR_get_error());++ appendPQExpBuffer(&conn->errorMessage,+ libpq_gettext("could not set SSL Server Name Indication (SNI): %s\n"),+ err);+ SSLerrfree(err);+ SSL_CTX_free(SSL_context);+ return -1;+ }+ }+
/*
* Read the SSL key. If a key is specified, treat it as an engine:key
* combination if there is colon present - we don't support files with
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.hindex 6374ec657a..24bd1209a0 100644--- a/src/interfaces/libpq/libpq-int.h+++ b/src/interfaces/libpq/libpq-int.h@@ -383,6 +383,7 @@ struct pg_conn
char *sslrootcert; /* root certificate filename */
char *sslcrl; /* certificate revocation list filename */
char *sslcrldir; /* certificate revocation list directory name */
+ char *sslsni; /* use SSL SNI extension (0 or 1) */
char *requirepeer; /* required peer credentials for local sockets */
char *gssencmode; /* GSS mode (require,prefer,disable) */
char *krbsrvname; /* Kerberos service name */
base-commit: ed62d3737c1b823f796d974060b1d0295a3dd831
--
2.30.2
Attachments:
[text/plain] v3-0001-libpq-Set-Server-Name-Indication-SNI-for-SSL-conn.patch (7.4K, ../4eaa2b56-c3d0-67b6-4716-010d001c7601@enterprisedb.com/2-v3-0001-libpq-Set-Server-Name-Indication-SNI-for-SSL-conn.patch)
download | inline diff:
From 50bc10d11018883ad7a555e90231b6ef16e3fdf6 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 18 Mar 2021 12:25:13 +0100
Subject: [PATCH v3] libpq: Set Server Name Indication (SNI) for SSL
connections
By default, have libpq set the TLS extension "Server Name Indication" (SNI).
This allows an SNI-aware SSL proxy to route connections. (This
requires a proxy that is aware of the PostgreSQL protocol, not just
any SSL proxy.)
In the future, this could also allow the server to use different SSL
certificates for different host specifications. (That would require
new server functionality. This would be the client-side functionality
for that.)
Since SNI makes the host name appear in cleartext in the network
traffic, this might be undesirable in some cases. Therefore, also add
a libpq connection option "sslsni" to turn it off.
Discussion: https://www.postgresql.org/message-id/flat/7289d5eb-62a5-a732-c3b9-438cee2cb709%40enterprisedb.com
---
.../postgres_fdw/expected/postgres_fdw.out | 2 +-
doc/src/sgml/libpq.sgml | 31 +++++++++++++++++++
src/interfaces/libpq/fe-connect.c | 6 ++++
src/interfaces/libpq/fe-secure-openssl.c | 22 +++++++++++++
src/interfaces/libpq/libpq-int.h | 1 +
5 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.outindex 0649b6b81c..d0405cf4a7 100644--- a/contrib/postgres_fdw/expected/postgres_fdw.out+++ b/contrib/postgres_fdw/expected/postgres_fdw.out@@ -8946,7 +8946,7 @@ DO $d$
END;
$d$;
ERROR: invalid option "password"
-HINT: Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host, hostaddr, port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, sslcrldir, requirepeer, ssl_min_protocol_version, ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, fetch_size, batch_size+HINT: Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host, hostaddr, port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, sslcrldir, sslsni, requirepeer, ssl_min_protocol_version, ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, fetch_size, batch_size
CONTEXT: SQL statement "ALTER SERVER loopback_nopw OPTIONS (ADD password 'dummypw')"
PL/pgSQL function inline_code_block line 3 at EXECUTE
-- If we add a password for our user mapping instead, we should get a different
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgmlindex be674fbaa9..dbfc7add14 100644--- a/doc/src/sgml/libpq.sgml+++ b/doc/src/sgml/libpq.sgml@@ -1777,6 +1777,27 @@ <title>Parameter Key Words</title>
</listitem>
</varlistentry>
+ <varlistentry id="libpq-connect-sslsni" xreflabel="sslsni">+ <term><literal>sslsni</literal><indexterm><primary>Server Name Indication</primary></indexterm></term>+ <listitem>+ <para>+ By default, libpq sets the TLS extension <quote>Server Name+ Indication</quote> (SNI) on SSL-enabled connections. See <ulink+ url="https://tools.ietf.org/html/rfc6066#section-3">RFC 6066</ulink>+ for details. By setting this parameter to 0, this is turned off.+ </para>++ <para>+ The Server Name Indication can be used by SSL-aware proxies to route+ connections without having to decrypt the SSL stream. (Note that this+ requires a proxy that is aware of the PostgreSQL protocol handshake,+ not just any SSL proxy.) However, SNI makes the destination host name+ appear in cleartext in the network traffic, so it might be undesirable+ in some cases.+ </para>+ </listitem>+ </varlistentry>+
<varlistentry id="libpq-connect-requirepeer" xreflabel="requirepeer">
<term><literal>requirepeer</literal></term>
<listitem>
@@ -7757,6 +7778,16 @@ <title>Environment Variables</title>
</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.cindex 53b354abb2..f1551e796c 100644--- a/src/interfaces/libpq/fe-connect.c+++ b/src/interfaces/libpq/fe-connect.c@@ -303,6 +303,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"SSL-Revocation-List-Dir", "", 64,
offsetof(struct pg_conn, sslcrldir)},
+ {"sslsni", "PGSSLSNI", "1", NULL,+ "SSL-SNI", "", 1,+ offsetof(struct pg_conn, sslsni)},+
{"requirepeer", "PGREQUIREPEER", NULL, NULL,
"Require-Peer", "", 10,
offsetof(struct pg_conn, requirepeer)},
@@ -4094,6 +4098,8 @@ freePGconn(PGconn *conn)
free(conn->sslcrldir);
if (conn->sslcompression)
free(conn->sslcompression);
+ if (conn->sslsni)+ free(conn->sslsni);
if (conn->requirepeer)
free(conn->requirepeer);
if (conn->ssl_min_protocol_version)
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.cindex 9c2222c1d1..6f357dfbfe 100644--- a/src/interfaces/libpq/fe-secure-openssl.c+++ b/src/interfaces/libpq/fe-secure-openssl.c@@ -1082,6 +1082,28 @@ initialize_SSL(PGconn *conn)
SSL_CTX_free(SSL_context);
SSL_context = NULL;
+ /*+ * Set Server Name Indication (SNI), if enabled by connection parameters.+ * Per RFC 6066, do not set it if the host is a literal IP address (IPv4+ * or IPv6).+ */+ if (conn->sslsni && conn->sslsni[0] &&+ !(strspn(conn->pghost, "0123456789.") == strlen(conn->pghost) ||+ strchr(conn->pghost, ':')))+ {+ if (SSL_set_tlsext_host_name(conn->ssl, conn->pghost) != 1)+ {+ char *err = SSLerrmessage(ERR_get_error());++ appendPQExpBuffer(&conn->errorMessage,+ libpq_gettext("could not set SSL Server Name Indication (SNI): %s\n"),+ err);+ SSLerrfree(err);+ SSL_CTX_free(SSL_context);+ return -1;+ }+ }+
/*
* Read the SSL key. If a key is specified, treat it as an engine:key
* combination if there is colon present - we don't support files with
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.hindex 6374ec657a..24bd1209a0 100644--- a/src/interfaces/libpq/libpq-int.h+++ b/src/interfaces/libpq/libpq-int.h@@ -383,6 +383,7 @@ struct pg_conn
char *sslrootcert; /* root certificate filename */
char *sslcrl; /* certificate revocation list filename */
char *sslcrldir; /* certificate revocation list directory name */
+ char *sslsni; /* use SSL SNI extension (0 or 1) */
char *requirepeer; /* required peer credentials for local sockets */
char *gssencmode; /* GSS mode (require,prefer,disable) */
char *krbsrvname; /* Kerberos service name */
base-commit: ed62d3737c1b823f796d974060b1d0295a3dd831
--
2.30.2
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: peter.eisentraut@enterprisedb.com, pchampion@vmware.com
Subject: Re: SSL SNI
In-Reply-To: <4eaa2b56-c3d0-67b6-4716-010d001c7601@enterprisedb.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox