Allow Node Owners to Administer Nodes¶
This spec describes an update that allows a node owner to administer their node through the Ironic API without being able to administer the entire node cluster. This is accomplished by exposing owner node data in the REST API for policy checks.
Ironic is not multi-tenant; anyone with API access to one node has access
to all nodes. While nodes have an
owner field, it is purely
informational and not tied into any form of access control.
Bringing full multi-tenant support to Ironic would allow us to address the following user stories:
As users of a shared data center, we would like to use Ironic to manage our hardware resources. Each work group should be able to control their own resources without having access to hardware resources owned by other groups.
Data center operator¶
As the operator of shared datacenter, I would like to delegate power control of baremetal hardware to the hardware owners using the Ironic API. By using the Ironic API instead of existing embedded management protocols (such as IPMI) I can maintain the simplicity of a shared management network while having granular control over who can access what hardware.
The Ironic API already has a way of controlling access to the REST API: a policy engine 0 that is used throughout the REST API. However, when the node controller uses the policy engine 1, it does so without passing in any information about the node being checked. Other services such as Nova 2 and Barbican 3 pass in additional information, and we propose doing the same. In summary, we would like to:
Assume that a node’s
ownerfield is set to the id of a Keystone project. We refer to this project as the “node owner”.
Update the node controller so that policy checks also pass in information about the node, including the
Update Ironic’s default generated policy file to include an
The remainder of the policy file would stay the same, meaning that there is no change to default API access.
Create documentation explaining how to update an Ironic policy file to give API access to owners. For example:
“baremetal:node:set_power_state”: “rule:is_admin or rule:is_node_owner”
Note that Nova grants API access in a similar manner 4.
This update is enough to control access for most API functions - except for list functions. For those, we propose the following:
Right now, the policy rule ‘baremetal:node:get’ is used for both
get_one. We can add two new policy rules:
baremetal:node:list_allfor retrieving all nodes, and
baremetal:node:listfor retrieving nodes whose
ownerfield matches the querying project.
Updating the REST API node list functions to first perform a policy check against
baremetal:node:list_all. If it passes, then we return all nodes; otherwise we perform a policy check against
baremetal:node:list. If that passes, then we return nodes filtered by the
ownerfield against the project specified in the request context. These list functions already have the option of filtering by
Updating the default generated policy file to set
rule:baremetal:node:getto ensure backwards compatibility.
Allocation API changes¶
Allocation objects represent a request to find and reserve a suitable node,
and as such should be available for non-administrator users. For this purpose
we will introduce an
owner field for allocations and start distinguishing
two types of allocations:
- unrestricted allocations
work the same as before. They can be created only by administrative users and can have any
ownerset. Creating them is governed by the existing policy
- restricted allocations
are created by non-administrative users and have
ownermatching the project ID of a creating user. Creating them will be governed by a new policy
The two policies will be exclusive and work as follows:
baremetal:allocation:createis checked. If it passes, then any value of
owneris accepted. If no
owneris provided, the field is left as
If the first check does not pass,
baremetal:allocation:create_restrictedis checked. The
ownerfield must match the project ID of the user or be empty (in which case it is set to the project ID of the user). HTTP Forbidden is returned if:
The project ID of the user cannot be determined (i.e. restricted allocations do not work in the standalone mode).
The requested non-empty owner does not match the project ID.
Otherwise, creating the allocation fails.
Finally, when processing an allocation with non-empty
owner, only nodes
with a matching
owner are considered.
One alternative is to perform these checks at the database API level. However that requires a new code pattern to be used on a large number of individual functions.
Data model impact¶
State Machine Impact¶
REST API impact¶
See details in “Proposed change” above.
Client (CLI) impact¶
“openstack baremetal” CLI¶
RPC API impact¶
Driver API impact¶
Nova driver impact¶
This change allows functionality to be exposed to additional users. However this access is blocked off by default; it requires an update to the Oslo policy file, and can be adjusted as an administrator desires.
Other end user impact¶
None: although node data needs to be retrieved in order to pass that information into a policy check, the controller functions already fetch that information. 6
Other deployer impact¶
Other contributors: * dtantsur - firstname.lastname@example.org
Update node controller.
We will add unit tests and Tempest tests.
Upgrades and Backwards Compatibility¶
Existing Ironic installations that use the
owner field for something other
than a project ID will be minimally affected for two reasons:
ownerfield does not match a project ID (or is None), the proposed update to the policy file will not give any non-admin access to the Ironic API.
This change has no end-user impact if the policy file is not updated. An existing install can simply choose not to update their policy file.
We will include additional documentation describing the possible
applications of using the
node_owner policy roles.
https://github.com/openstack/ironic/blob/master/ironic/api/controllers/v1/node.py#L225 Example of a current policy check. Note the use of
cdict; it is being passed in as both the
https://github.com/openstack/nova/blob/master/nova/api/openstack/compute/servers.py#L648-L652 Example of Nova creating a
https://github.com/openstack/barbican/blob/stable/rocky/barbican/api/controllers/__init__.py#L59-L72 Example of Barbican creating a
https://github.com/openstack/nova/blob/master/nova/policies/base.py#L27-L30 Example of Nova defaulting a rule that uses information from a