PolicyDatabase

Overview
This document describes a relational database schema that stores security policies for Openstack. However, it has been designed to be generic enough so that it could also store policies for other cloud systems such as Azure and Amazon, to allow cloud federations to share a common policy store.

An OpenStack Policy file comprises a set of policy rules. For example, "identity:create_region": "role:admin or is_admin:1" may be a policy rule from Keystone's policy file. This says that the principal must either be the administrator or have the role of admin in order to create a region with the identity API. A policy rule logically comprises a set of conditions. The example policy rule comprises the following conditions: service equals the identity API AND action equals create_region AND (role equals admin OR is_admin equals TRUE).

OpenStack policies are stored in the database in Disjunctive Normal Form (DNF). The DNF stores sets of simple conditions combined by the AND logical operator, and each set is combined by the OR logical operator. Each policy rule will form one or more sets of simple ANDed conditions.

The DNF policy structure can be represented by the tree structure below.

                   OR                   /    |    \ AND   AND    AND ....... / | \    |    /   \           C1 C2 C3  C4   C5   C6

Using the example policy rule from above, this converts into the following simple conditions:
 * C1: role = "admin"
 * C2: is_admin = TRUE
 * C3: service = "identity"
 * C4: action = "create_region"

The policy rule in DNF forms two sets of simple conditions, namely:

(C3 and C4 and C1) or (C3 and C4 and C2)

Database Schema
The following figure presents the database schema to store security policies in Openstack.



Each of these tables are detailed below.

Policy
Represents the Openstack Policy. Each entry in this table can represent one or multiple Openstack Policy files.

The id is the unique key for this policy.

The description field is optional, and describes the meaning of the Policy.

AND Rule
Each "AND rule" comprises the following.

The id is the unique key for this AND rule

policy_id is the ID of the policy that this rule is for. All AND rules in a policy are implicitly combined with the OR operator.

description is the optional description of this AND rule.

Enabled is a Boolean saying whether this AND rule is active or not. The default value is True. By setting the value to False this allows us to remove this whole set of ANDed conditions from the policy.

Condition
A condition represents the basic element of a policy. A condition comprises an attribute, an operator and a value. The policy engine will verify if the value of the "attribute" from the request matches with the "value" field in the condition, according to the operator.

A value can be a literal (e.g. a string or a number) or a variable (e.g., %(user_id)s) which will be evaluated by the Policy Engine.

Attributes can be of different types, for instance:

Services are represented in the policy.json file by "service:action" entries e.g. "identity:create_protocol", where "identity" is the service.
 * Service

Actions are represented in the policy.json file by "service:action" entries e.g. "identity:create_protocol", where "create_protocol" is the service.
 * Action

Roles in Openstack represent Subjects. A user in a domain or project can be directly assigned to roles, or can be part of a group which is assigned to a role.
 * Role

Despite the fact that Openstack uses an RBAC engine, it allows other attributes from the subject to be verified in its policies, for example, user_id and project_id.
 * Other types

And_Rule_has_Condition
Conditions are combined in the And_Rule_has_Condition table. This table represent a many to many relationship between conditions and AND rules.

This allows a single condition to be part of multiple "AND Rules".

Supported Operations
Policies stored in the database will support CRUD operations and also complex SQL queries.

For instance, it will be possible to find out what are the necessary conditions to perform a given action on a service, or what actions a particular role can perform.

Besides the generic SQL queries, two other operations will also be supported:
 * Import policy.json file into the database: This will parse the json file and convert it into the relational tables. During this operation, any duplicate rules will be removed.
 * Export policies from database to new policy.json files. These new files will reflect the managed set of rules. It's important to note that, since the rules are decomposed to and stored in DNF, and not stored in their original syntax, the new generated policy files will be semantically equivalent to their original, but can be syntactically different.

Note. By supporting different import and export utilities, we will be able to support the storage and retrieval of policies from other (non-OpenStack) cloud systems.

Introduction
In order to show how the Database Scheme is applicable, we present a database representation for the following lines from Keystone's policy.json.

"admin_required": "role:admin or is_admin:1", "service_or_admin": "rule:admin_required or role:service", "owner" : "user_id:%(user_id)s", "admin_or_owner": "rule:admin_required or rule:owner",

"identity:list_regions": "", "identity:create_region": "rule:admin_required", "identity:ec2_create_credential": "rule:admin_or_owner", "identity:create_trust": "user_id:%(trust.trustor_user_id)s", "identity:ec2_delete_credential": "rule:admin_required or (rule:owner and user_id:%(target.credential.user_id)s)",

Subject Rules
The 4 first lines constitute a set of subject conditions combined with AND or OR operators, which define who are the subjects in the following policy rules.

Subject rules are bound to labels, which are "shortcuts" to them. In order to use these subject rules, the labels must be prefixed by the reserved keyword "rule".

However, these labels are not stored in the DNF policy structure, because they cover different OR and AND branches of the policy.

Subject rules comprise:

" " : " "

where:
 * is the name that represents the subject rule
 * can be conditions on the attributes of the subject, or "rule:label" or a mixture of both.

Policy Rules
The last 5 lines comprise policy rules which are represented by the following syntax:

" : " : " "

where:
 * represents the service API (eg. identity, compute)
 * represents the requested action
 * can be conditions on the attributes of the subject, or "rule:label" or a mixture of both.

Converting Subject Rules and Policy Rules into Conditions and AND Rules
Each line of the example above will be converted into one or more Conditions.

Policy rules will be converted into one or more AND rules, depending upon the number of different subject rules in the policy rule. A complete policy rule can easily be recovered from the database by searching for all the AND rules that have the same "service" and "action" conditions.

Conventions: Terms in bold represent items stored in the database (conditions and "and rules"). Terms in italic represent items temporarily stored in memory (subject rules and policy rules) whilst the json policy file is imported. Conditions are represented as:     C: =  And Rules are represented as:      A:  Subject rules are represented as:  S("label"):  Policy rules are represented as:   R("service:action"): 

LINE 1: "admin_required": "role:admin or is_admin:1",


 * C1: role = "admin"
 * C2: is_admin = True
 * S("admin_required"): C1 or C2

LINE 2: "service_or_admin": "rule:admin_required or role:service",


 * C3: role = "service"
 * S("service_or_admin"): C1 or C2 or C3

LINE 3: "owner" : "user_id:%(user_id)s",


 * C4: user_id = "%(user_id)s"
 * S("owner"): C4

LINE 4: "admin_or_owner": "rule:admin_required or rule:owner",
 * S("admin_or_owner"): C1 or C2 or C4

LINE 5: "identity:list_regions": "",


 * C5: service = "identity"
 * C6: action = "list_regions"
 * R("identity:list_regions"): C5 and C6
 * A1: C5 and C6

LINE 6: "identity:create_region": "rule:admin_required",
 * C7: action = "create_region"
 * R("identity:create_region"): C5 and C7 and S("admin_required")
 * R("identity:create_region"): C5 and C7 and (C1 or C2)
 * R("identity:create_region"): (C5 and C7 and C1) or (C5 and C7 and C2)
 * A2: C5 and C7 and C1
 * A3: C5 and C7 and C2

LINE 7: "identity:ec2_create_credential": "rule:admin_or_owner",
 * C8: action = "ec2_create_credential"
 * R("identity:ec2_create_credential"): C5 and C8 and S("rule:admin_or_owner")
 * R("identity:ec2_create_credential"): C5 and C8 and (C1 or C2 or C4)
 * R("identity:ec2_create_credential"): (C5 and C8 and C1) or (C5 and C8 and C2) or (C5 and C8 and C4)
 * A4: C5 and C8 and C1
 * A5: C5 and C8 and C2
 * A6: C5 and C8 and C4

LINE 8: "identity:create_trust": "user_id:%(trust.trustor_user_id)s",
 * C9: action = "create_trust"
 * Ca: user_id = "%(trust.trustor_user_id)s"
 * R("identity:create_trust"): C5 and C9 and CA
 * A7: C5 and C9 and Ca

LINE 9: "identity:ec2_delete_credential": "rule:admin_required or (rule:owner and user_id:%(target.credential.user_id)s)",
 * Cb: action = "ec2_delete_credential"
 * Cc: user_id = "%(target.credential.user_id)s"
 * R("identity:ec2_delete_credential"): C5 and Cb and (S("admin_required") or (S("owner") and Cc))
 * R("identity:ec2_delete_credential"): C5 and Cb and (C1 or C2 or (C4 and Cc))
 * R("identity:ec2_delete_credential"): (C5 and Cb and C1) or (C5 and Cb and C2) or (C5 and Cb and C4 and Cc)
 * A8: C5 and Cb and C1
 * A9: C5 and Cb and C2
 * Aa: C5 and Cb and C4 and Cc

Tables
All tables in this section are populated according to the example above.