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 7ce9a64..3db7ace 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 d315c8a..14d8894 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 7c4b733..243243d 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 ebe0d9d..c1aa619 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 603de94..800506b 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 304eabb..7a8b83e 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
 }