@@ -5569,6 +5569,182 @@ check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
hmap_destroy(&dhcpv6_opts_to_add);
}
+static const char *rbac_chassis_auth[] =
+ {"name"};
+static const char *rbac_chassis_update[] =
+ {"nb_cfg", "external_ids", "encaps", "vtep_logical_switches"};
+
+static const char *rbac_encap_auth[] =
+ {""};
+static const char *rbac_encap_update[] =
+ {"type", "options", "ip"};
+
+static const char *rbac_port_binding_auth[] =
+ {""};
+static const char *rbac_port_binding_update[] =
+ {"chassis"};
+
+static const char *rbac_mac_binding_auth[] =
+ {""};
+static const char *rbac_mac_binding_update[] =
+ {"logical_port", "ip", "mac", "datapath"};
+
+static struct rbac_perm_cfg {
+ const char *table;
+ const char **auth;
+ int n_auth;
+ bool insdel;
+ const char **update;
+ int n_update;
+ const struct sbrec_rbac_permission *row;
+} rbac_perm_cfg[] = {
+ {
+ "Chassis",
+ rbac_chassis_auth,
+ ARRAY_SIZE(rbac_chassis_auth),
+ true,
+ rbac_chassis_update,
+ ARRAY_SIZE(rbac_chassis_update),
+ NULL
+ },{
+ "Encap",
+ rbac_encap_auth,
+ ARRAY_SIZE(rbac_encap_auth),
+ true,
+ rbac_encap_update,
+ ARRAY_SIZE(rbac_encap_update),
+ NULL
+ },{
+ "Port_Binding",
+ rbac_port_binding_auth,
+ ARRAY_SIZE(rbac_port_binding_auth),
+ false,
+ rbac_port_binding_update,
+ ARRAY_SIZE(rbac_port_binding_update),
+ NULL
+ },{
+ "MAC_Binding",
+ rbac_mac_binding_auth,
+ ARRAY_SIZE(rbac_mac_binding_auth),
+ true,
+ rbac_mac_binding_update,
+ ARRAY_SIZE(rbac_mac_binding_update),
+ NULL
+ },
+ {}
+};
+
+static bool
+ovn_rbac_validate_perm(const struct sbrec_rbac_permission *perm)
+{
+ struct rbac_perm_cfg *pcfg;
+ int i, j, n_found;
+
+ for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
+ if (!strcmp(perm->table, pcfg->table)) {
+ break;
+ }
+ }
+ if (!pcfg->table) {
+ return false;
+ }
+ if (perm->n_authorization != pcfg->n_auth ||
+ perm->n_update != pcfg->n_update) {
+ return false;
+ }
+ if (perm->insert_delete != pcfg->insdel) {
+ return false;
+ }
+ /* verify perm->authorization vs. pcfg->auth */
+ n_found = 0;
+ for (i = 0; i < pcfg->n_auth; i++) {
+ for (j = 0; j < perm->n_authorization; j++) {
+ if (!strcmp(pcfg->auth[i], perm->authorization[j])) {
+ n_found++;
+ break;
+ }
+ }
+ }
+ if (n_found != pcfg->n_auth) {
+ return false;
+ }
+
+ /* verify perm->update vs. pcfg->update */
+ n_found = 0;
+ for (i = 0; i < pcfg->n_update; i++) {
+ for (j = 0; j < perm->n_update; j++) {
+ if (!strcmp(pcfg->update[i], perm->update[j])) {
+ n_found++;
+ break;
+ }
+ }
+ }
+ if (n_found != pcfg->n_update) {
+ return false;
+ }
+
+ /* Success, db state matches expected state */
+ pcfg->row = perm;
+ return true;
+}
+
+static void
+ovn_rbac_create_perm(struct rbac_perm_cfg *pcfg,
+ struct northd_context *ctx,
+ const struct sbrec_rbac_role *rbac_role)
+{
+ struct sbrec_rbac_permission *rbac_perm;
+
+ rbac_perm = sbrec_rbac_permission_insert(ctx->ovnsb_txn);
+ sbrec_rbac_permission_set_table(rbac_perm, pcfg->table);
+ sbrec_rbac_permission_set_authorization(rbac_perm,
+ pcfg->auth,
+ pcfg->n_auth);
+ sbrec_rbac_permission_set_insert_delete(rbac_perm, pcfg->insdel);
+ sbrec_rbac_permission_set_update(rbac_perm,
+ pcfg->update,
+ pcfg->n_update);
+ sbrec_rbac_role_update_permissions_setkey(rbac_role, pcfg->table,
+ rbac_perm);
+}
+
+static void
+check_and_update_rbac(struct northd_context *ctx)
+{
+ const struct sbrec_rbac_role *rbac_role = NULL;
+ const struct sbrec_rbac_permission *perm_row, *perm_next;
+ const struct sbrec_rbac_role *role_row, *role_row_next;
+ struct rbac_perm_cfg *pcfg;
+
+ for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
+ pcfg->row = NULL;
+ }
+
+ SBREC_RBAC_PERMISSION_FOR_EACH_SAFE(perm_row, perm_next, ctx->ovnsb_idl) {
+ if (!ovn_rbac_validate_perm(perm_row)) {
+ sbrec_rbac_permission_delete(perm_row);
+ }
+ }
+ SBREC_RBAC_ROLE_FOR_EACH_SAFE(role_row, role_row_next, ctx->ovnsb_idl) {
+ if (strcmp(role_row->name, "ovn-controller")) {
+ sbrec_rbac_role_delete(role_row);
+ } else {
+ rbac_role = role_row;
+ }
+ }
+
+ if (!rbac_role) {
+ rbac_role = sbrec_rbac_role_insert(ctx->ovnsb_txn);
+ sbrec_rbac_role_set_name(rbac_role, "ovn-controller");
+ }
+
+ for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
+ if (!pcfg->row) {
+ ovn_rbac_create_perm(pcfg, ctx, rbac_role);
+ }
+ }
+}
+
/* Updates the sb_cfg and hv_cfg columns in the northbound NB_Global table. */
static void
update_northbound_cfg(struct northd_context *ctx,
@@ -5782,6 +5958,19 @@ main(int argc, char *argv[])
add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name);
add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_addresses);
+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_role);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_name);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_permissions);
+
+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_permission);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_rbac_permission_col_table);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_rbac_permission_col_authorization);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_rbac_permission_col_insert_delete);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_permission_col_update);
+
ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);
ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
@@ -5800,6 +5989,7 @@ main(int argc, char *argv[])
if (ctx.ovnsb_txn) {
check_and_add_supported_dhcp_opts_to_sb_db(&ctx);
check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx);
+ check_and_update_rbac(&ctx);
}
unixctl_server_run(unixctl);
@@ -1404,6 +1404,165 @@
</li>
</ol>
+ <h1>Security</h1>
+
+ <h2>Role-Based Access Controls for the Soutbound DB</h2>
+ <p>
+ In order to provide additional security against the possibility that an OVN
+ chassis could become compromised in a way that would allow rogue
+ software to make arbitrary modifications to the southbound database state
+ and thus disrupt the OVN network, role-based access controls are provided
+ for the southbound database.
+ </p>
+
+ <p>
+ The role-based access control (RBAC) implementation involves the addition
+ of two tables to an OVSDB schema: the <code>RBAC_Role</code> table, which
+ is indexed by role name and provides a map from the names of the various
+ tables that are modifiable for a given role to individual rows in a
+ permission table containing detailed permission information, and the
+ permission table itself which has rows containing the following
+ information:
+ </p>
+ <ul>
+ <li>
+ Table name.
+ </li>
+
+ <li>
+ Authorization criteria; a set of strings containing the names of
+ columns (or column:key pairs for columns containing string:string maps).
+ The contents of at least one of the columns or column:key values
+ in the row to be modified must be equal to the ID of the client
+ attempting to modify the row in order for the authorization check
+ to pass. If the authorization criteria is empty, authorization
+ checking is disabled and all clients will be considered to be
+ authorized.
+ </li>
+
+ <li>
+ Row insertion/deletion permission; boolean value indicating whether
+ insertion and deletion of rows is allowed for the associated table.
+ If true, insertion is allowed for any client and deletion is allowed
+ for authorized clients.
+ </li>
+
+ <li>
+ Column update permissions; a set of strings containing the names of
+ columns or column:key pairs that may be updated or mutated by authorized
+ clients. Modifications to columns within a row are only permitted when
+ the authorization check for the client passes and all columns to be
+ modified are included in this set of modifiable columns.
+ </li>
+ </ul>
+
+ <p>
+ RBAC configuration for the OVN southbound database is maintained by
+ ovn-northd. With RBAC enabled, modifications are only permitted for the
+ <code>Chassis</code>, <code>Encap</code>, <code>Port_Binding</code>, and
+ <code>MAC_Binding</code> tables, and are resstricted as follows:
+ </p>
+ <dl>
+ <dt><code>Chassis</code></dt>
+ <dd>
+ <ul>
+ <li>
+ <code>Authorization</code>: client ID must match the chassis name.
+ </li>
+ <li>
+ <code>Insert/Delete</code>: row insertion and authorized row deletion
+ are permitted.
+ </li>
+ <li>
+ <code>Update</code>: The columns <code>nb_cfg</code>,
+ <code>external_ids</code>, <code>encaps</code>, and
+ <code>vtep_logical_switches</code> may be modified when authorized.
+ </li>
+ </ul>
+ </dd>
+
+ <dt><code>Encap</code></dt>
+ <dd>
+ <ul>
+ <li>
+ <code>Authorization</code>: disabled (all clients are considered
+ to be authorized. Future: add a "creating chassis name" column to
+ this table and use it for authorization checking.
+ </li>
+ <li>
+ <code>Insert/Delete</code>: row insertion and authorized row deletion
+ are permitted.
+ </li>
+ <li>
+ <code>Update</code>: The columns <code>type</code>,
+ <code>options</code>, and <code>ip</code> can be modified.
+ </li>
+ </ul>
+ </dd>
+
+ <dt><code>Port_Binding</code></dt>
+ <dd>
+ <ul>
+ <li>
+ <code>Authorization</code>: disabled (all clients are considered
+ authorized. A future enhancement may add columns (or keys to
+ <code>external_ids</code>) in order to control which chassis are
+ allowed to bind each port.
+ </li>
+ <li>
+ <code>Insert/Delete</code>: row insertion/deletion are not permitted
+ (ovn-northd maintains rows in this table.
+ </li>
+ <li>
+ <code>Update</code>: Only modifications to the <code>chassis</code>
+ column are permitted.
+ </li>
+ </ul>
+ </dd>
+
+ <dt><code>MAC_Binding</code></dt>
+ <dd>
+ <ul>
+ <li>
+ <code>Authorization</code>: disabled (all clients are considered
+ to be authorized).
+ </li>
+ <li>
+ <code>Insert/Delete</code>: row insertion/deletion are permitted.
+ </li>
+ <li>
+ <code>Update</code>: The columns <code>logical_port</code>,
+ <code>ip</code>, <code>mac</code>, and <code>datapath</code> may be
+ modified by ovn-controller.
+ </li>
+ </ul>
+ </dd>
+ </dl>
+
+ <p>
+ Enabling RBAC for ovn-controller connections to the southbound database
+ requires the following steps:
+ </p>
+
+ <ul>
+ <li>
+ Creating SSL certificates for each chassis with the certificate CN field
+ set to the chassis name (e.g. for a chassis named <code>chassis-1</code>,
+ via the command "<code>ovs-pki -B 1024 -u req+sign chassis-1
+ switch</code>").
+ </li>
+ <li>
+ Configuring each ovn-controller to use SSL when connecting to the
+ southbound database (e.g. via "<code>ovs-vsctl set open .
+ external-ids:ovn-remote=ssl:x.x.x.x:6642</code>").
+ </li>
+ <li>
+ Configuring a southbound database SSL remote with "ovn-controller" role
+ (e.g. via "<code>ovn-sbctl set-connection role=ovn-controller
+ pssl:6642</code>").
+ </li>
+ </ul>
+
<h1>Design Decisions</h1>
<h2>Tunnel Encapsulations</h2>
@@ -1,7 +1,7 @@
{
"name": "OVN_Southbound",
- "version": "1.10.0",
- "cksum": "860871483 9898",
+ "version": "1.12.0",
+ "cksum": "3249454395 10927",
"tables": {
"SB_Global": {
"columns": {
@@ -176,6 +176,7 @@
"min": 0,
"max": 1}},
"read_only": {"type": "boolean"},
+ "role": {"type": "string"},
"other_config": {"type": {"key": "string",
"value": "string",
"min": 0,
@@ -201,4 +202,25 @@
"value": "string",
"min": 0,
"max": "unlimited"}}},
- "maxRows": 1}}}
+ "maxRows": 1},
+ "RBAC_Role": {
+ "columns": {
+ "name": {"type": "string"},
+ "permissions": {
+ "type": {"key": {"type": "string"},
+ "value": {"type": "uuid",
+ "refTable": "RBAC_Permission",
+ "refType": "weak"},
+ "min": 0, "max": "unlimited"}}},
+ "isRoot": true},
+ "RBAC_Permission": {
+ "columns": {
+ "table": {"type": "string"},
+ "authorization": {"type": {"key": "string",
+ "min": 0,
+ "max": "unlimited"}},
+ "insert_delete": {"type": "boolean"},
+ "update" : {"type": {"key": "string",
+ "min": 0,
+ "max": "unlimited"}}},
+ "isRoot": true}}}
@@ -2504,6 +2504,9 @@ tcp.flags = RST;
<code>true</code> to restrict these connections to read-only
transactions, <code>false</code> to allow them to modify the database.
</column>
+ <column name="role">
+ String containing name of role for this connection entry.
+ </column>
</group>
<group title="Client Failure Detection and Handling">
@@ -2678,4 +2681,40 @@ tcp.flags = RST;
<column name="external_ids"/>
</group>
</table>
+ <table name="RBAC_Role">
+ Roles table for role-based access controls.
+
+ <column name="name">
+ String containing the role name, corresponding to the <code>role</code>
+ column in the <code>Connection</code> table.
+ </column>
+
+ <column name="permissions">
+ A string:uuid map mapping table names to rows in the
+ <code>RBAC_Permission</code> table.
+ </column>
+ </table>
+ <table name="RBAC_Permission">
+ Permissions table for role-based access controls.
+
+ <column name="table">
+ Name of table to which this row applies.
+ </column>
+
+ <column name="authorization">
+ Set of strings identifying columns and column:key pairs to be compared
+ with client ID. At least one match is required in order to be
+ authorized. A zero-length string is treated as a special value
+ indicating all clients should be considered authorized.
+ </column>
+
+ <column name="insert_delete">
+ Boolean value, if "true" then row insertions and authorized row
+ deletions are allowed.
+ </column>
+ <column name="update">
+ Set of strings identifying columns and column:key pairs that authorized
+ clients are allowed to modify.
+ </column>
+ </table>
</database>
Add rbac "roles" and "permissions" tables to ovn southbound database schema, add support to ovn-northd for managing these tables. Signed-off-by: Lance Richardson <lrichard@redhat.com> --- v2: - Corrected authorization setup for Chassis and Encap tables. v3: - Added description of RBAC implementation to ovn-architecture.7.xml - Bumped sb schema version. ovn/northd/ovn-northd.c | 190 +++++++++++++++++++++++++++++++++++++++++++++ ovn/ovn-architecture.7.xml | 159 +++++++++++++++++++++++++++++++++++++ ovn/ovn-sb.ovsschema | 28 ++++++- ovn/ovn-sb.xml | 39 ++++++++++ 4 files changed, 413 insertions(+), 3 deletions(-)