On 10/15/25 11:28 PM, Gurucharan Shetty wrote:
> This commit introduces Server Name Indication (SNI) support for all OVS
> SSL/TLS clients, enabling clients to specify which hostname they are
> attempting to reach during the SSL handshake. This is essential for
> connecting through proxies, load balancers, and service meshes where
> the connection endpoint differs from the intended server name.
> 
> An example use case is trying to connect to a ovsdb-server through
> a istio proxy gateway in kubernetes environments
> 
> Signed-off-by: Gurucharan Shetty <[email protected]>
> ---
>  NEWS                 |  6 +++++
>  lib/ssl-syn.man      |  2 ++
>  lib/ssl.man          |  7 ++++++
>  lib/ssl.xml          |  9 +++++++
>  lib/stream-nossl.c   |  7 ++++++
>  lib/stream-ssl.c     | 21 ++++++++++++++++-
>  lib/stream-ssl.h     | 13 +++++++---
>  lib/stream.c         |  4 +++-
>  ovsdb/ovsdb-server.c |  9 +++++++
>  tests/ovs-vsctl.at   | 56 ++++++++++++++++++++++++++++++++++++++++++++
>  10 files changed, 129 insertions(+), 5 deletions(-)
> 
> diff --git a/NEWS b/NEWS
> index 96bf4992c..d25be10db 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -1,5 +1,11 @@
>  Post-v3.6.0
>  --------------------
> +   - TLS:
> +     * Added support for TLS Server Name Indication (SNI) with the new
> +       --ssl-server-name option. This allows specifying the server name
> +       for SNI, which is useful when connecting through proxies or service
> +       meshes where the connection endpoint differs from the intended
> +       server name.
>     - Userspace datapath:
>       * Conntrack now supports the FTP commands EPSV and EPRT with IPv4
>         connections, instead of limiting these commands to IPv6 only.
> diff --git a/lib/ssl-syn.man b/lib/ssl-syn.man
> index 583454548..77682bade 100644
> --- a/lib/ssl-syn.man
> +++ b/lib/ssl-syn.man
> @@ -4,3 +4,5 @@
>  [\fB\-\-certificate=\fIcert.pem\fR]
>  .br
>  [\fB\-\-ca\-cert=\fIcacert.pem\fR]
> +.br
> +[\fB\-\-ssl\-server\-name=\fIservername\fR]
> diff --git a/lib/ssl.man b/lib/ssl.man
> index 9bec3a786..303f0550b 100644
> --- a/lib/ssl.man
> +++ b/lib/ssl.man
> @@ -24,3 +24,10 @@ be a different one, depending on the PKI design in use.)
>  Disables verification of certificates presented by SSL/TLS peers.  This
>  introduces a security risk, because it means that certificates cannot
>  be verified to be those of known trusted hosts.
> +.
> +.IP "\fB\-\-ssl\-server\-name=\fIservername\fR"
> +Specifies the server name to use for TLS Server Name Indication (SNI).
> +By default, the hostname from the connection string is used for SNI.
> +This option allows overriding the SNI hostname, which is useful when
> +connecting through proxies or service meshes where the connection endpoint
> +differs from the intended server name.
> diff --git a/lib/ssl.xml b/lib/ssl.xml
> index bd2502898..4dd2938a7 100644
> --- a/lib/ssl.xml
> +++ b/lib/ssl.xml
> @@ -33,4 +33,13 @@
>      introduces a security risk, because it means that certificates cannot
>      be verified to be those of known trusted hosts.
>    </dd>
> +
> +  <dt><code>--ssl-server-name=</code><var>servername</var></dt>
> +  <dd>
> +    Specifies the server name to use for TLS Server Name Indication (SNI).
> +    By default, the hostname from the connection string is used for SNI.
> +    This option allows overriding the SNI hostname, which is useful when
> +    connecting through proxies or service meshes where the connection 
> endpoint
> +    differs from the intended server name.
> +  </dd>
>  </dl>
> diff --git a/lib/stream-nossl.c b/lib/stream-nossl.c
> index 105ac377a..c0a6bf92a 100644
> --- a/lib/stream-nossl.c
> +++ b/lib/stream-nossl.c
> @@ -96,3 +96,10 @@ stream_ssl_set_ciphersuites(const char *arg OVS_UNUSED)
>      /* Ignore this option since it seems harmless to set TLS ciphersuites if
>       * SSL/TLS won't be used. */
>  }
> +
> +void
> +stream_ssl_set_server_name(const char *server_name OVS_UNUSED)
> +{
> +    /* Ignore this option since it seems harmless to set TLS server name if
> +     * SSL/TLS won't be used. */
> +}
> diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c
> index 99c82b6af..c647716ab 100644
> --- a/lib/stream-ssl.c
> +++ b/lib/stream-ssl.c
> @@ -168,6 +168,11 @@ static char *ssl_protocols = "TLSv1.2+";
>  static char *ssl_ciphers = "DEFAULT:@SECLEVEL=2";
>  static char *ssl_ciphersuites = ""; /* Using default ones, unless specified. 
> */
>  
> +/* Server name override for SNI (Server Name Indication).
> + * If set, this name will be used for SNI instead of the hostname
> + * extracted from the connection string. */
> +static char *ssl_server_name_override;
> +
>  /* Ordinarily, the SSL client and server verify each other's certificates 
> using
>   * a CA certificate.  Setting this to false disables this behavior.  (This 
> is a
>   * security risk.) */
> @@ -372,7 +377,11 @@ ssl_open(const char *name, char *suffix, struct stream 
> **streamp, uint8_t dscp)
>                               dscp);
>      if (fd >= 0) {
>          int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
> -        return new_ssl_stream(xstrdup(name), get_server_name(suffix),
> +        char *server_name = ssl_server_name_override
> +                           ? xstrdup(ssl_server_name_override)
> +                           : get_server_name(suffix);

nit: Above two lines should be 1 space to the right.

> +
> +        return new_ssl_stream(xstrdup(name), server_name,
>                                fd, CLIENT, state, streamp);
>      } else {
>          VLOG_ERR("%s: connect: %s", name, ovs_strerror(error));
> @@ -1251,6 +1260,16 @@ stream_ssl_set_ciphersuites(const char *arg)
>      ssl_ciphersuites = xstrdup(arg);
>  }
>  
> +/* Sets the server name override for SNI (Server Name Indication).
> + * If 'server_name' is NULL, clears any existing override and SNI will
> + * use the hostname from the connection string. */
> +void
> +stream_ssl_set_server_name(const char *server_name)
> +{
> +    free(ssl_server_name_override);
> +    ssl_server_name_override = nullable_xstrdup(server_name);
> +}
> +
>  /* Set SSL/TLS protocols based on the string input. Aborts with an error
>   * message if 'arg' is invalid. */
>  void
> diff --git a/lib/stream-ssl.h b/lib/stream-ssl.h
> index abd3ba219..6127d6fb1 100644
> --- a/lib/stream-ssl.h
> +++ b/lib/stream-ssl.h
> @@ -28,11 +28,13 @@ void stream_ssl_set_key_and_cert(const char 
> *private_key_file,
>  void stream_ssl_set_protocols(const char *arg);
>  void stream_ssl_set_ciphers(const char *arg);
>  void stream_ssl_set_ciphersuites(const char *arg);
> +void stream_ssl_set_server_name(const char *server_name);
>  
>  #define SSL_OPTION_ENUMS \
>          OPT_SSL_PROTOCOLS, \
>          OPT_SSL_CIPHERS, \
> -        OPT_SSL_CIPHERSUITES
> +        OPT_SSL_CIPHERSUITES, \
> +        OPT_SSL_SERVER_NAME
>  
>  #define STREAM_SSL_LONG_OPTIONS                     \
>          {"private-key", required_argument, NULL, 'p'}, \
> @@ -40,7 +42,8 @@ void stream_ssl_set_ciphersuites(const char *arg);
>          {"ca-cert",     required_argument, NULL, 'C'}, \
>          {"ssl-protocols", required_argument, NULL, OPT_SSL_PROTOCOLS}, \
>          {"ssl-ciphers", required_argument, NULL, OPT_SSL_CIPHERS}, \
> -        {"ssl-ciphersuites", required_argument, NULL, OPT_SSL_CIPHERSUITES}
> +        {"ssl-ciphersuites", required_argument, NULL, OPT_SSL_CIPHERSUITES}, 
> \
> +        {"ssl-server-name", required_argument, NULL, OPT_SSL_SERVER_NAME}
>  
>  #define STREAM_SSL_OPTION_HANDLERS                      \
>          case 'p':                                       \
> @@ -65,10 +68,14 @@ void stream_ssl_set_ciphersuites(const char *arg);
>                                                          \
>          case OPT_SSL_CIPHERSUITES:                      \
>              stream_ssl_set_ciphersuites(optarg);        \
> +            break;                                      \
> +                                                        \
> +        case OPT_SSL_SERVER_NAME:                       \
> +            stream_ssl_set_server_name(optarg);         \
>              break;
>  
>  #define STREAM_SSL_CASES \
>      case 'p': case 'c': case 'C': case OPT_SSL_PROTOCOLS: \
> -    case OPT_SSL_CIPHERS: case OPT_SSL_CIPHERSUITES:
> +    case OPT_SSL_CIPHERS: case OPT_SSL_CIPHERSUITES: case 
> OPT_SSL_SERVER_NAME:
>  
>  #endif /* stream-ssl.h */
> diff --git a/lib/stream.c b/lib/stream.c
> index aa48a973b..feaa1cb2d 100644
> --- a/lib/stream.c
> +++ b/lib/stream.c
> @@ -163,7 +163,9 @@ stream_usage(const char *name, bool active, bool passive,
>             "  --ssl-ciphers=CIPHERS     list of SSL/TLS ciphers to enable\n"
>             "                            with TLSv1.2\n"
>             "  --ssl-ciphersuites=SUITES list of SSL/TLS ciphersuites to\n"
> -           "                            enable with TLSv1.3 and later\n");
> +           "                            enable with TLSv1.3 and later\n"
> +           "  --ssl-server-name=NAME    server name for TLS Server Name\n"
> +           "                            Indication (SNI)\n");
>  #endif
>  }
>  
> diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
> index 5303a46c6..86b0dc7b8 100644
> --- a/ovsdb/ovsdb-server.c
> +++ b/ovsdb/ovsdb-server.c
> @@ -74,6 +74,7 @@ static char *ca_cert_file;
>  static char *ssl_protocols;
>  static char *ssl_ciphers;
>  static char *ssl_ciphersuites;
> +static char *ssl_server_name;
>  static bool bootstrap_ca_cert;
>  
>  /* Try to reclaim heap memory back to system after DB compaction. */
> @@ -1793,6 +1794,7 @@ reconfigure_ssl(const struct shash *all_dbs)
>      const char *resolved_ssl_protocols;
>      const char *resolved_ssl_ciphers;
>      const char *resolved_ssl_ciphersuites;
> +    const char *resolved_ssl_server_name;
>  
>      resolved_private_key = query_db_string(all_dbs, private_key_file, 
> &errors);
>      resolved_certificate = query_db_string(all_dbs, certificate_file, 
> &errors);
> @@ -1801,12 +1803,15 @@ reconfigure_ssl(const struct shash *all_dbs)
>      resolved_ssl_ciphers = query_db_string(all_dbs, ssl_ciphers, &errors);
>      resolved_ssl_ciphersuites = query_db_string(all_dbs, ssl_ciphersuites,
>                                                  &errors);
> +    resolved_ssl_server_name = query_db_string(all_dbs, ssl_server_name,
> +                                               &errors);
>  
>      stream_ssl_set_key_and_cert(resolved_private_key, resolved_certificate);
>      stream_ssl_set_ca_cert_file(resolved_ca_cert, bootstrap_ca_cert);
>      stream_ssl_set_protocols(resolved_ssl_protocols);
>      stream_ssl_set_ciphers(resolved_ssl_ciphers);
>      stream_ssl_set_ciphersuites(resolved_ssl_ciphersuites);
> +    stream_ssl_set_server_name(resolved_ssl_server_name);
>  
>      return errors.string;
>  }
> @@ -2707,6 +2712,10 @@ parse_options(int argc, char *argv[],
>              ssl_ciphersuites = optarg;
>              break;
>  
> +        case OPT_SSL_SERVER_NAME:
> +            ssl_server_name = optarg;
> +            break;
> +
>          case OPT_BOOTSTRAP_CA_CERT:
>              ca_cert_file = optarg;
>              bootstrap_ca_cert = true;
> diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
> index 59245fff8..9b9d73c5f 100644
> --- a/tests/ovs-vsctl.at
> +++ b/tests/ovs-vsctl.at
> @@ -1769,6 +1769,62 @@ AT_CHECK([grep "server name" ovsdb-server.log], [0],
>  OVS_VSCTL_CLEANUP
>  AT_CLEANUP
>  
> +AT_SETUP([TLS server name indication (SNI) with --ssl-server-name])
> +AT_KEYWORDS([ovs-vsctl ssl tls sni client])
> +AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
> +OVSDB_INIT([conf.db])
> +PKIDIR=$abs_top_builddir/tests
> +
> +# This test validates the --ssl-server-name option for SNI.
> +# Test 1: Connect to IP with --ssl-server-name to verify SNI override.
> +# Test 2: Connect to same IP without --ssl-server-name (no SNI sent).
> +
> +AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile \
> +    --private-key=$PKIDIR/testpki-privkey.pem \
> +    --certificate=$PKIDIR/testpki-cert.pem \
> +    --ca-cert=$PKIDIR/testpki-cacert.pem \
> +    --remote=pssl:0:127.0.0.1 \
> +    -vstream_ssl:file:dbg conf.db], [0], [ignore], [ignore])
> +PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT])
> +
> +# Test 1: SNI override - connect to IP but specify server name.
> +# This validates that --ssl-server-name overrides connection hostname.
> +AT_CHECK([ovs-vsctl -t 5 --no-wait \
> +    --db=ssl:127.0.0.1:$SSL_PORT \
> +    --private-key=$PKIDIR/testpki-privkey.pem \
> +    --certificate=$PKIDIR/testpki-cert.pem \
> +    --ca-cert=$PKIDIR/testpki-cacert.pem \
> +    --ssl-server-name=sni-test.example \
> +    add-br br0])
> +
> +# Verify SNI was sent with the overridden name.
> +OVS_WAIT_UNTIL([grep -q \
> +    "connection indicated server name sni-test.example" \
> +    ovsdb-server.log])
> +
> +# Save current log size for Test 2.
> +LOG_SIZE=$(wc -l < ovsdb-server.log)
> +
> +# Test 2: Default behavior without SNI override - should NOT show SNI
> +# connecting to IP address (no hostname to extract).
> +AT_CHECK([ovs-vsctl -t 5 --no-wait \
> +    --db=ssl:127.0.0.1:$SSL_PORT \
> +    --private-key=$PKIDIR/testpki-privkey.pem \
> +    --certificate=$PKIDIR/testpki-cert.pem \
> +    --ca-cert=$PKIDIR/testpki-cacert.pem \
> +    add-br br1])
> +
> +# Stop server to ensure logs are flushed before checking.
> +OVS_VSCTL_CLEANUP
> +
> +# Check that no new SNI messages appeared in Test 2 (connecting to IP
> +# without --ssl-server-name should not generate SNI).
> +AT_CHECK([tail -n +$(($LOG_SIZE + 1)) ovsdb-server.log | \
> +    grep -q "connection indicated server name"], [1])
> +
> +AT_CAPTURE_FILE([ovsdb-server.log])

nit: This should be close to the daemon start at the top.  This macro says
"print out this file on failure".  Placing it at the end doesn't make a
lot of sense, as we'll never get to it on failure.  We're also missing the
on_exit hook to kill ovsdb-server process on failure.

Above two issues are pretty minor, so I fixed them myself.  Also added a
small remark to the NEWS record to avoid it sounding like we didn't support
SNI at all before.

With that, applied to main.  Thanks!

Best regards, Ilya Maximets.
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to