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. Without this option, the hostname from the connection
+ string is used for SNI, as before.
- Userspace datapath:
* Conntrack now supports the FTP commands EPSV and EPRT with IPv4
connections, instead of limiting these commands to IPv6 only.
[\fB\-\-certificate=\fIcert.pem\fR]
.br
[\fB\-\-ca\-cert=\fIcacert.pem\fR]
+.br
+[\fB\-\-ssl\-server\-name=\fIservername\fR]
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.
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>
/* 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. */
+}
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.) */
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);
+
+ return new_ssl_stream(xstrdup(name), server_name,
fd, CLIENT, state, streamp);
} else {
VLOG_ERR("%s: connect: %s", name, ovs_strerror(error));
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
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'}, \
{"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': \
\
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 */
" --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
}
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. */
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);
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;
}
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;
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_CAPTURE_FILE([ovsdb-server.log])
+on_exit 'kill $(cat ovsdb-server.pid)'
+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_CLEANUP
+
dnl ----------------------------------------------------------------------
AT_BANNER([set ingress policing test])