RoleBindings
A RoleBinding is where access actually starts. It takes a Role and gives it to someone: a person, a team, or a playbook or notification that acts on its own.
Think of the Role as a job description and the RoleBinding as handing that job to people. You write a Role such as "staging operator" once, then create a RoleBinding for each team that does that job.
A first RoleBinding
This is the RoleBinding Mission Control ships to make every member of the admins team an admin:
admins-binding.yamlapiVersion: mission-control.flanksource.com/v1
kind: RoleBinding
metadata:
name: admins
namespace: mission-control-builtin
spec:
description: Members of the admins team are admins
role: admin
subjects:
teams:
- admins
Every installation has this binding. You can't create your own bindings in mission-control-builtin, but it shows the two parts every RoleBinding has:
rolenames the Role to give. Here it's a bare name,admin, because the Role is in the same namespace as the binding. A Role in another namespace is written asnamespace/name, such asmission-control-builtin/viewer.subjectssays who gets it.
A RoleBinding can also have a description, a constraint and deny, which are covered below.
Who you can give a Role to
People
List people by email under subjects.people. This binding gives John read access to every config:
john-catalog-read.yamlapiVersion: mission-control.flanksource.com/v1
kind: Scope
metadata:
name: john-catalog-read
namespace: default
spec:
targets:
- config:
name: "*"
---
apiVersion: mission-control.flanksource.com/v1
kind: Role
metadata:
name: john-catalog-read
namespace: default
spec:
description: Grant John read permissions to catalogs
rules:
- name: read-configs
action: read
resource:
scopeRef: john-catalog-read
---
apiVersion: mission-control.flanksource.com/v1
kind: RoleBinding
metadata:
name: john-catalog-read
namespace: default
spec:
role: john-catalog-read
subjects:
people:
- john@doe.com
Use * as the only entry to give a Role to everyone who signs in, which is the usual way to give everyone the shipped viewer Role.
Users of an external identity provider are named as <provider>:<username>, and * doesn't include them. To give a Role to every user of one provider, use <provider>:*.
Teams
List team names under subjects.teams. Teams are created and managed from Settings → Teams. Everyone in the team gets the Role, so access follows team membership: add someone to the team and they get it, remove them and they lose it.
Binding Roles to teams, rather than to individual people, is usually easier to manage. You can also keep team membership in sync with your login provider's groups. See Team Mapping.
Playbooks, notifications and other automation
Starting a playbook run is checked as the person who starts it: they need playbook:run on the playbook, and read on the config it runs on. Everything the run then does, such as reading configs, using connections or running other playbooks, is checked as the playbook. A notification works the same way when it's sent. So when a playbook needs to call an API through a connection, the playbook itself needs a RoleBinding:
playbook-connection.yamlapiVersion: mission-control.flanksource.com/v1
kind: Scope
metadata:
name: allow-playbook-connection
namespace: default
spec:
targets:
- connection:
name: httpbin
namespace: mc
---
apiVersion: mission-control.flanksource.com/v1
kind: Role
metadata:
name: allow-playbook-connection
namespace: default
spec:
description: allow playbook mc/http-authorized to use connection mc/httpbin
rules:
- name: use-connections
action: connection:use
resource:
scopeRef: allow-playbook-connection
---
apiVersion: mission-control.flanksource.com/v1
kind: RoleBinding
metadata:
name: allow-playbook-connection
namespace: default
spec:
role: allow-playbook-connection
subjects:
playbooks:
- name: http-authorized
namespace: mc
Here the subject is the http-authorized playbook in the mc namespace, and the Role lets it use the httpbin connection. Whoever runs the playbook doesn't need access to the connection themselves.
You can select playbooks, notifications, scrapers, canaries, topologies and plugins this way, each by namespace and name. Use name: "*" on its own to match every resource of that kind in every namespace, or set only namespace to match everything in that namespace. Here plugins means Mission Control plugins that add operations to the catalog, not config scraper plugins.
Permissions for automation
| When this happens | Bind a Role to | With |
|---|---|---|
| A playbook uses a connection | The playbook | connection:use on the connection |
| A playbook reads configs, for example in a template | The playbook | read on the configs |
| A playbook runs another playbook | The first playbook | playbook:run on the second playbook, and read on its target |
| A notification sends through a connection | The notification | connection:use on the connection |
| A notification runs a playbook | The notification | playbook:run on the playbook, and read on the config it runs on |
| A scraper, canary or topology uses a connection | The scraper, canary or topology | connection:use on the connection |
| A plugin looks up a connection | The plugin | read or connection:use on the connection |
See also Who a run acts as and Notification permissions.
A binding by namespace also covers resources created there later. Anyone who can create a playbook in that namespace gets a playbook with the binding's access. For sensitive Roles, such as using a connection that pages people, bind each playbook or notification by its exact namespace and name.
Limiting a Role to some resources
Sometimes several teams do the same job on different resources. Team A operates tenant A's configs, and team B operates tenant B's. Instead of writing a Role per team, write one Role and add a constraint to each team's RoleBinding.
A constraint names a Scope, and narrows every rule of the Role to resources that are in both the rule's Scope and the constraint's Scope:
| Constraint field | Narrows |
|---|---|
constraint.resource.scopeRef | What the rules act on, such as the configs that may be read, or the playbooks that may be run |
constraint.target.scopeRef | What playbooks may be run against |
For example, bind a shared production-operator Role to team A with both fields set to a tenant-a Scope. Team A can then read, and run playbooks on, only the production configs that are also in tenant-a. Team B's binding does the same with tenant-b.
constraint.resource also narrows the playbooks that playbook:run rules allow. So tenant-a must select tenant A's playbooks as well as its configs, or team A can't run any playbook. One Scope can select several types, as described in Combining targets.
Some tips for constraints:
- Set both
resourceandtargetwhen the Role runs playbooks. Running a playbook on a config also needsreadon that config, and areadrule is only narrowed byresource. - Check
AllRulesApply. A constraint can't narrow every kind of rule. For example, creating and deleting are always granted for a whole type. Those rules are left out of the binding rather than granted in full, and the binding'sAllRulesApplycondition, whichkubectl describeshows, names each one. If a team gets less than you expected, look there first. - To narrow rules differently, use separate Roles. A constraint applies to the whole Role.
Refusing a Role
Set deny: true on a RoleBinding to refuse its Role instead of granting it. A deny wins over everything else, including admin. For example, take a read-secrets Role with a read rule on a Scope of configs tagged secret=true. A deny binding of that Role to the contractors team keeps contractors from reading those configs, even if someone later binds them to a broader Role by mistake.
Use deny bindings as a safety net, not as the main way of controlling access. Because nothing is allowed by default, the simplest way to keep something from someone is not to grant it. Keep these limits in mind:
- A broken deny binding denies nothing. If its Role or Scope is missing, the binding shows
Ready=Falseand has no effect. Check its status after you create it. - A deny only blocks actions, not the data itself. Denying
readon some configs doesn't remove them from a view that shows them, or stop a playbook the person may run from reading them. Deny the view as well. - A deny can't lock admins out. A deny binding is ignored for actions on RoleBindings, so admins can always remove it.
Checking a RoleBinding
Every RoleBinding has a Ready status. Ready=False means it isn't granting anything yet, for example because its Role doesn't exist. It starts working as soon as the problem is fixed, so you can create a RoleBinding before its Role.
To see what someone can actually do, open the Subjects page under Settings. It explains which rules match a given person and action.
Anyone who can create or change RoleBindings can give anyone, themselves included, any Role. Only let people you'd trust as admins write Scopes, Roles and RoleBindings, in the UI or in Kubernetes. The namespace a RoleBinding lives in doesn't limit what it can grant.