Chapter 5. Policies

Each OpenStack service contains resources that are managed by access policies. For example, a resource might include the following functions:

  • Permission to create and start instances
  • The ability to attach a volume to an instance

If you are a Red Hat OpenStack Platform (RHOSP) administrator, you can create custom policies to introduce new roles with varying levels of access, or to change the default behavior of existing roles.

Important

Customizing roles or policies is not supported by Red Hat. Syntax errors can lead to downtime, and misapplied authorization can negatively impact security or usability. If you feel you need custom policies in your production environment, contact Red Hat support for a support exception.

5.1. Reviewing existing policies

Policy files for services traditionally existed in the /etc/$service directory. For example, the full path of the policy.json file for Compute (nova) was /etc/nova/policy.json.

There are two important architectural changes that affect how you can find existing policies:

  • Red Hat OpenStack Platform is now containerized.

    • Policy files, if present, are in the traditional path if you view them from inside the service container:

      /etc/$service/policy.json

    • Policy files, if present, are in the following path if you view them from outside the service container:

      /var/lib/config-data/puppet-generated/$service/etc/$service/policy.json

  • Each service has default policies that are provided in code, with files that are available only if you created them manually, or if they are generated with oslopolicy tooling. To generate a policy file, use the oslopolicy-policy-generator from within a container, as in the following example:

    podman exec -it keystone oslopolicy-policy-generator --namespace keystone

By default, generated policies are pushed to stdout by osly.policy CLI tools.

5.2. Understanding service policies

Service policy file statements are either alias definitions or rules. Alias definitions exist at the top of the file. The following list contains an explanation of the alias definitions from the generated policy.json file for Compute (nova):

  • "context_is_admin": "role:admin"

    When rule:context_is_admin appears after a target, the policy checks that the user is operating with an administrative context before it allows that action.

  • "admin_or_owner": "is_admin:True or project_id:%(project_id)s"

    When admin_or_owner appears after a target, the policy checks that the user is either an admin, or that their project ID matches the owning project ID of the target object before it allows that action.

  • "admin_api": "is_admin:True

    When admin_api appears after a target, the policy checks that the user is an admin before it allows that action.

5.3. Policy syntax

Policy.json files support certain operators so that you can control the target scope of these settings. For example, the following keystone setting contains the rule that only admin users can create users:

"identity:create_user": "rule:admin_required"

The section to the left of the : character describes the privilege, and the section to the right defines who can use the privilege. You can also use operators to the right side to further control the scope:

  • ! - No user (including admin) can perform this action.
  • @ and "" - Any user can perform this action.
  • not, and, or - Standard operator functions are available.

For example, the following setting means that no users have permission to create new users:

"identity:create_user": "!"

5.4. Using policy files for access control

To override the default rules, edit the policy.json file for the appropriate OpenStack service. For example, the Compute service has a policy.json in the nova directory, which is the correct location of the file for containerized services when you view it from inside the container.

Note
  • You must thoroughly test changes to policy files in a staging environment before implementing them in production.
  • You must check that any changes to the access control policies do not unintentionally weaken the security of any resource. In addition, any changes to a policy.json file are effective immediately and do not require a service restart.

5.5. Example: Creating a power user role

To customize the permissions of a keystone role, update the policy.json file of a service. This means that you can more granularly define the permissions that you assign to a class of users. This example creates a power user role for your deployment with the following privileges:

  • Start an instance.
  • Stop an instance.
  • Manage the volumes that are attached to instances.

The intention of this role is to grant additional permissions to certain users, without the need to then grant admin access. To use these privileges, you must grant the following permissions to a custom role:

  • Start an instance: "os_compute_api:servers:start": "role:PowerUsers"
  • Stop an instance: "os_compute_api:servers:stop": "role:PowerUsers"
  • Configure an instance to use a particular volume: "os_compute_api:servers:create:attach_volume": "role:PowerUsers"
  • List the volumes that are attached to an instance: "os_compute_api:os-volumes-attachments:index": "role:PowerUsers"
  • Attach a volume: "os_compute_api:os-volumes-attachments:create": "role:PowerUsers"
  • View the details of an attached volume: "os_compute_api:os-volumes-attachments:show": "role:PowerUsers"
  • Change the volume that is attached to an instance: "os_compute_api:os-volumes-attachments:update": "role:PowerUsers"
  • Delete a volume that is attached to an instance: "os_compute_api:os-volumes-attachments:delete": "role:PowerUsers"
Note

When you modify the policy.json file, you override the default policy. As a result, members of PowerUsers are the only users that can perform these actions. To allow admin users to retain these permissions, you can create rules for admin_or_power_user. You can also use some basic conditional logic to define role:PowerUsers or role:Admin.

  1. Create the custom keystone role:

    $ openstack role create PowerUsers
    +-----------+----------------------------------+
    | Field     | Value                            |
    +-----------+----------------------------------+
    | domain_id | None                             |
    | id        | 7061a395af43455e9057ab631ad49449 |
    | name      | PowerUsers                      |
    +-----------+----------------------------------+
  2. Add an existing user to the role, and assign the role to a project:

    $ openstack role add --project [PROJECT_NAME] --user [USER_ID] [PowerUsers-ROLE_ID]
    Note

    A role assignment is paired exclusively with one project. This means that when you assign a role to a user, you also define the target project at the same time. If you want the user to receive the same role but for a different project, you must assign the role to them again separately but target the different project.

  3. View the default nova policy settings:

    $ oslopolicy-policy-generator --namespace nova
  4. Create custom permissions for the new PowerUsers role by adding the following entries to /var/lib/config-data/puppet-generated/nova/etc/nova/policy.json:

    Note

    Test your policy changes before deployment to verify that they work as you expect.

    {
    "os_compute_api:servers:start": "role:PowerUsers",
    "os_compute_api:servers:stop": "role:PowerUsers",
    "os_compute_api:servers:create:attach_volume": "role:PowerUsers",
    "os_compute_api:os-volumes-attachments:index": "role:PowerUsers",
    "os_compute_api:os-volumes-attachments:create": "role:PowerUsers",
    "os_compute_api:os-volumes-attachments:show": "role:PowerUsers",
    "os_compute_api:os-volumes-attachments:update": "role:PowerUsers",
    "os_compute_api:os-volumes-attachments:delete": "role:PowerUsers"
    }

    You implement the changes when you save this file and restart the nova container. Users that are added to the PowerUsers keystone role receive these privileges.

5.6. Example: Limiting access based on attributes

You can create policies that will restrict access to API calls based on the attributes of the user making that API call. For example, the following default rule states that keypair deletion is allowed if run from an administrative context, or if the user ID of the token matches the user ID associated with the target.

"os_compute_api:os-keypairs:delete": "rule:admin_api or user_id:%(user_id)s"

NOTE: * Newly implemented features are not guaranteed to be in every service with each release. Therefore, it is important to write rules using the conventions of the target service’s existing policies. For details on viewing these policies, see Reviewing existing policies. * All policies should be rigorously tested in a non-production environment for every version on which they will be deployed, as policies are not guaranteed for compatibility across releases.

Based on the above example, you can craft API rules to expand or restrict access to users based on whether or not they own a resource. Additionally, attributes can be combined with other restrictions to form rules as seen in the example below:

"admin_or_owner": "is_admin:True or project_id:%(project_id)s"

Considering the examples above, you can create a unique rule limited to administrators and users, and then use that rule to further restrict actions:

"admin_or_user": "is_admin:True or user_id:%(user_id)s"
"os_compute_api:os-instance-actions": "rule:admin_or_user"

For more information about the policy.json syntax options that are available, see Policy syntax.

5.7. Auditing your users and roles

You can use tools available in Red Hat OpenStack Platform to build a report of role assignments per user and associated privileges.

  1. Run the openstack role list command to see the roles currently in your environment:

    openstack role list -c Name -f value
    
    swiftoperator
    ResellerAdmin
    admin
    _member_
    heat_stack_user
  2. Run the openstack role assignment list command to list all users that are members of a particular role. For example, to see all users that have the admin role, run the following:

    $ openstack role assignment list --names --role admin
    +-------+------------------------------------+-------+-----------------+------------+--------+-----------+
    | Role  | User                               | Group | Project         | Domain     | System | Inherited |
    +-------+------------------------------------+-------+-----------------+------------+--------+-----------+
    | admin | heat-cfn@Default                   |       | service@Default |            |        | False     |
    | admin | placement@Default                  |       | service@Default |            |        | False     |
    | admin | neutron@Default                    |       | service@Default |            |        | False     |
    | admin | zaqar@Default                      |       | service@Default |            |        | False     |
    | admin | swift@Default                      |       | service@Default |            |        | False     |
    | admin | admin@Default                      |       | admin@Default   |            |        | False     |
    | admin | zaqar-websocket@Default            |       | service@Default |            |        | False     |
    | admin | heat@Default                       |       | service@Default |            |        | False     |
    | admin | ironic-inspector@Default           |       | service@Default |            |        | False     |
    | admin | nova@Default                       |       | service@Default |            |        | False     |
    | admin | ironic@Default                     |       | service@Default |            |        | False     |
    | admin | glance@Default                     |       | service@Default |            |        | False     |
    | admin | mistral@Default                    |       | service@Default |            |        | False     |
    | admin | heat_stack_domain_admin@heat_stack |       |                 | heat_stack |        | False     |
    | admin | admin@Default                      |       |                 |            | all    | False     |
    +-------+------------------------------------+-------+-----------------+------------+--------+-----------+
    Note

    You can use the -f {csv,json,table,value,yaml} parameter to export these results.

5.8. Auditing API access

You can audit the API calls a given role can access. Repeating this process for each role will result in a comprehensive report on the accessible APIs for each role. For the following steps you need:

  • An authentication file to source as a user in the target role.
  • An access token in JSON format.
  • A policy file for each service’s API you wish to audit.

Procedure

  1. Start by sourcing an authentication file of a user in the desired role.
  2. Capture a Keystone generated token and save it to a file. You can do this by running any openstack-cli command and using the --debug option, which prints the provided token to stdout. You can copy this token and save it to an access file. Use the following command to do this as a single step:

    openstack token issue --debug 2>&1 | egrep ^'{\"token\":' > access.file.json
  3. Create a policy file. This can be done on an overcloud node that hosts the containerized service of interest. The following example creates a policy file for the cinder service:

    ssh heat-admin@CONTROLLER-1 sudo podman exec cinder_api \
    oslopolicy-policy-generator \
    --config-file /etc/cinder/cinder.conf \
    --namespace cinder > cinderpolicy.json
  4. Using these files, you can now audit the role in question for access to cinder’s APIs:

    oslopolicy-checker --policy cinderpolicy.json --access access.file.json