From 78876719e0eaf181a6775417ad41ec5852efa863 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 18 Mar 2010 17:12:02 -0700 Subject: [PATCH] ovsdb-server: Obtain SSL configuration from database. ovsdb-server should be able to obtain its SSL configuration from the database that it is serving out, instead of having to specify it on the command line. This commit makes it so. --- debian/openvswitch-switch.init | 3 + ovsdb/ovsdb-server.1.in | 9 +++ ovsdb/ovsdb-server.c | 109 ++++++++++++++++++++++++++------- tests/ovsdb-macros.at | 3 +- tests/ovsdb-server.at | 54 ++++++++++++++++ xenserver/etc_init.d_vswitch | 5 +- 6 files changed, 159 insertions(+), 24 deletions(-) diff --git a/debian/openvswitch-switch.init b/debian/openvswitch-switch.init index 7ce9a645..3db7ace7 100755 --- a/debian/openvswitch-switch.init +++ b/debian/openvswitch-switch.init @@ -230,6 +230,9 @@ case "$1" in set -- "$@" --detach --pidfile $monitor_opt set -- "$@" --remote punix:/var/run/ovsdb-server set -- "$@" /etc/openvswitch-switch/conf + set -- "$@" --private-key=db:SSL,private_key + set -- "$@" --certificate=db:SSL,certificate + set -- "$@" --bootstrap-ca-cert=db:SSL,ca_cert set -- "$@" $OVSDB_SERVER_OPTS echo -n "Starting ovsdb-server: " start-stop-daemon --start --quiet --pidfile /var/run/ovsdb-server.pid \ diff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in index d315c8ae..14d88944 100644 --- a/ovsdb/ovsdb-server.1.in +++ b/ovsdb/ovsdb-server.1.in @@ -59,6 +59,15 @@ run a single command, e.g.: .SS "Logging Options" .so lib/vlog.man .SS "Public Key Infrastructure Options" +The options described below for configuring the SSL public key +infrastructure accept a special syntax for obtaining their +configuration from the database. If any of these options is given +\fBdb:\fItable\fB,\fIcolumn\fR as its argument, then the actual file +name is read from the specified \fIcolumn\fR in \fItable\fR within the +\fBovsdb\-server\fR database. The \fIcolumn\fR must have type string +or set of strings. The first nonempty string in the table is taken as +the file name. (This means that ordinarily there should be at most +one row in \fItable\fR.) .so lib/ssl.man .so lib/ssl-bootstrap.man .SS "Other Options" diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c index 7c4b7330..243243d2 100644 --- a/ovsdb/ovsdb-server.c +++ b/ovsdb/ovsdb-server.c @@ -49,6 +49,12 @@ #include "vlog.h" #define THIS_MODULE VLM_ovsdb_server +/* SSL configuration. */ +static char *private_key_file; +static char *certificate_file; +static char *ca_cert_file; +static bool bootstrap_ca_cert; + static unixctl_cb_func ovsdb_server_exit; static unixctl_cb_func ovsdb_server_compact; @@ -57,8 +63,8 @@ static void parse_options(int argc, char *argv[], char **file_namep, char **run_command); static void usage(void) NO_RETURN; -static void set_remotes(struct ovsdb_jsonrpc_server *jsonrpc, - const struct ovsdb *db, struct shash *remotes); +static void reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc, + const struct ovsdb *db, struct shash *remotes); int main(int argc, char *argv[]) @@ -95,7 +101,7 @@ main(int argc, char *argv[]) } jsonrpc = ovsdb_jsonrpc_server_create(db); - set_remotes(jsonrpc, db, &remotes); + reconfigure_from_db(jsonrpc, db, &remotes); retval = unixctl_server_create(unixctl_path, &unixctl); if (retval) { @@ -126,7 +132,7 @@ main(int argc, char *argv[]) exiting = false; while (!exiting) { - set_remotes(jsonrpc, db, &remotes); + reconfigure_from_db(jsonrpc, db, &remotes); ovsdb_jsonrpc_server_run(jsonrpc); unixctl_server_run(unixctl); ovsdb_trigger_run(db, time_msec()); @@ -159,13 +165,14 @@ main(int argc, char *argv[]) } static void -query_db_remotes(const char *name_, const struct ovsdb *db, - struct shash *remotes) +parse_db_string_column(const struct ovsdb *db, + const char *name_, + const struct ovsdb_table **tablep, + const struct ovsdb_column **columnp) { char *name, *table_name, *column_name; const struct ovsdb_column *column; const struct ovsdb_table *table; - const struct ovsdb_row *row; char *save_ptr = NULL; name = xstrdup(name_); @@ -173,26 +180,68 @@ query_db_remotes(const char *name_, const struct ovsdb *db, table_name = strtok_r(NULL, ",", &save_ptr); column_name = strtok_r(NULL, ",", &save_ptr); if (!table_name || !column_name) { - ovs_fatal(0, "remote \"%s\": invalid syntax", name_); + ovs_fatal(0, "\"%s\": invalid syntax", name_); } table = ovsdb_get_table(db, table_name); if (!table) { - ovs_fatal(0, "remote \"%s\": no table named %s", name_, table_name); + ovs_fatal(0, "\"%s\": no table named %s", name_, table_name); } column = ovsdb_table_schema_get_column(table->schema, column_name); if (!column) { - ovs_fatal(0, "remote \"%s\": table \"%s\" has no column \"%s\"", + ovs_fatal(0, "\"%s\": table \"%s\" has no column \"%s\"", name_, table_name, column_name); } + free(name); if (column->type.key.type != OVSDB_TYPE_STRING || column->type.value.type != OVSDB_TYPE_VOID) { - ovs_fatal(0, "remote \"%s\": type of table \"%s\" column \"%s\" is " + ovs_fatal(0, "\"%s\": table \"%s\" column \"%s\" is " "not string or set of strings", - name_, table_name, column_name); + name_, table->schema->name, column->name); + } + + *columnp = column; + *tablep = table; +} + +static const char * +query_db_string(const struct ovsdb *db, const char *name) +{ + if (!name || strncmp(name, "db:", 3)) { + return name; + } else { + const struct ovsdb_column *column; + const struct ovsdb_table *table; + const struct ovsdb_row *row; + + parse_db_string_column(db, name, &table, &column); + + HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node, &table->rows) { + const struct ovsdb_datum *datum; + size_t i; + + datum = &row->fields[column->index]; + for (i = 0; i < datum->n; i++) { + if (datum->keys[i].string[0]) { + return datum->keys[i].string; + } + } + } + return NULL; } +} + +static void +query_db_remotes(const char *name, const struct ovsdb *db, + struct shash *remotes) +{ + const struct ovsdb_column *column; + const struct ovsdb_table *table; + const struct ovsdb_row *row; + + parse_db_string_column(db, name, &table, &column); HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node, &table->rows) { const struct ovsdb_datum *datum; @@ -203,17 +252,17 @@ query_db_remotes(const char *name_, const struct ovsdb *db, shash_add_once(remotes, datum->keys[i].string, NULL); } } - - free(name); } +/* Reconfigures ovsdb-server based on information in the database. */ static void -set_remotes(struct ovsdb_jsonrpc_server *jsonrpc, - const struct ovsdb *db, struct shash *remotes) +reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc, + const struct ovsdb *db, struct shash *remotes) { struct shash resolved_remotes; struct shash_node *node; + /* Configure remotes. */ shash_init(&resolved_remotes); SHASH_FOR_EACH (node, remotes) { const char *name = node->name; @@ -226,8 +275,13 @@ set_remotes(struct ovsdb_jsonrpc_server *jsonrpc, } ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes); shash_destroy(&resolved_remotes); -} + /* Configure SSL. */ + stream_ssl_set_private_key_file(query_db_string(db, private_key_file)); + stream_ssl_set_certificate_file(query_db_string(db, certificate_file)); + stream_ssl_set_ca_cert_file(query_db_string(db, ca_cert_file), + bootstrap_ca_cert); +} static void ovsdb_server_exit(struct unixctl_conn *conn, const char *args OVS_UNUSED, @@ -282,7 +336,9 @@ parse_options(int argc, char *argv[], char **file_namep, LEAK_CHECKER_LONG_OPTIONS, #ifdef HAVE_OPENSSL {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT}, - STREAM_SSL_LONG_OPTIONS + {"private-key", required_argument, 0, 'p'}, + {"certificate", required_argument, 0, 'c'}, + {"ca-cert", required_argument, 0, 'C'}, #endif {0, 0, 0, 0}, }; @@ -322,14 +378,25 @@ parse_options(int argc, char *argv[], char **file_namep, LEAK_CHECKER_OPTION_HANDLERS #ifdef HAVE_OPENSSL - STREAM_SSL_OPTION_HANDLERS + case 'p': + private_key_file = optarg; + break; + + case 'c': + certificate_file = optarg; + break; + + case 'C': + ca_cert_file = optarg; + bootstrap_ca_cert = false; + break; case OPT_BOOTSTRAP_CA_CERT: - stream_ssl_set_ca_cert_file(optarg, true); + ca_cert_file = optarg; + bootstrap_ca_cert = true; break; #endif - case '?': exit(EXIT_FAILURE); diff --git a/tests/ovsdb-macros.at b/tests/ovsdb-macros.at index ebe0d9de..c1aa619c 100644 --- a/tests/ovsdb-macros.at +++ b/tests/ovsdb-macros.at @@ -7,7 +7,8 @@ m4_define([OVSDB_INIT], [0], [stdout], [ignore]) AT_CHECK( [[ovsdb-tool transact $1 \ - '[{"op": "insert", + '["Open_vSwitch", + {"op": "insert", "table": "Open_vSwitch", "row": {}}]']], [0], [ignore], [ignore])]) diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at index 603de945..800506bb 100644 --- a/tests/ovsdb-server.at +++ b/tests/ovsdb-server.at @@ -90,6 +90,60 @@ AT_CHECK( OVSDB_SERVER_SHUTDOWN AT_CLEANUP +AT_SETUP([SSL db: implementation]) +AT_KEYWORDS([ovsdb server positive ssl $5]) +AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) +AT_SKIP_IF([test "x$RANDOM" = x]) +SSL_PORT=`expr 32767 + \( $RANDOM % 32767 \)` +PKIDIR=$abs_top_srcdir/tests +AT_SKIP_IF([expr "$PKIDIR" : ".*[ '\" + \\]"]) +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]) +AT_CHECK( + [[ovsdb-tool transact db \ + '["mydb", + {"op": "insert", + "table": "SSL", + "row": {"private_key": "'"$PKIDIR/testpki-privkey2.pem"'", + "certificate": "'"$PKIDIR/testpki-cert2.pem"'", + "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'"}}]']], + [0], [ignore], [ignore]) +AT_CHECK( + [ovsdb-server --detach --pidfile=$PWD/pid \ + --private-key=db:SSL,private_key \ + --certificate=db:SSL,certificate \ + --ca-cert=db:SSL,ca_cert \ + --remote=pssl:$SSL_PORT:127.0.0.1 --unixctl=$PWD/unixctl db], + [0], [ignore], [ignore]) +AT_CHECK( + [[ovsdb-client \ + --private-key=$PKIDIR/testpki-privkey.pem \ + --certificate=$PKIDIR/testpki-cert.pem \ + --ca-cert=$PKIDIR/testpki-cacert.pem \ + transact ssl:127.0.0.1:$SSL_PORT \ + '["mydb", + {"op": "select", + "table": "SSL", + "where": [], + "columns": ["private_key"]}]']], + [0], [stdout], [ignore], [test ! -e pid || kill `cat pid`]) +cat stdout >> output +AT_CHECK_UNQUOTED( + [perl $srcdir/uuidfilt.pl output], [0], + [[[{"rows":[{"private_key":"$PKIDIR/testpki-privkey2.pem"}]}] +]], [ignore], [test ! -e pid || kill `cat pid`]) +OVSDB_SERVER_SHUTDOWN +AT_CLEANUP + AT_SETUP([compacting online]) AT_KEYWORDS([ovsdb server compact]) AT_DATA([schema], [ORDINAL_SCHEMA diff --git a/xenserver/etc_init.d_vswitch b/xenserver/etc_init.d_vswitch index 304eabb1..7a8b83e6 100755 --- a/xenserver/etc_init.d_vswitch +++ b/xenserver/etc_init.d_vswitch @@ -171,12 +171,13 @@ function start_ovsdb_server { valgrind_opt="valgrind --log-file=$OVSDB_SERVER_VALGRIND_LOG $OVSDB_SERVER_VALGRIND_OPT" daemonize="n" fi + ssl_opts="--private-key=db:SSL,private_key --certificate=db:SSL,certificate --bootstrap-ca-cert=db:SSL,ca_cert" if [ "$daemonize" != "y" ]; then # Start in background and force a "success" message action "Starting ovsdb_server ($strace_opt$valgrind_opt)" true - (nice -n "$OVSDB_SERVER_PRIORITY" $strace_opt $valgrind_opt "$ovsdb_server" "$OVSDB_SERVER_DB" --pidfile="$OVSDB_SERVER_PIDFILE" --detach $monitor_opt --no-chdir -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt $remotes) & + (nice -n "$OVSDB_SERVER_PRIORITY" $strace_opt $valgrind_opt "$ovsdb_server" "$OVSDB_SERVER_DB" --pidfile="$OVSDB_SERVER_PIDFILE" --detach $monitor_opt --no-chdir -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt $remotes $ssl_opts) & else - action "Starting ovsdb-server" nice -n "$OVSDB_SERVER_PRIORITY" "$ovsdb_server" "$OVSDB_SERVER_DB" --pidfile="$OVSDB_SERVER_PIDFILE" --detach $monitor_opt --no-chdir -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt $remotes + action "Starting ovsdb-server" nice -n "$OVSDB_SERVER_PRIORITY" "$ovsdb_server" "$OVSDB_SERVER_DB" --pidfile="$OVSDB_SERVER_PIDFILE" --detach $monitor_opt --no-chdir -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt $remotes $ssl_opts fi } -- 2.30.2