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 <[email protected]> --- 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(-) diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 027e5a1..e2393d5 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -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); diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml index d8114f1..935cb86 100644 --- a/ovn/ovn-architecture.7.xml +++ b/ovn/ovn-architecture.7.xml @@ -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> diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema index a576dc4..29b6196 100644 --- a/ovn/ovn-sb.ovsschema +++ b/ovn/ovn-sb.ovsschema @@ -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}}} diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index 8605c98..7ffae74 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -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> -- 2.7.4 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
