Permissions in Boundary
Overview
Boundary’s permissions model is a composable, RBAC, allow-only model that attempts to marry flexibility with usability. This page discusses the permission model’s fundamental concepts, provides examples of the specific forms of allowed grants, and contains a table that acts as an easy cheat sheet to help those new to its grant syntax with crafting roles.
Boundary’s domain model is based on resource types. These can be implemented directly, such as with targets, or they can be abstract types that are implemented by concrete types within the system. As an example of the latter, a host catalog is an abstract type and a Static host catalog is a concrete type.
From a permissions standpoint, all actions take place against directly implemented or abstract types. There may be actions that are only implemented by some concrete types (e.g., not all auth-method
implementations will support a change-password
action), but the permissions model still defines these capabilities at the abstract type level. This helps keep the overall system relatively simple and predictable.
At a very high level, permissions within Boundary are declared via grant strings and mapped to users via roles.
Grant Strings
A grant string has a form similar to:
id=<id>;type=<type>;actions=<action list>;output_fields=<fields list>
Each grant string is a mapping that describes a resource or set of resources and the permissions that should be granted on them.
There are currently two types of selectors:
An
id
field that indicates a specific resource or a wildcard to match allA
type
field that indicates a specific resource type or a wildcard to match all; this might also be used to grant permissions on collections of resources
Selectors are used to indicate which resources on which the grant should apply, using specific IDs or wildcard IDs and type selectors. (The acceptable grant string formats are detailed later on this page.)
Additionally, there are two types of assigned permissions:
An
actions
field indicating which actions to allow the client to perform on the resources matched byid
andtype
An
output_fields
field indicating which top-level fields to return in the response (0.2.1+)
Grant strings can be supplied via a human-friendly string syntax or via JSON.
Roles
Roles map grant strings to principals, currently users and groups. Every role assigns grants within a specific scope: either the scope in which the role exists, or a scope that is a child of the scope in which the role exists, controlled by the role’s “grant scope ID” value
When a request is made, the scope in which to discover grants is either provided by the client (if against a resource collection itself) or is looked up using the resource’s ID. This scope ID, along with the user’s ID and the IDs of the groups the user belongs to, controls which roles are fetched to provide grants for the request.
A role provides grants for a request if the grant scope ID matches the request’s scope ID and one or more of the following are true:
The user’s ID is contained in the principal IDs set on the role
A group the user belongs to is contained in the principal IDs set on the role
The user is logged in and the
u_auth
user is contained in the principal IDs set on the roleThe role contains the
u_anon
user in the in the principal IDs set on the role
Roles are composable; a user’s final set of grants will be composed of grants that originate from all matching roles.a
Assignable Permissions
Resources identified by the ID/Type selectors have permissions granted to the user in the form of actions and visibility (via output_fields
). Each of these is detailed in the subsections below.
Actions
Actions convey the ability to perform some action against a resource or collection. Many of these are common CRUD actions (create
, read
, update
, delete
) but many resources also specify actions specific to their type; for instance, a target
has an authorize-session
action to allow you to request making a connection to that target, and auth-method
resources have an authenticate
action to allow you to authenticate to Boundary. For the most part these are straightforward, however there are a couple of special cases to know.
Subactions
Starting in Boundary 0.1.6, some subactions are supported. These actions have a format top_level_action:subaction
, such as read:self
. Being granted the top level action infers being granted all subactions. Thus, if a grant conveys read
, it also matches the API actions read
and read:self
. However, if a grant conveys read:self
, it will match the API action read:self
but will not match read
.
The no-op
Action
Starting in Boundary 0.2.1 there is an action that can be granted called no-op
. As might be apparent, no-op
is not used for any real action in Boundary; the purpose of this action is for listing visibility. Boundary only shows resources in the output of a list
action for which a user has at least one granted action. Thus, without no-op
, in order for a resource to be visible in a list
action, a different action such as read
would have to be granted to a user. This could result in exposing more information than desired, especially in the case of listing scopes and authentication methods so that users can perform initial authentication to Boundary (that is, granting access to u_anon
).
By granting the no-op
action to users, they can see the resources in the output of a list command without needing other capability grants as well.
Output Fields
Starting in Boundary 0.2.1, grant strings can contain an output_fields
field. This allows fine-grained control over visibility of data returned to users.
In many cases, output_fields
will not need to be set directly. However, an example in the form of some history helps provide some context as to when this might be useful.
In Boundary 0.2.0, we restricted the set of fields returned for some list
calls (those on the scopes
and auth-methods
collections) to a small set if the user was the anonymous user u_anon
. Although default behavior in Boundary was to display all resource fields when listing, because the default was also to allow anonymous access to perform list
on scopes and auth methods (in order to discover auth methods and then authenticate to them), returning all configuration information for all scopes and auth methods felt like it was publicly disclosing more information to users than might be desired.
At the same time, this was not an ideal solution for two reasons:
There is no one-size-fits-all security policy, and what we thought were reasonable defaults may not work in all situations
It made the scopes and auth methods listing behavior work differently from any other
list
action, which is not ideal
As a result, we decided to approach this problem the same way we normally try to within Boundary: set resonable defaults, but give the administrator ultimate control.
The resulting behavior is as follows: output_fields
references the field names of the JSON object response, composes just like other parts of the RBAC model, and are action-specific. That is, a grant like:
id=*;type=auth-methods;actions=list,no-op;output_fields=scope_id,name,description
will, if all by itself, result in those three identified output fields applying to list
(and no-op
) actions for all auth methods in the scope. Thus when performing a list
, each item returned will have only those three fields populated. Any other actions (like read
or update
) are unaffected and will use defaults.
A grant like:
id=*;type=auth-methods;output_fields=id
will, if all by itself, result in any action against auth methods in the scope having only id
returned.
However, if both of the above grants are included, since grants are composable the final set of output fields will be id,scope_id,name,description
for list
(and no-op
) actions, and id
for all other actions.
If, after the grants are composed for a given request, none of the grants applicable to the resource/action contain output_fields
definitions, the defaults are used. These defaults are implemented using internal values for output_fields
and vary based on whether the user is the anonymous user:
If the user is the anonymous user, a useful but restricted set of fields is returned. This includes
id
,scope_id
,scope
,name
,description
, and a few more.If the user is any authenticated user, the full set of fields is returned
To think about it a different way, empty output_fields
, after grant composition, is equivalent to using Boundary’s default; however the moment that grants start specifying output fields, it is composed from an empty set and thus nothing is contained unless explicitly specified. (An actual empty set is not currently supported, as we don’t perform validation on the values given. However, this means setting output_fields=none
is functionally equivalent!)
Output Fields and Monitoring
As another example, it’s possible to get creative and use output_fields
to allow services to keep tabs on various aspects of the system for monitoring purposes without needing to provision Boundary tokens to those services. For instance, by allowing access to list users to u_anon
but restrict the set of fields to id
only, a monitoring system could watch and alert if more users than expected are showing up in the system, while the IDs themselves are not really meaningful to any other caller that accesses the same endpoint.
This will be especially useful once we introduce the ability to have CIDR restrictions against roles and/or grants, which are planned but not currently scheduled, as you can only have these grants apply to specific internal services, along with restricting the data that is returned for those services that do match.
Permission Grant Formats
Because of the aforementioned properties of the permissions model, grants are relatively simple. All grants take one of four forms. These examples use the canonical string syntax; the JSON equivalents are simply an object with a string id
value, a string type
value, a string array actions
value, and a string array output_fields
value.
output_fields
is omitted in most example below for brevity but are valid in all of them. It is also valid in each case to omit actions
and specify only output_fields
.
ID Only
This is the simplest form: for a given specific resource, allow these actions. Example:
id=hsst_1234567890;actions=read,update
This grants read
and update
actions to that single resource. It is invalid to specify create
or list
as actions in this format, as this format explicitly identifies a resource, whereas those actions operate exclusively on collections.
Type Only
For a given type, allow these actions. Example:
type=host-catalog;actions=create,list
Because type specifies only a collection as opposed to specific resources within that collection, only collection actions are allowed in this format. Currently, this is create
and list
.
There is one additional restriction: this is only valid against “top-level” resource types, which currently are:
- Auth Methods
- Auth Tokens
- Groups
- Host Catalogs
- Roles
- Scopes
- Sessions
- Targets
- Users
The reason for this is that other types of resources are contained within one of these resource types; for instance, accounts are instantiated within an auth method. To specify actions against those, you must also specify to which specific containing resource you want the grants to apply. This can be done with the pinned format shown below.
Pinned ID
This form “pins” actions to a non-top-level type within a specific ID. It’s easiest to explain with an example:
id=hcst_1234567890;type=host-set;actions=create,read,update
In this example, the user is able to create, read, or update host sets within the scope, but only the host sets belonging to host catalog hcst_1234567890. Pinning is essentially a way to use top-level resources to create mini permission boundaries for their subordinate resources.
Wildcard ID
Various wildcard possibilities are allowed:
Wildcard ID
When just the ID is *
, it matches all IDs of the given type. This can be used with both top-level resource types and not. Example:
id=*;type=host-set;actions=create,read,update,set-hosts
Wildcard Type
For non-top-level resources with pinned IDs, the type
can be a wildcard:
id=hcst_1234567890;type=*;actions=create,read,update
This would allow create
, read
, and update
actions for all types of subordinate resources (in this case host sets and hosts) underneath the host catalog with ID hcst_1234567890
.
Wildcard ID and Type
If ID and type are both a wildcard, the grant is essentially a catch-all that will match any resource of any type within the scope and allow the given actions.
id=*;type=*;actions=read,list
Wildcard ID, Type, and Actions
Finally, ID, type, and actions can all be wildcards:
id=*;type=*;actions=*
Such a grant is essentially a full administrator grant for a scope.
Templates
A few template possibilities exist, which will at grant evaluation time substitute the given value into the ID field of the grant string:
{{account.id}}: The substituted value is the account ID associated with the token used to perform the action. As an example,
id={{account.id}};actions=read,change-password"
is one of Boundary’s default grants to allow users that have authenticated with the Password auth method to change their own password.{{user.id}}: The substituted value is the user ID associated with the token used to perform the action.
Resource Table
The following table works as a quick cheat-sheet to help you manage your permissions. Note that it’s not exhaustive; for brevity it does not show wildcard or templated grant strings.
Additionally, this does not include available output fields; see the service documentation for guidance.
Resource Type | Applicable Scopes | API Endpoint | Parameters into Permissions Engine | Available Actions / Examples |
---|---|---|---|---|
Account |
| /accounts |
|
|
/accounts/<id> |
|
| ||
Auth Method |
| /auth-methods |
|
|
/auth-methods/<id> |
|
| ||
Auth Token |
| /auth-tokens |
|
|
/auth-tokens/<id> |
|
| ||
Group |
| /groups |
|
|
/groups/<id> |
|
| ||
Host |
| /hosts |
|
|
/hosts/<id> |
|
| ||
Host Catalog |
| /host-catalogs |
|
|
/host-catalogs/<id> |
|
| ||
Host Set |
| /host-sets |
|
|
/host-sets/<id> |
|
| ||
Managed Group |
| /managed-groups |
|
|
/managed-groups/<id> |
|
| ||
Role |
| /roles |
|
|
/roles/<id> |
|
| ||
Scope |
| /scopes |
|
|
/scopes/<id> |
|
| ||
Session |
| /sessions |
|
|
/session/<id> |
|
| ||
Target |
| /targets |
|
|
/targets/<id> |
|
| ||
User |
| /users |
|
|
/users/<id> |
|
|