Skip to content

Commit

Permalink
ovsdb: add support for role-based access controls
Browse files Browse the repository at this point in the history
Add suport for ovsdb RBAC (role-based access control). This includes:

   - Support for "RBAC_Role" table. A db schema containing a table
     by this name will enable role-based access controls using
     this table for RBAC role configuration.

     The "RBAC_Role" table has one row per role, with each row having a
     "name" column (role name) and a "permissions" column (map of
     table name to UUID of row in separate permission table.) The
     permission table has one row per access control configuration,
     with the following columns:
          "name"          - name of table to which this row applies
          "authorization" - set of column names and column:key pairs
                            to be compared against client ID to
                            determine authorization status
          "insert_delete" - boolean, true if insertions and
                            authorized deletions are allowed.
          "update"        - Set of columns and column:key pairs for
                            which authorized updates are allowed.
   - Support for a new "role" column in the remote configuration
     table.
   - Logic for applying the RBAC role and permission tables, in
     combination with session role from the remote connection table
     and client id, to determine whether operations modifying database
     contents should be permitted.
   - Support for specifying RBAC role string as a command-line option
     to ovsdb-tool (Ben Pfaff).

Signed-off-by: Lance Richardson <[email protected]>
Co-authored-by: Ben Pfaff <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
hlrichardson and blp committed Jun 8, 2017
1 parent 8155ab7 commit d6db7b3
Show file tree
Hide file tree
Showing 26 changed files with 1,092 additions and 16 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Post-v2.7.0
abbreviated to 4 hex digits.
* "ovn-sbctl lflow-list" can now print OpenFlow flows that correspond
to logical flows.
- OVSDB:
* New support for role-based access control (see ovsdb-server(1)).
- Add the command 'ovs-appctl stp/show' (see ovs-vswitchd(8)).
- OpenFlow:
* All features required by OpenFlow 1.4 are now implemented, so
Expand Down
10 changes: 10 additions & 0 deletions lib/jsonrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,16 @@ jsonrpc_session_get_name(const struct jsonrpc_session *s)
return reconnect_get_name(s->reconnect);
}

const char *
jsonrpc_session_get_id(const struct jsonrpc_session *s)
{
if (s->rpc && s->rpc->stream) {
return stream_get_peer_id(s->rpc->stream);
} else {
return NULL;
}
}

/* Always takes ownership of 'msg', regardless of success. */
int
jsonrpc_session_send(struct jsonrpc_session *s, struct jsonrpc_msg *msg)
Expand Down
1 change: 1 addition & 0 deletions lib/jsonrpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,6 @@ void jsonrpc_session_set_probe_interval(struct jsonrpc_session *,
int probe_interval);
void jsonrpc_session_set_dscp(struct jsonrpc_session *,
uint8_t dscp);
const char *jsonrpc_session_get_id(const struct jsonrpc_session *);

#endif /* jsonrpc.h */
13 changes: 13 additions & 0 deletions lib/ovsdb-error.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ ovsdb_internal_error(struct ovsdb_error *inner_error,
return error;
}

struct ovsdb_error *
ovsdb_perm_error(const char *details, ...)
{
struct ovsdb_error *error;
va_list args;

va_start(args, details);
error = ovsdb_error_valist("permission error", details, args);
va_end(args);

return error;
}

void
ovsdb_error_destroy(struct ovsdb_error *error)
{
Expand Down
4 changes: 4 additions & 0 deletions lib/ovsdb-error.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ struct ovsdb_error *ovsdb_internal_error(struct ovsdb_error *error,
OVS_PRINTF_FORMAT(4, 5)
OVS_WARN_UNUSED_RESULT;

struct ovsdb_error *ovsdb_perm_error(const char *details, ...)
OVS_PRINTF_FORMAT(1, 2)
OVS_WARN_UNUSED_RESULT;

/* Returns a pointer to an ovsdb_error that represents an internal error for
* the current file name and line number with MSG as the associated message.
* The caller is responsible for freeing the internal error. */
Expand Down
6 changes: 6 additions & 0 deletions lib/ovsdb-idl.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ static const char *row_update_names[] = {"row_update", "row_update2"};

static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5);
static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5);
static struct vlog_rate_limit other_rl = VLOG_RATE_LIMIT_INIT(1, 5);

static void ovsdb_idl_clear(struct ovsdb_idl *);
static void ovsdb_idl_send_schema_request(struct ovsdb_idl *);
Expand Down Expand Up @@ -3768,9 +3769,14 @@ ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl,
soft_errors++;
} else if (!strcmp(error->u.string, "not owner")) {
lock_errors++;
} else if (!strcmp(error->u.string, "not allowed")) {
hard_errors++;
ovsdb_idl_txn_set_error_json(txn, op);
} else if (strcmp(error->u.string, "aborted")) {
hard_errors++;
ovsdb_idl_txn_set_error_json(txn, op);
VLOG_WARN_RL(&other_rl,
"transaction error: %s", txn->error);
}
} else {
hard_errors++;
Expand Down
2 changes: 2 additions & 0 deletions ovsdb/automake.mk
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ ovsdb_libovsdb_la_SOURCES = \
ovsdb/monitor.h \
ovsdb/query.c \
ovsdb/query.h \
ovsdb/rbac.c \
ovsdb/rbac.h \
ovsdb/replication.c \
ovsdb/replication.h \
ovsdb/row.c \
Expand Down
44 changes: 41 additions & 3 deletions ovsdb/execution.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "ovsdb-parser.h"
#include "ovsdb.h"
#include "query.h"
#include "rbac.h"
#include "row.h"
#include "server.h"
#include "table.h"
Expand All @@ -39,6 +40,8 @@ struct ovsdb_execution {
struct ovsdb_txn *txn;
struct ovsdb_symbol_table *symtab;
bool durable;
const char *role;
const char *id;

/* Triggers. */
long long int elapsed_msec;
Expand Down Expand Up @@ -97,6 +100,7 @@ lookup_executor(const char *name, bool *read_only)
struct json *
ovsdb_execute(struct ovsdb *db, const struct ovsdb_session *session,
const struct json *params, bool read_only,
const char *role, const char *id,
long long int elapsed_msec, long long int *timeout_msec)
{
struct ovsdb_execution x;
Expand Down Expand Up @@ -126,6 +130,8 @@ ovsdb_execute(struct ovsdb *db, const struct ovsdb_session *session,
x.txn = ovsdb_txn_create(db);
x.symtab = ovsdb_symbol_table_create();
x.durable = false;
x.role = role;
x.id = id;
x.elapsed_msec = elapsed_msec;
x.timeout_msec = LLONG_MAX;
results = NULL;
Expand Down Expand Up @@ -348,6 +354,13 @@ ovsdb_execute_insert(struct ovsdb_execution *x, struct ovsdb_parser *parser,
}
}
}

if (!error && !ovsdb_rbac_insert(x->db, table, row, x->role, x->id)) {
error = ovsdb_perm_error("RBAC rules for client \"%s\" role \"%s\" "
"prohibit row insertion into table \"%s\".",
x->id, x->role, table->schema->name);
}

if (!error) {
*ovsdb_row_get_uuid_rw(row) = row_uuid;
ovsdb_txn_row_insert(x->txn, row);
Expand Down Expand Up @@ -410,6 +423,8 @@ struct update_row_cbdata {
struct ovsdb_txn *txn;
const struct ovsdb_row *row;
const struct ovsdb_column_set *columns;
const char *role;
const char *id;
};

static bool
Expand Down Expand Up @@ -470,7 +485,15 @@ ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser,
ur.txn = x->txn;
ur.row = row;
ur.columns = &columns;
ovsdb_query(table, &condition, update_row_cb, &ur);
if (ovsdb_rbac_update(x->db, table, &columns, &condition, x->role,
x->id)) {
ovsdb_query(table, &condition, update_row_cb, &ur);
} else {
error = ovsdb_perm_error("RBAC rules for client \"%s\" role "
"\"%s\" prohibit modification of "
"table \"%s\".",
x->id, x->role, table->schema->name);
}
json_object_put(result, "count", json_integer_create(ur.n_matches));
}

Expand Down Expand Up @@ -529,7 +552,15 @@ ovsdb_execute_mutate(struct ovsdb_execution *x, struct ovsdb_parser *parser,
mr.txn = x->txn;
mr.mutations = &mutations;
mr.error = &error;
ovsdb_query(table, &condition, mutate_row_cb, &mr);
if (ovsdb_rbac_mutate(x->db, table, &mutations, &condition, x->role,
x->id)) {
ovsdb_query(table, &condition, mutate_row_cb, &mr);
} else {
error = ovsdb_perm_error("RBAC rules for client \"%s\" role "
"\"%s\" prohibit mutate operation on "
"table \"%s\".",
x->id, x->role, table->schema->name);
}
json_object_put(result, "count", json_integer_create(mr.n_matches));
}

Expand Down Expand Up @@ -579,8 +610,15 @@ ovsdb_execute_delete(struct ovsdb_execution *x, struct ovsdb_parser *parser,
dr.n_matches = 0;
dr.table = table;
dr.txn = x->txn;
ovsdb_query(table, &condition, delete_row_cb, &dr);

if (ovsdb_rbac_delete(x->db, table, &condition, x->role, x->id)) {
ovsdb_query(table, &condition, delete_row_cb, &dr);
} else {
error = ovsdb_perm_error("RBAC rules for client \"%s\" role "
"\"%s\" prohibit row deletion from "
"table \"%s\".",
x->id, x->role, table->schema->name);
}
json_object_put(result, "count", json_integer_create(dr.n_matches));
}

Expand Down
6 changes: 5 additions & 1 deletion ovsdb/jsonrpc-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ struct ovsdb_jsonrpc_remote {
struct ovs_list sessions; /* List of "struct ovsdb_jsonrpc_session"s. */
uint8_t dscp;
bool read_only;
char *role;
};

static struct ovsdb_jsonrpc_remote *ovsdb_jsonrpc_server_add_remote(
Expand Down Expand Up @@ -270,6 +271,7 @@ ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr,
ovs_list_init(&remote->sessions);
remote->dscp = options->dscp;
remote->read_only = options->read_only;
remote->role = nullable_xstrdup(options->role);
shash_add(&svr->remotes, name, remote);

if (!listener) {
Expand All @@ -287,6 +289,7 @@ ovsdb_jsonrpc_server_del_remote(struct shash_node *node)
ovsdb_jsonrpc_session_close_all(remote);
pstream_close(remote->listener);
shash_delete(&remote->server->remotes, node);
free(remote->role);
free(remote);
}

Expand Down Expand Up @@ -1038,7 +1041,8 @@ ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
/* Insert into trigger table. */
t = xmalloc(sizeof *t);
ovsdb_trigger_init(&s->up, db, &t->trigger, params, time_msec(),
s->read_only);
s->read_only, s->remote->role,
jsonrpc_session_get_id(s->js));
t->id = id;
hmap_insert(&s->triggers, &t->hmap_node, hash);

Expand Down
1 change: 1 addition & 0 deletions ovsdb/jsonrpc-server.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct ovsdb_jsonrpc_options {
int probe_interval; /* Max idle time before probing, in msec. */
bool read_only; /* Only read-only transactions are allowed. */
int dscp; /* Dscp value for manager connections */
char *role; /* Role, for role-based access controls */
};
struct ovsdb_jsonrpc_options *
ovsdb_jsonrpc_default_options(const char *target);
Expand Down
45 changes: 45 additions & 0 deletions ovsdb/ovsdb-server.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ narrow down the particular syntax that could not be parsed.
The request triggered a bug in \fBovsdb\-server\fR.
.IP "\fBovsdb error\fR"
A map or set contains a duplicate key.
.IP "\fBpermission error\fR"
The request was denied by the role-based access control extension,
introduced in version 2.8.
.RE
.
.IP "3.2. Schema Format"
Expand All @@ -281,6 +284,36 @@ This raises the issue of the behavior of the weak reference when the
rows that it references are deleted. Since version 2.6,
\fBovsdb\-server\fR forces columns that contain weak references to be
mutable.
.IP
Since version 2.8, the table name \fBRBAC_Role\fR is used internally
by the role-based access control extension to \fBovsdb\-server\fR and
should not be used for purposes other than defining mappings of role
names to table access permissions. This table has one row per role
name and the following columns:
.RS
.IP "\fBname\fR"
The role name.
.IP "\fBpermissions\fR"
A map of table name to a reference to a row in a separate permission
table.
.RE
.IP
The separate RBAC permission table has one row per access control
configuration and the following columns:
.RS
.IP "\fBname\fR"
The name of the table to which the row applies.
.IP "\fBauthorization\fR"
The set of column names and column:key pairs to be compared with
the client ID in order to determine the authorization status of
the requested operation.
.IP "\fBinsert_delete\fR"
A boolean value, true if authorized insertions and authorized are allowed,
false if no insertions or deletions are allowed.
.IP "\fBupdate\fR"
The set of columns and column:key pairs for which authorized update and
mutate operations should be permitted.
.RE
.
.IP "4. Wire Protocol"
The original OVSDB specifications included the following reason,
Expand All @@ -299,6 +332,18 @@ any corresponding advantage.
The JSON-RPC specification for HTTP transport is incomplete.
.RE
.
.IP "4.1.3. Transact"
Since version 2.8, role-based access controls can be applied to operations
within a transaction that would modify the contents of the database
(these operations include row insert, row delete, column update, and
column mutate). Role-based access controls are applied when the database
schema contains a table with the name "\fBRBAC_Role\fR" and the connection
on which the transaction request was received has an associated role
name (from the "\fBrole\fR" column in the remote connection table). When
role-based access controls are enabled, transactions that are otherwise
well-formed may be rejected depending on the client's role, ID, and the
contents of the \fBRBAC_Role\fR table and associated permissions table.
.
.IP "4.1.5. Monitor"
For backward compatibility, \fBovsdb\-server\fR currently permits a
single <monitor-request> to be used instead of an array; it is treated
Expand Down
8 changes: 7 additions & 1 deletion ovsdb/ovsdb-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ add_manager_options(struct shash *remotes, const struct ovsdb_row *row)
struct ovsdb_jsonrpc_options *options;
long long int max_backoff, probe_interval;
bool read_only;
const char *target, *dscp_string;
const char *target, *dscp_string, *role;

if (!ovsdb_util_read_string_column(row, "target", &target) || !target) {
VLOG_INFO_RL(&rl, "Table `%s' has missing or invalid `target' column",
Expand All @@ -703,6 +703,12 @@ add_manager_options(struct shash *remotes, const struct ovsdb_row *row)
options->read_only = read_only;
}

free(options->role);
options->role = NULL;
if (ovsdb_util_read_string_column(row, "role", &role) && role) {
options->role = xstrdup(role);
}

options->dscp = DSCP_DEFAULT;
dscp_string = ovsdb_util_read_map_string_column(row, "other_config",
"dscp");
Expand Down
10 changes: 8 additions & 2 deletions ovsdb/ovsdb-tool.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ will print a blank line.
.
.SS "Other Commands"
.
.IP "\fBquery\fI db transaction\fR"
.IP "[\fB\-\-rbac\-role=\fIrole\fR] \fBquery\fI db transaction\fR"
Opens \fIdb\fR, executes \fItransaction\fR on it, and prints the
results. The \fItransaction\fR must be a JSON array in the format of
the \fBparams\fR array for the JSON-RPC \fBtransact\fR method, as
Expand All @@ -142,8 +142,11 @@ safely run concurrently with other database activity, including
\fBovsdb\-server\fR and other database writers. The \fItransaction\fR
may specify database modifications, but these will have no effect on
\fIdb\fR.
.IP
By default, the transaction is executed using the ``superuser'' RBAC
role. Use \fB\-\-rbac\-role\fR to specify a different role.
.
.IP "\fBtransact\fI db transaction\fR"
.IP "[\fR\-\-rbac\-role=\fIrole\fR] \fBtransact\fI db transaction\fR"
Opens \fIdb\fR, executes \fItransaction\fR on it, prints the results,
and commits any changes to \fIdb\fR. The \fItransaction\fR must be a
JSON array in the format of the \fBparams\fR array for the JSON-RPC
Expand All @@ -154,6 +157,9 @@ command will fail if the database is opened for writing by any other
process, including \fBovsdb\-server\fR(1). Use \fBovsdb\-client\fR(1),
instead, to write to a database that is served by
\fBovsdb\-server\fR(1).
.IP
By default, the transaction is executed using the ``superuser'' RBAC
role. Use \fB\-\-rbac\-role\fR to specify a different role.
.
.IP "\fBshow\-log\fI db\fR"
Prints a summary of the records in \fIdb\fR's log, including the time
Expand Down
Loading

0 comments on commit d6db7b3

Please sign in to comment.