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 all

  • A 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 by id and type

  • 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 role

  • The 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:

  1. There is no one-size-fits-all security policy, and what we thought were reasonable defaults may not work in all situations

  2. 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 TypeApplicable ScopesAPI EndpointParameters into Permissions EngineAvailable Actions / Examples
Account
  • Global
  • Org
/accounts
  • Type
    • account
  • create: Create an account
    • type=<type>;actions=create
  • list: List accounts
    • type=<type>;actions=list
/accounts/<id>
  • ID
    • <id>
  • Pin
    • <auth-method-id>
  • Type
    • account
  • read: Read an account
    • id=<id>;actions=read
    • id=<pin>;type=<type>;actions=read
  • update: Update an account
    • id=<id>;actions=update
    • id=<pin>;type=<type>;actions=update
  • delete: Delete an account
    • id=<id>;actions=delete
    • id=<pin>;type=<type>;actions=delete
  • set-password: Set a password on an account, without requiring the current password
    • id=<id>;actions=set-password
    • id=<pin>;type=<type>;actions=set-password
  • change-password: Change a password on an account given the current password
    • id=<id>;actions=change-password
    • id=<pin>;type=<type>;actions=change-password
Auth Method
  • Global
  • Org
/auth-methods
  • Type
    • auth-method
  • create: Create an auth method
    • type=<type>;actions=create
  • list: List auth methods
    • type=<type>;actions=list
/auth-methods/<id>
  • ID
    • <id>
  • Type
    • auth-method
  • read: Read an auth method
    • id=<id>;actions=read
  • update: Update an auth method
    • id=<id>;actions=update
  • delete: Delete an auth method
    • id=<id>;actions=delete
  • authenticate: Authenticate to an auth method
    • id=<id>;actions=authenticate
Auth Token
  • Global
  • Org
/auth-tokens
  • Type
    • auth-token
  • list: List auth tokens
    • type=<type>;actions=list
/auth-tokens/<id>
  • ID
    • <id>
  • Type
    • auth-token
  • read: Read an auth token
    • id=<id>;actions=read
  • delete: Delete an auth token
    • id=<id>;actions=delete
Group
  • Global
  • Org
  • Project
/groups
  • Type
    • group
  • create: Create a group
    • type=<type>;actions=create
  • list: List groups
    • type=<type>;actions=list
/groups/<id>
  • ID
    • <id>
  • Type
    • group
  • read: Read a group
    • id=<id>;actions=read
  • update: Update a group
    • id=<id>;actions=update
  • delete: Delete a group
    • id=<id>;actions=delete
  • add-members: Add members to a group
    • id=<id>;actions=add-members
  • set-members: Set the full set of members on a group
    • id=<id>;actions=set-members
  • remove-members: Remove members from a group
    • id=<id>;actions=remove-members
Host
  • Project
/hosts
  • Type
    • host
  • create: Create a host
    • type=<type>;actions=create
  • list: List hosts
    • type=<type>;actions=list
/hosts/<id>
  • ID
    • <id>
  • Pin
    • <host-catalog-id>
  • Type
    • host
  • read: Read a host
    • id=<id>;actions=read
    • id=<pin>;type=<type>;actions=read
  • update: Update a host
    • id=<id>;actions=update
    • id=<pin>;type=<type>;actions=update
  • delete: Delete a host
    • id=<id>;actions=delete
    • id=<pin>;type=<type>;actions=delete
Host Catalog
  • Project
/host-catalogs
  • Type
    • host-catalog
  • create: Create a host catalog
    • type=<type>;actions=create
  • list: List host catalogs
    • type=<type>;actions=list
/host-catalogs/<id>
  • ID
    • <id>
  • Type
    • host-catalog
  • read: Read a host catalog
    • id=<id>;actions=read
  • update: Update a host catalog
    • id=<id>;actions=update
  • delete: Delete a host catalog
    • id=<id>;actions=delete
Host Set
  • Project
/host-sets
  • Type
    • host-set
  • create: Create a host set
    • type=<type>;actions=create
  • list: List host sets
    • type=<type>;actions=list
/host-sets/<id>
  • ID
    • <id>
  • Pin
    • <host-catalog-id>
  • Type
    • host-set
  • read: Read a host set
    • id=<id>;actions=read
    • id=<pin>;type=<type>;actions=read
  • update: Update a host set
    • id=<id>;actions=update
    • id=<pin>;type=<type>;actions=update
  • delete: Delete a host set
    • id=<id>;actions=delete
    • id=<pin>;type=<type>;actions=delete
  • add-hosts: Add hosts to a host-set
    • id=<id>;actions=add-hosts
    • id=<pin>;type=<type>;actions=add-hosts
  • set-hosts: Set the full set of hosts on a host set
    • id=<id>;actions=set-hosts
    • id=<pin>;type=<type>;actions=set-hosts
  • remove-hosts: Remove hosts from a host set
    • id=<id>;actions=remove-hosts
    • id=<pin>;type=<type>;actions=remove-hosts
Managed Group
  • Global
  • Org
/managed-groups
  • Type
    • managed-group
  • create: Create a managed group
    • type=<type>;actions=create
  • list: List managed groups
    • type=<type>;actions=list
/managed-groups/<id>
  • ID
    • <id>
  • Pin
    • <auth-method-id>
  • Type
    • managed-group
  • read: Read a managed group
    • id=<id>;actions=read
    • id=<pin>;type=<type>;actions=read
  • update: Update a managed group
    • id=<id>;actions=update
    • id=<pin>;type=<type>;actions=update
  • delete: Delete a managed group
    • id=<id>;actions=delete
    • id=<pin>;type=<type>;actions=delete
Role
  • Global
  • Org
  • Project
/roles
  • Type
    • role
  • create: Create a role
    • type=<type>;actions=create
  • list: List roles
    • type=<type>;actions=list
/roles/<id>
  • ID
    • <id>
  • Type
    • role
  • read: Read a role
    • id=<id>;actions=read
  • update: Update a role
    • id=<id>;actions=update
  • delete: Delete a role
    • id=<id>;actions=delete
  • add-principals: Add principals to a role
    • id=<id>;actions=add-principals
  • set-principals: Set the full set of principals on a role
    • id=<id>;actions=set-principals
  • remove-principals: Remove principals from a role
    • id=<id>;actions=remove-principals
  • add-grants: Add grants to a role
    • id=<id>;actions=add-grants
  • set-grants: Set the full set of grants on a role
    • id=<id>;actions=set-grants
  • remove-grants: Remove grants from a role
    • id=<id>;actions=remove-grants
Scope
  • Global
  • Org
/scopes
  • Type
    • scope
  • create: Create a scope
    • type=<type>;actions=create
  • list: List scopes
    • type=<type>;actions=list
/scopes/<id>
  • ID
    • <id>
  • Type
    • scope
  • read: Read a scope
    • id=<id>;actions=read
  • update: Update a scope
    • id=<id>;actions=update
  • delete: Delete a scope
    • id=<id>;actions=delete
Session
  • Project
/sessions
  • Type
    • session
  • list: List sessions
    • type=<type>;actions=list
/session/<id>
  • ID
    • <id>
  • Type
    • session
  • read: Read a session
    • id=<id>;actions=read
  • cancel: Cancel a session
    • id=<id>;actions=cancel
  • read:self: Read a session, which must be associated with the calling user
    • id=;type=session;actions=read:self
  • cancel:self: Cancel a session, which must be associated with the calling user
    • id=;type=session;actions=cancel:self
Target
  • Project
/targets
  • Type
    • target
  • create: Create a target
    • type=<type>;actions=create
  • list: List targets
    • type=<type>;actions=list
/targets/<id>
  • ID
    • <id>
  • Type
    • target
  • read: Read a target
    • id=<id>;actions=read
  • update: Update a target
    • id=<id>;actions=update
  • delete: Delete a target
    • id=<id>;actions=delete
  • add-host-sets: Add host sets to a target
    • id=<id>;actions=add-host-sets
  • set-host-sets: Set the full set of host sets on a target
    • id=<id>;actions=set-host-sets
  • remove-host-sets: Remove host sets from a target
    • id=<id>;actions=remove-host-sets
  • authorize-session: Authorize a session via the target
    • id=<id>;actions=authorize-session
User
  • Global
  • Org
/users
  • Type
    • user
  • create: Create a user
    • type=<type>;actions=create
  • list: List users
    • type=<type>;actions=list
/users/<id>
  • ID
    • <id>
  • Type
    • user
  • read: Read a user
    • id=<id>;actions=read
  • update: Update a user
    • id=<id>;actions=update
  • delete: Delete a user
    • id=<id>;actions=delete
  • add-accounts: Add accounts to a user
    • id=<id>;actions=add-accounts
  • set-accounts: Set the full set of accounts on a user
    • id=<id>;actions=set-accounts
  • remove-accounts: Remove accounts from a user
    • id=<id>;actions=remove-accounts