2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

Add support for specifying SSL connection parameters to ovsdb

Signed-off-by: Ethan Rahn <erahn@arista.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
Ethan Rahn 2016-10-06 16:21:33 -07:00 committed by Ben Pfaff
parent 60230e06ce
commit e18a1d0861
26 changed files with 243 additions and 20 deletions

View File

@ -85,6 +85,7 @@ Eohyung Lee liquidnuker@gmail.com
Eric Garver e@erig.me
Eric Sesterhenn eric.sesterhenn@lsexperts.de
Ethan J. Jackson ejj@eecs.berkeley.edu
Ethan Rahn erahn@arista.com
Eziz Durdyyev ezizdurdy@gmail.com
Flavio Fernandes flavio@flaviof.com
Flavio Leitner fbl@redhat.com

2
NEWS
View File

@ -1,5 +1,7 @@
Post-v2.6.0
---------------------
- Utilities and daemons that support SSL now allow protocols and
ciphers to be configured with --ssl-protocols and --ssl-ciphers.
- OVN:
* QoS is now implemented via egress shaping rather than ingress policing.
* DSCP marking is now supported, via the new northbound QoS table.

View File

@ -461,6 +461,8 @@ MAN_FRAGMENTS += \
lib/ssl-peer-ca-cert-syn.man \
lib/ssl.man \
lib/ssl-syn.man \
lib/ssl-connect.man \
lib/ssl-connect-syn.man \
lib/table.man \
lib/unixctl.man \
lib/unixctl-syn.man \

5
lib/ssl-connect-syn.man Normal file
View File

@ -0,0 +1,5 @@
.IP "SSL connection options:"
[\fB\-\-ssl\-protocols=\fIprotocols\fR]
.br
[\fB\-\-ssl\-ciphers=\fIciphers\fR]
.br

12
lib/ssl-connect.man Normal file
View File

@ -0,0 +1,12 @@
.IP "\fB\-\-ssl\-protocols=\fIprotocols\fR"
Specifies, in a comma- or space-delimited list, the SSL protocols
\fB\*(PN\fR will enable for SSL connections. Supported
\fIprotocols\fR include \fBTLSv1\fR, \fBTLSv1.1\fR, and \fBTLSv1.2\fR.
Regardless of order, the highest protocol supported by both sides will
be chosen when making the connection. The default when this option is
omitted is \fBTLSv1,TLSv1.1,TLSv1.2\fR.
.
.IP "\fB\-\-ssl\-ciphers=\fIciphers\fR"
Specifies, in OpenSSL cipher string format, the ciphers \fB\*(PN\fR will
support for SSL connections. The default when this option is omitted is
\fBHIGH:!aNULL:!MD5\fR.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 Nicira, Inc.
* Copyright (c) 2011, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -74,3 +74,17 @@ stream_ssl_set_key_and_cert(const char *private_key_file,
stream_ssl_set_private_key_file(private_key_file);
stream_ssl_set_certificate_file(certificate_file);
}
void
stream_ssl_set_protocols(const char *arg OVS_UNUSED)
{
/* Ignore this option since it seems harmless to set SSL protocols if SSL
* won't be used. */
}
void
stream_ssl_set_ciphers(const char *arg OVS_UNUSED)
{
/* Ignore this option since it seems harmless to set SSL ciphers if SSL
* won't be used. */
}

View File

@ -162,6 +162,8 @@ struct ssl_config_file {
static struct ssl_config_file private_key;
static struct ssl_config_file certificate;
static struct ssl_config_file ca_cert;
static char *ssl_protocols = "TLSv1,TLSv1.1,TLSv1.2";
static char *ssl_ciphers = "HIGH:!aNULL:!MD5";
/* 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
@ -966,6 +968,7 @@ do_ssl_init(void)
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
NULL);
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!MD5");
return 0;
}
@ -1114,6 +1117,68 @@ stream_ssl_set_key_and_cert(const char *private_key_file,
}
}
/* Sets SSL ciphers based on string input. Aborts with an error message
* if 'arg' is invalid. */
void
stream_ssl_set_ciphers(const char *arg)
{
if (ssl_init() || !arg || !strcmp(ssl_ciphers, arg)) {
return;
}
if (SSL_CTX_set_cipher_list(ctx,arg) == 0) {
VLOG_ERR("SSL_CTX_set_cipher_list: %s",
ERR_error_string(ERR_get_error(), NULL));
}
ssl_ciphers = xstrdup(arg);
}
/* Set SSL protocols based on the string input. Aborts with an error message
* if 'arg' is invalid. */
void
stream_ssl_set_protocols(const char *arg)
{
if (ssl_init() || !arg || !strcmp(arg, ssl_protocols)){
return;
}
/* Start with all the flags off and turn them on as requested. */
long protocol_flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
protocol_flags |= SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
char *s = xstrdup(arg);
char *save_ptr = NULL;
char *word = strtok_r(s, " ,\t", &save_ptr);
if (word == NULL) {
VLOG_ERR("SSL protocol settings invalid");
goto exit;
}
while (word != NULL) {
long on_flag;
if (!strcasecmp(word, "TLSv1.2")){
on_flag = SSL_OP_NO_TLSv1_2;
} else if (!strcasecmp(word, "TLSv1.1")){
on_flag = SSL_OP_NO_TLSv1_1;
} else if (!strcasecmp(word, "TLSv1")){
on_flag = SSL_OP_NO_TLSv1;
} else {
VLOG_ERR("%s: SSL protocol not recognized", word);
goto exit;
}
/* Reverse the no flag and mask it out in the flags
* to turn on that protocol. */
protocol_flags &= ~on_flag;
word = strtok_r(NULL, " ,\t", &save_ptr);
};
/* Set the actual options. */
SSL_CTX_set_options(ctx, protocol_flags);
ssl_protocols = xstrdup(arg);
exit:
free(s);
}
/* Reads the X509 certificate or certificates in file 'file_name'. On success,
* stores the address of the first element in an array of pointers to
* certificates in '*certs' and the number of certificates in the array in

View File

@ -25,11 +25,19 @@ void stream_ssl_set_ca_cert_file(const char *file_name, bool bootstrap);
void stream_ssl_set_peer_ca_cert_file(const char *file_name);
void stream_ssl_set_key_and_cert(const char *private_key_file,
const char *certificate_file);
void stream_ssl_set_protocols(const char *arg);
void stream_ssl_set_ciphers(const char *arg);
#define SSL_OPTION_ENUMS \
OPT_SSL_PROTOCOLS, \
OPT_SSL_CIPHERS
#define STREAM_SSL_LONG_OPTIONS \
{"private-key", required_argument, NULL, 'p'}, \
{"certificate", required_argument, NULL, 'c'}, \
{"ca-cert", required_argument, NULL, 'C'}
{"ca-cert", required_argument, NULL, 'C'}, \
{"ssl-protocols", required_argument, NULL, OPT_SSL_PROTOCOLS}, \
{"ssl-ciphers", required_argument, NULL, OPT_SSL_CIPHERS}
#define STREAM_SSL_OPTION_HANDLERS \
case 'p': \
@ -42,6 +50,14 @@ void stream_ssl_set_key_and_cert(const char *private_key_file,
\
case 'C': \
stream_ssl_set_ca_cert_file(optarg, false); \
break;
break; \
\
case OPT_SSL_PROTOCOLS: \
stream_ssl_set_protocols(optarg); \
break; \
\
case OPT_SSL_CIPHERS: \
stream_ssl_set_ciphers(optarg); \
break;
#endif /* stream-ssl.h */

View File

@ -20,6 +20,8 @@ ovsdb/ovsdb-client.1: \
lib/daemon.man \
lib/ssl-bootstrap-syn.man \
lib/ssl-bootstrap.man \
lib/ssl-connect-syn.man \
lib/ssl-connect.man \
lib/ssl-syn.man \
lib/ssl.man \
lib/table.man \
@ -34,6 +36,8 @@ lib/daemon-syn.man:
lib/daemon.man:
lib/ssl-bootstrap-syn.man:
lib/ssl-bootstrap.man:
lib/ssl-connect-syn.man:
lib/ssl-connect.man:
lib/ssl-syn.man:
lib/ssl.man:
lib/table.man:
@ -54,6 +58,8 @@ ovsdb/ovsdb-server.1: \
lib/service.man \
lib/ssl-bootstrap-syn.man \
lib/ssl-bootstrap.man \
lib/ssl-connect-syn.man \
lib/ssl-connect.man \
lib/ssl-peer-ca-cert-syn.man \
lib/ssl-peer-ca-cert.man \
lib/ssl-syn.man \
@ -78,6 +84,8 @@ lib/service-syn.man:
lib/service.man:
lib/ssl-bootstrap-syn.man:
lib/ssl-bootstrap.man:
lib/ssl-connect-syn.man:
lib/ssl-connect.man:
lib/ssl-peer-ca-cert-syn.man:
lib/ssl-peer-ca-cert.man:
lib/ssl-syn.man:

View File

@ -166,7 +166,8 @@ parse_options(int argc, char *argv[])
OPT_PEER_CA_CERT = UCHAR_MAX + 1,
OPT_BOOTSTRAP_CA_CERT,
VLOG_OPTION_ENUMS,
DAEMON_OPTION_ENUMS
DAEMON_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static struct option long_options[] = {

View File

@ -674,7 +674,8 @@ parse_options(int argc, char *argv[])
OPT_PEER_CA_CERT = UCHAR_MAX + 1,
OPT_BOOTSTRAP_CA_CERT,
VLOG_OPTION_ENUMS,
DAEMON_OPTION_ENUMS
DAEMON_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static struct option long_options[] = {

View File

@ -4696,6 +4696,7 @@ parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
enum {
DAEMON_OPTION_ENUMS,
VLOG_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option long_options[] = {
{"ovnsb-db", required_argument, NULL, 'd'},

View File

@ -166,7 +166,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
OPT_COMMANDS,
OPT_OPTIONS,
VLOG_OPTION_ENUMS,
TABLE_OPTION_ENUMS
TABLE_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option global_long_options[] = {
{"db", required_argument, NULL, OPT_DB},

View File

@ -161,7 +161,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
OPT_COMMANDS,
OPT_OPTIONS,
VLOG_OPTION_ENUMS,
TABLE_OPTION_ENUMS
TABLE_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option global_long_options[] = {
{"db", required_argument, NULL, OPT_DB},

View File

@ -153,6 +153,7 @@ parse_options(int argc, char *argv[])
OPT_MINIMAL,
OPT_ALL,
DAEMON_OPTION_ENUMS,
SSL_OPTION_ENUMS,
VLOG_OPTION_ENUMS
};
static const struct option long_options[] = {

View File

@ -55,6 +55,7 @@ ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1)
.so lib/vlog-syn.man
.so lib/ssl-syn.man
.so lib/ssl-bootstrap-syn.man
.so lib/ssl-connect-syn.man
.so lib/common-syn.man
.
.SH DESCRIPTION
@ -205,6 +206,8 @@ With any other command, they have no effect.
.SS "Public Key Infrastructure Options"
.so lib/ssl.man
.so lib/ssl-bootstrap.man
.SS "SSL Connection Options"
.so lib/ssl-connect.man
.SS "Other Options"
.so lib/common.man
.SH "SEE ALSO"

View File

@ -175,7 +175,8 @@ parse_options(int argc, char *argv[])
OPT_TIMESTAMP,
VLOG_OPTION_ENUMS,
DAEMON_OPTION_ENUMS,
TABLE_OPTION_ENUMS
TABLE_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},

View File

@ -23,6 +23,7 @@ ovsdb\-server \- Open vSwitch database server
.so lib/ssl-syn.man
.so lib/ssl-bootstrap-syn.man
.so lib/ssl-peer-ca-cert-syn.man
.so lib/ssl-connect-syn.man
.so lib/unixctl-syn.man
.so lib/common-syn.man
.
@ -135,6 +136,8 @@ one row in \fItable\fR.)
.so lib/ssl.man
.so lib/ssl-bootstrap.man
.so lib/ssl-peer-ca-cert.man
.SS "SSL Connection Options"
.so lib/ssl-connect.man
.SS "Other Options"
.so lib/unixctl.man
.so lib/common.man

View File

@ -74,6 +74,8 @@ struct db {
static char *private_key_file;
static char *certificate_file;
static char *ca_cert_file;
static char *ssl_protocols;
static char *ssl_ciphers;
static bool bootstrap_ca_cert;
static unixctl_cb_func ovsdb_server_exit;
@ -1110,13 +1112,19 @@ reconfigure_ssl(const struct shash *all_dbs)
const char *resolved_private_key;
const char *resolved_certificate;
const char *resolved_ca_cert;
const char *resolved_ssl_protocols;
const char *resolved_ssl_ciphers;
resolved_private_key = query_db_string(all_dbs, private_key_file, &errors);
resolved_certificate = query_db_string(all_dbs, certificate_file, &errors);
resolved_ca_cert = query_db_string(all_dbs, ca_cert_file, &errors);
resolved_ssl_protocols = query_db_string(all_dbs, ssl_protocols, &errors);
resolved_ssl_ciphers = query_db_string(all_dbs, ssl_ciphers, &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);
return errors.string;
}
@ -1517,7 +1525,8 @@ parse_options(int *argcp, char **argvp[],
OPT_SYNC_EXCLUDE,
OPT_ACTIVE,
VLOG_OPTION_ENUMS,
DAEMON_OPTION_ENUMS
DAEMON_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option long_options[] = {
{"remote", required_argument, NULL, OPT_REMOTE},
@ -1531,9 +1540,7 @@ parse_options(int *argcp, char **argvp[],
VLOG_LONG_OPTIONS,
{"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
{"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
{"private-key", required_argument, NULL, 'p'},
{"certificate", required_argument, NULL, 'c'},
{"ca-cert", required_argument, NULL, 'C'},
STREAM_SSL_LONG_OPTIONS,
{"sync-from", required_argument, NULL, OPT_SYNC_FROM},
{"sync-exclude-tables", required_argument, NULL, OPT_SYNC_EXCLUDE},
{"active", no_argument, NULL, OPT_ACTIVE},
@ -1590,6 +1597,14 @@ parse_options(int *argcp, char **argvp[],
bootstrap_ca_cert = false;
break;
case OPT_SSL_PROTOCOLS:
ssl_protocols = optarg;
break;
case OPT_SSL_CIPHERS:
ssl_ciphers = optarg;
break;
case OPT_BOOTSTRAP_CA_CERT:
ca_cert_file = optarg;
bootstrap_ca_cert = true;

View File

@ -513,9 +513,13 @@ AT_DATA([schema],
"columns": {
"private_key": {"type": "string"},
"certificate": {"type": "string"},
"ca_cert": {"type": "string"}}}}}
"ca_cert": {"type": "string"},
"ssl_protocols" : {"type": "string"},
"ssl_ciphers" : {"type" : "string"}}}}}
]])
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
# The !ECDHE-ECDSA-AES256-GCM-SHA384 in the ssl_ciphers is so that
# a cipher negotiation failure can be tested for later.
AT_CHECK(
[[ovsdb-tool transact db \
'["mydb",
@ -523,13 +527,17 @@ AT_CHECK(
"table": "SSL",
"row": {"private_key": "'"$PKIDIR/testpki-privkey2.pem"'",
"certificate": "'"$PKIDIR/testpki-cert2.pem"'",
"ca_cert": "'"$PKIDIR/testpki-cacert.pem"'"}}]']],
"ca_cert": "'"$PKIDIR/testpki-cacert.pem"'",
"ssl_protocols": "'"TLSv1.2,TLSv1.1"'",
"ssl_ciphers": "'"HIGH:!aNULL:!MD5:!ECDHE-ECDSA-AES256-GCM-SHA384"'"}}]']],
[0], [ignore], [ignore])
AT_CHECK(
[ovsdb-server --log-file --detach --no-chdir --pidfile \
--private-key=db:mydb,SSL,private_key \
--certificate=db:mydb,SSL,certificate \
--ca-cert=db:mydb,SSL,ca_cert \
--ssl-protocols=db:mydb,SSL,ssl_protocols \
--ssl-ciphers=db:mydb,SSL,ssl_ciphers \
--remote=pssl:0:127.0.0.1 db],
[0], [ignore], [ignore])
PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT])
@ -538,18 +546,74 @@ AT_CHECK(
--private-key=$PKIDIR/testpki-privkey.pem \
--certificate=$PKIDIR/testpki-cert.pem \
--ca-cert=$PKIDIR/testpki-cacert.pem \
--ssl-protocols=TLSv1.2,TLSv1.1 \
--ssl-ciphers=HIGH:!aNULL:!MD5 \
transact ssl:127.0.0.1:$SSL_PORT \
'["mydb",
{"op": "select",
"table": "SSL",
"where": [],
"columns": ["private_key"]}]']],
"columns": ["private_key"]}]']],
[0], [stdout], [ignore], [test ! -e pid || kill `cat pid`])
cat stdout >> output
AT_CHECK_UNQUOTED(
[cat output], [0],
[[@<:@{"rows":@<:@{"private_key":"$PKIDIR/testpki-privkey2.pem"}@:>@}@:>@
]], [ignore], [test ! -e pid || kill `cat pid`])
# Check that when the server has TLSv1.1+ and the client has
# TLSv1 that the connection fails.
AT_CHECK(
[[ovsdb-client \
--private-key=$PKIDIR/testpki-privkey.pem \
--certificate=$PKIDIR/testpki-cert.pem \
--ca-cert=$PKIDIR/testpki-cacert.pem \
--ssl-protocols=TLSv1 \
--ssl-ciphers=HIGH:!aNULL:!MD5 \
transact ssl:127.0.0.1:$SSL_PORT \
'["mydb",
{"op": "select",
"table": "SSL",
"where": [],
"columns": ["private_key"]}]']],
[1], [stdout],
[stderr],
[test ! -e pid || kill `cat pid`])
cat stderr > output
AT_CHECK_UNQUOTED(
[grep "failed to connect" output], [0],
[ovsdb-client: failed to connect to "ssl:127.0.0.1:$SSL_PORT" (Protocol error)
],
[ignore], [test ! -e pid || kill `cat pid`])
# Check that when ciphers are not compatible, that a negotiation
# failure occurs.
AT_CHECK(
[[ovsdb-client \
--private-key=$PKIDIR/testpki-privkey.pem \
--certificate=$PKIDIR/testpki-cert.pem \
--ca-cert=$PKIDIR/testpki-cacert.pem \
--ssl-protocols=TLSv1.2,TLSv1.1 \
--ssl-ciphers=ECDHE-ECDSA-AES256-GCM-SHA384 \
transact ssl:127.0.0.1:$SSL_PORT \
'["mydb",
{"op": "select",
"table": "SSL",
"where": [],
"columns": ["private_key"]}]']],
[1], [stdout],
[stderr],
[test ! -e pid || kill `cat pid`])
cat stderr > output
AT_CHECK_UNQUOTED(
[grep "failed to connect" output], [0],
[ovsdb-client: failed to connect to "ssl:127.0.0.1:$SSL_PORT" (Protocol error)
],
[ignore], [test ! -e pid || kill `cat pid`])
# The error message for being unable to negotiate a shared ciphersuite
# is 'sslv3 alert handshake failure'. This is not the clearest message.
AT_CHECK_UNQUOTED(
[grep "sslv3 alert handshake failure" output], [0],
[stdout],
[ignore], [test ! -e pid || kill `cat pid`])
OVSDB_SERVER_SHUTDOWN
AT_CLEANUP

View File

@ -55,7 +55,8 @@ parse_options(int argc, char *argv[])
{
enum {
OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1,
DAEMON_OPTION_ENUMS
DAEMON_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option long_options[] = {
{"verbose", optional_argument, NULL, 'v'},

View File

@ -190,7 +190,8 @@ parse_options(int argc, char *argv[])
OPT_READ_ONLY,
DAEMON_OPTION_ENUMS,
OFP_VERSION_OPTION_ENUMS,
VLOG_OPTION_ENUMS
VLOG_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option long_options[] = {
{"timeout", required_argument, NULL, 't'},

View File

@ -259,7 +259,8 @@ parse_options(int argc, char *argv[])
OPT_UNIXCTL,
VLOG_OPTION_ENUMS,
DAEMON_OPTION_ENUMS,
OFP_VERSION_OPTION_ENUMS
OFP_VERSION_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option long_options[] = {
{"hub", no_argument, NULL, 'H'},

View File

@ -206,7 +206,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
OPT_COMMANDS,
OPT_OPTIONS,
VLOG_OPTION_ENUMS,
TABLE_OPTION_ENUMS
TABLE_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option global_long_options[] = {
{"db", required_argument, NULL, OPT_DB},

View File

@ -144,6 +144,7 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
OPT_DISABLE_SYSTEM,
DAEMON_OPTION_ENUMS,
OPT_DPDK,
SSL_OPTION_ENUMS,
OPT_DUMMY_NUMA,
};
static const struct option long_options[] = {

View File

@ -165,7 +165,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
OPT_PEER_CA_CERT,
OPT_LOCAL,
VLOG_OPTION_ENUMS,
TABLE_OPTION_ENUMS
TABLE_OPTION_ENUMS,
SSL_OPTION_ENUMS,
};
static const struct option global_long_options[] = {
{"db", required_argument, NULL, OPT_DB},