ovsdb-server: Obtain SSL configuration from database.
authorBen Pfaff <blp@nicira.com>
Fri, 19 Mar 2010 00:12:02 +0000 (17:12 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 19 Mar 2010 23:47:11 +0000 (16:47 -0700)
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
ovsdb/ovsdb-server.1.in
ovsdb/ovsdb-server.c
tests/ovsdb-macros.at
tests/ovsdb-server.at
xenserver/etc_init.d_vswitch

index 7ce9a64508ebbbfe7736669b45415255665c1dc3..3db7ace79a9e44f1aa90dda58338c361b26996eb 100755 (executable)
@@ -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 \
index d315c8ae00daf55b626b29a19f32aeaf3ff2ca38..14d8894498c6bab1f715a2787ff600ec17857eed 100644 (file)
@@ -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"
index 7c4b73306cfcba2ca1a45eb0615d0d3b1147794c..243243d2e08bf7b13ff1e1024878c0fb78d64db7 100644 (file)
 #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);
 
index ebe0d9de27a4c7957680fc35e54ba2cc9c51dbf8..c1aa619c3a951a79794c50633c5012dedbea722b 100644 (file)
@@ -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])])
index 603de94576121f39cf7bfbca14c569e0a643dd39..800506bb319f5b578a3b301c2b58d1865c1da186 100644 (file)
@@ -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" : ".*[      '\"
+\r\\]"])
+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
index 304eabb1cf962b5fd0da18aee5eedeae5dd0aa23..7a8b83e66bad433a223893b4625df79199a68d4e 100755 (executable)
@@ -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
 }