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 <lrich...@redhat.com> --- ovn/northd/ovn-northd.c | 190 +++++++++++++++++++++++++++++++++++++++++++++ ovn/ovn-architecture.7.xml | 155 ++++++++++++++++++++++++++++++++++++ ovn/ovn-sb.ovsschema | 28 ++++++- ovn/ovn-sb.xml | 39 ++++++++++ 4 files changed, 409 insertions(+), 3 deletions(-) diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index d0a5ba2..2d4bc81 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -5608,6 +5608,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, @@ -5821,6 +5997,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); @@ -5839,6 +6028,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..eb3d827 100644 --- a/ovn/ovn-architecture.7.xml +++ b/ovn/ovn-architecture.7.xml @@ -1404,6 +1404,161 @@ </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 of an OVN + chassis becoming compromised in such a way as to 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 implementation of role-based access controls (RBAC) requires the + addition of two tables to an OVSDB schema: the <code>RBAC_Role</code> + table, which is indexed by role name and maps the the names of the various + tables that may be modifiable for a given role to individual rows in a + permissions table containing detailed permission information for that role, + and the permission table itself which consists of rows containing the + following information: + </p> + <dl> + <dt><code>Table Name</code></dt> + <dd> + The name of the associated table. This column exists primarily as an + aid for humans reading the contents of this table. + </dd> + + <dt><code>Auth Criteria</code></dt> + <dd> + 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 for the role + will be treated as authorized. + </dd> + + <dt><code>Insert/Delete</code></dt> + <dd> + 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. + </dd> + + <dt><code>Updatable Columns</code></dt> + <dd> + 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. + </dd> + </dl> + + <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> + <p> + <code>Authorization</code>: client ID must match the chassis name. + </p> + <p> + <code>Insert/Delete</code>: row insertion and authorized row deletion + are permitted. + </p> + <p> + <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. + </p> + </dd> + + <dt><code>Encap</code></dt> + <dd> + <p> + <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. + </p> + <p> + <code>Insert/Delete</code>: row insertion and authorized row deletion + are permitted. + </p> + <p> + <code>Update</code>: The columns <code>type</code>, + <code>options</code>, and <code>ip</code> can be modified. + </p> + </dd> + + <dt><code>Port_Binding</code></dt> + <dd> + <p> + <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. + </p> + <p> + <code>Insert/Delete</code>: row insertion/deletion are not permitted + (ovn-northd maintains rows in this table. + </p> + <p> + <code>Update</code>: Only modifications to the <code>chassis</code> + column are permitted. + </p> + </dd> + + <dt><code>MAC_Binding</code></dt> + <dd> + <p> + <code>Authorization</code>: disabled (all clients are considered + to be authorized). + </p> + <p> + <code>Insert/Delete</code>: row insertion/deletion are permitted. + </p> + <p> + <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. + </p> + </dd> + </dl> + + <p> + Enabling RBAC for ovn-controller connections to the southbound database + requires the following steps: + </p> + + <ol> + <li> + Creating SSL certificates for each chassis with the certificate CN field + set to the chassis name (e.g. for a chassis with + <code>external-ids:system-id=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> + </ol> + <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 5542f7e..2a2e32a 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -2512,6 +2512,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"> @@ -2686,4 +2689,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 d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev