On 10/15/25 11:27 PM, Guru Shetty wrote:
> On Wed, 15 Oct 2025 at 04:52, Ilya Maximets <[email protected]> wrote:
> 
>> On 10/13/25 11:07 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]>
>>> ---
>>>  lib/ssl.man           |  7 ++++
>>>  lib/ssl.xml           |  9 +++++
>>>  lib/stream-nossl.c    |  7 ++++
>>>  lib/stream-ssl.c      | 20 +++++++++-
>>>  lib/stream-ssl.h      | 14 +++++--
>>>  tests/ovsdb-server.at | 86 ++++++++++++++++++++++++++++++++++++++++++-
>>>  6 files changed, 138 insertions(+), 5 deletions(-)
>>
>> Hi.  Thanks for the patch!  The change is definitely a nice addition.
>>
>> See a few comments below.
>>
>>>
>>> diff --git a/lib/ssl.man b/lib/ssl.man
>>> index 9bec3a786..d67c91a8f 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 SSL/TLS Server Name Indication
>> (SNI).
>>
>> nit: SNI is not supported by SSL, only TLS, so we may just say TLS here.
>> We keep SSL/TLS in many places for things that can be in both even though
>> we no longer support SSL, but it may be good to not mention it for TLS-only
>> features and try to slowly move away from using SSL at least in the docs.
>>
> 
> Done
> 
>>
>>> +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..c7c0bddd4 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 SSL/TLS Server Name Indication
>> (SNI).
>>
>> nit: TLS.
>>
> Done
> 
>>
>>> +    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>
>>
>> We need a change in the ssl-syn.man as well.
>>
> Done
> 
>>
>> I'm also not sure if this should be in "ssl" or "ssl-connect".  It can
>> likely
>> be argued both ways, so it's fine to keep the new options in "ssl".
> 
> Okay. I am keeping it in "ssl".

OK.

> 
>> Current man
>> pages are definitely a mess, as not every binary that has "ssl-connect"
>> options
>> includes the "ssl-connect" man page shard.  At the same time most binaries
>> include ssl.man in their man pages, but not all of them implement
>> --ssl-server-name
>> option with this patch.  For exmaple, ovsdb-server includes ssl.man, but
>> this
>> change doesn't add support for the the server-name to it.  ovsdb-server
>> dosn't use
>> the STREAM_SSL_OPTION_HANDLERS macro, because it allows getting the
>> configuration
>> directly from a database, so we need to do manual changes while adding new
>> options.
>>
>> Commit 4d09d6b48ef5 ("stream-ssl: Add explicit support for configuring
>> TLSv1.3.")
>> has an exmaple of what needs to be changed in ovsdb-server.
>>
> Thanks. I made changes to ovsdb-server too by looking at the commit. Did
> you have other binaries too in mind that need special handling?

Initially I thought we'll need some changes in ovs-vswitchd as well, but
it's not the case.

> 
> 
>> And we'll need an update for the stream_usage() function in lib/stream.c.
>>
> Done
> 
>>
>> And a small NEWS entry would also be nice to have.
>>
> Done
> 
>>
>>> diff --git a/lib/stream-nossl.c b/lib/stream-nossl.c
>>> index 105ac377a..665f65b51 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 SSL/TLS server
>> name if
>>
>> nit: TLS.
>>
> Done
> 
>>
>>> +     * SSL/TLS won't be used. */
>>> +}
>>> diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c
>>> index 99c82b6af..f97bfd739 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,10 @@ 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: Since this block is getting larger, it may be good to have an empty
>> line
>> here between the definitions and the return.
>>
> Done
> 
>>
>>> +        return new_ssl_stream(xstrdup(name), server_name,
>>>                                fd, CLIENT, state, streamp);
>>>      } else {
>>>          VLOG_ERR("%s: connect: %s", name, ovs_strerror(error));
>>> @@ -1251,6 +1259,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 = server_name ? xstrdup(server_name) :
>> NULL;
>>
>> nit: nullable_xstrdup(server_name);
>>
> Done
> 
>>
>>> +}
>>> +
>>>  /* 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 7036b176d..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,9 +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 'p': case 'c': case 'C': case OPT_SSL_PROTOCOLS: \
>>> +    case OPT_SSL_CIPHERS: case OPT_SSL_CIPHERSUITES: case
>> OPT_SSL_SERVER_NAME:
>>
>> Hmm.  Could you split the addition of OPT_SSL_CIPHERSUITES into a separate
>> patch, so we can backport this fix?
>>
>> Done. I made this into a 2 patch series.

Thanks!

> 
> 
>>>
>>>  #endif /* stream-ssl.h */
>>> diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
>>> index ca6e931be..245ef01ca 100644
>>> --- a/tests/ovsdb-server.at
>>> +++ b/tests/ovsdb-server.at
>>> @@ -1043,7 +1043,91 @@ OVSDB_SERVER_SHUTDOWN(["
>>>    /Protocol error/d
>>>  "])
>>>  AT_CLEANUP
>>> -
>>> +
>>
>> Keep the form feed.
>>
>>> +AT_SETUP([SSL/TLS db: SNI (Server Name Indication)])
>>
>> We have a similar test in ovs-vsctl.at, maybe we should extend it instead
>> of creating a new one here?  Either way, we're not really testing the
>> server here, but the client.  It seems reasonable to use ovs-vsctl for the
>> test instead of ovsdb-client, as database control tools are more likely
>> to be used in practice.
>>
> For v3, I decided to add a new test in ovs-vsctl.at instead. It felt like a
> new test
> would be cleaner as it specifically tests SNI override. I can do a v4 if
> you want to merge
> into a single test.

A separate test makes sense.  I'll take a look at v3.

Best regards, Ilya Maximets.

> 
> The new test includes all your other comments from below.
> 
> 
>>
>>> +AT_KEYWORDS([ovsdb server positive ssl tls sni])
>>> +AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
>>> +# For this test, we pass PKIDIR through a ovsdb-tool transact and
>>
>> Doesn't look like we do.
>>
>>> +# msys on Windows does not convert the path style automatically.
>>> +# So, do that forcefully with a 'pwd -W' (called through pwd()
>> function).
>>> +PKIDIR="$(cd $abs_top_builddir/tests && pwd)"
>>> +AT_SKIP_IF([expr "$PKIDIR" : ".*[[       '\"
>>> +\\]]"])
>>> +
>>> +# Create additional certificates for SNI testing in the current
>> directory
>>
>> nit: Period at the end of the sentence.  Same for other comments.
>>
>>> +OVS_PKI="sh $abs_top_srcdir/utilities/ovs-pki.in --dir=./sni-pki
>> --log=./sni-pki.log"
>>
>> I see other tests do this, but it's not clear to me why.  We should be
>> able to
>> use ovs-pki script inside the tests directly without the 'sh' magic.
>>
>>> +# Remove any existing PKI directory to ensure clean state for test
>>> +rm -rf ./sni-pki
>>
>> Tests are always executed from a clean state, this should not be needed.
>>
>>> +AT_CHECK([$OVS_PKI init], [0], [ignore], [ignore])
>>> +
>>> +# Create a certificate for a hostname that differs from the connection
>> IP
>>> +AT_CHECK([$OVS_PKI req+sign backend-service.test switch], [0],
>> [ignore], [ignore])
>>
>> Also, do we need to generate new certificates here?  Can we use the
>> existing
>> ones from $abs_top_builddir/tests ?
>>
>>> +
>>> +AT_DATA([schema],
>>> +  [[{"name": "mydb",
>>> +     "tables": {
>>> +       "SSL": {
>>> +         "columns": {
>>> +           "private_key": {"type": "string"},
>>> +           "certificate": {"type": "string"},
>>> +           "ca_cert": {"type": "string"}
>>> +         }}}}
>>> +]])
>>> +AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
>>> +
>>> +# Use direct file paths for SSL configuration
>>> +on_exit 'kill `cat *.pid`'
>>> +AT_CHECK(
>>> +  [ovsdb-server --log-file -vstream_ssl:dbg --detach --no-chdir
>> --pidfile \
>>
>> nit: -vstream_ssl:file:dbg to avoid accidentally enabling other types of
>> logging.
>>
>>> +        --private-key=backend-service.test-privkey.pem \
>>> +        --certificate=backend-service.test-cert.pem \
>>> +        --ca-cert=sni-pki/switchca/cacert.pem \
>>> +        --remote=pssl:0:127.0.0.1 db],
>>> +  [0], [ignore], [ignore])
>>> +PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT])
>>> +
>>> +# SSL_OVSDB_CLIENT_SNI(SNI_HOSTNAME)
>>> +m4_define([SSL_OVSDB_CLIENT_SNI], [dnl
>>> +  ovsdb-client -vconsole:stream_ssl:dbg \
>>
>> Looks like there is a bug in other tests, the logging module should go
>> before the target, i.e. -vstream_ssl:console:dbg.
>>
>>> +    --private-key=backend-service.test-privkey.pem \
>>> +    --certificate=backend-service.test-cert.pem \
>>> +    --ca-cert=sni-pki/switchca/cacert.pem \
>>> +    --ssl-server-name=[$1] \
>>> +    list-dbs ssl:127.0.0.1:$SSL_PORT])
>>> +
>>> +# SSL_OVSDB_CLIENT_NO_SNI() - test default behavior without SNI override
>>> +m4_define([SSL_OVSDB_CLIENT_NO_SNI], [dnl
>>> +  ovsdb-client -vconsole:stream_ssl:dbg \
>>> +    --private-key=backend-service.test-privkey.pem \
>>> +    --certificate=backend-service.test-cert.pem \
>>> +    --ca-cert=sni-pki/switchca/cacert.pem \
>>> +    list-dbs ssl:127.0.0.1:$SSL_PORT])
>>
>> There is no need for two macros, we could check if the arguemnt provided
>> or not and add the argument conditionally, i.e.:
>>
>>     m4_if([$1], [], [], [--ssl-server-name=$1]) \
>>
>>> +
>>> +# SNI Test Flow:
>>> +# 1. Client sends SNI extension with --ssl-server-name=HOSTNAME during
>> SSL handshake
>>> +# 2. Server receives SNI extension and logs "connection indicated
>> server name HOSTNAME"
>>> +# 3. We verify SNI transmission by checking server log for the expected
>> message
>>> +# This validates that the client-side SNI implementation correctly
>> sends SNI to the server
>>> +
>>> +# Test 1: SNI matches server certificate - should succeed
>>> +AT_CHECK(SSL_OVSDB_CLIENT_SNI([backend-service.test]), [0], [stdout],
>> [stderr])
>>> +AT_CHECK([grep -q "connection indicated server name
>> backend-service.test" ovsdb-server.log])
>>
>> May need to be OVS_WAIT_UNTIL.
>>
>>> +
>>> +# Save the current log size to check only new messages in Test 2
>>> +LOG_SIZE=$(wc -l < ovsdb-server.log)
>>> +
>>> +# Test 2: Default behavior without SNI override - should work
>>> +# and NOT show SNI server name in debug log (since no --ssl-server-name
>> was used)
>>> +AT_CHECK(SSL_OVSDB_CLIENT_NO_SNI(), [0], [stdout], [stderr])
>>> +# Check only new log entries after Test 1 - should have no SNI messages
>>> +AT_CHECK([tail -n +$(($LOG_SIZE + 1)) ovsdb-server.log | grep -q
>> "connection indicated server name"], [1])
>>
>> It's hard to wait for something that shouldn't happen, so we may need to
>> do this
>> check after the server shutdown to be sure that the logs are flushed.
>>
>> Also, the line is a little too long, can be split with a '\' in the middle.
>>
>>> +
>>> +OVSDB_SERVER_SHUTDOWN(["
>>> +  /stream_ssl|WARN/d
>>> +  /Protocol error/d
>>
>> Do we need these?  I haven't run the test, but it shouldn't cause any
>> warnings, IIUC.
>>
>>> +"])
>>> +AT_CLEANUP
>>> +
>>>  OVS_START_SHELL_HELPERS
>>>  # ovsdb_check_online_compaction MODEL
>>>  #
>>
>> Best regards, Ilya Maximets.
>>
> 

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to