Skip to content

Commit

Permalink
[v13] docs: document label expressions (#27878)
Browse files Browse the repository at this point in the history
* docs: document label expressions

This commit adds documentation for the label expressions feature
described in RFD 116.

* Apply suggestions from code review

Co-authored-by: Paul Gottschling <paul.gottschling@goteleport.com>

* use v6 role

Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>

* explain setting both <kind>_labels and <kind>_labels_expression

---------

Co-authored-by: Paul Gottschling <paul.gottschling@goteleport.com>
Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 15, 2023
1 parent f5f836e commit 8c0afc7
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 29 deletions.
90 changes: 76 additions & 14 deletions docs/pages/access-controls/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ controls (RBAC) in your Teleport cluster.
A Teleport `role` works by having two lists of rules: `allow` rules and `deny` rules.
When declaring access rules, keep in mind the following:

- Everything is denied by default.
- Nothing is allowed by default.
- Deny rules get evaluated first and take priority.

A rule consists of two parts: the resources and verbs. Here's an example of an
Expand All @@ -25,10 +25,10 @@ allow:
verbs: [list]
```

If this rule was declared in the `deny` section of a role definition, it
would prohibit users from getting a list of Trusted Clusters and
sessions. You can see all of the available resources and verbs under the `allow`
section in the example role configuration below.
If this rule was declared in the `deny` section of a role definition, it would
prohibit users from getting a list of active sessions. You can see all of the
available resources and verbs under the `allow` section in the example role
configuration below.

To manage cluster roles, a Teleport administrator can use the Web UI or the
command line using [tctl resource commands](../reference/resources.mdx).
Expand Down Expand Up @@ -151,15 +151,17 @@ Label | `v3`, `v4` and `v5` Default | `v6` Default

## RBAC for resources

A Teleport role defines which resources (e.g., applications, servers, and databases) a user can have access to.
This works by [labeling resources](../management/admin/labels.mdx) and listing
allow/deny labels in a role definition.
A Teleport role defines which resources (e.g., applications, servers, and
databases) a user can access.
This works by [labeling resources](../management/admin/labels.mdx) and
configuring allow/deny labels in a role definition.

Consider the following use case:

The infrastructure is split into staging/production environments using labels
like `environment=production` and `environment=staging`. You can create roles
that only have access to one environment. Let's say you create an intern role with the allow rule for label `environment=staging`.
like `environment=production` and `environment=staging`.
You can create roles that only have access to one environment.
Let's say you create an intern role with the allow rule for label `environment=staging`.

### Example

Expand Down Expand Up @@ -187,8 +189,8 @@ spec:
```

Teleport handles multiple label entries with logical "AND" operations.
As an example this entry would match to databases that have the `env: prod` label and a
`region` label of either `us-west-1` or `eu-central-1`:
As an example, this entry would match to databases that have the `env: prod`
label *and* a `region` label of either `us-west-1` or `eu-central-1`:

```yaml
db_labels:
Expand All @@ -200,12 +202,13 @@ As an example this entry would match to databases that have the `env: prod` labe
type="tip"
title="Dynamic RBAC"
>
Resource labels can be dynamic, i.e. determined at runtime by an output of an executable. In this case, you can implement "permissions follow workload"
Resource labels can be dynamic, i.e. determined at runtime by an output of an executable.
In this case, you can implement "permissions follow workload"
policies (eg., any server where PostgreSQL is running becomes *automatically*
accessible only by the members of the "DBA" group and nobody else).
</Admonition>

### Extended labels syntax
### Extended label matching syntax

Below are a few examples for more complex filtering using various regexes.

Expand All @@ -228,6 +231,65 @@ spec:
'environment': '^test|staging$'
```

### Label expressions

<Admonition type="warning">
Label expressions are available starting in Teleport version `13.1.1`.
All components of your Teleport cluster must be upgraded to version `13.1.1`
or newer before you will be able to use label expressions.
This includes the Auth Service and **all** Teleport agents.
</Admonition>

Teleport roles also support matching resource labels with predicate expressions
when you need to:

- combine logic with OR and AND operators
- perform matching on label keys
- implement negative matches

The following example role would allow access to any nodes labeled `env=staging`
or labeled `team=<team>`, where `<team>` is one of the values of the user's
`teams` trait:

```yaml
kind: role
version: v6
metadata:
name: example-role
spec:
allow:
node_labels_expression: |
labels["env"] == "staging" ||
contains(user.spec.traits["teams"], labels["team"])
```

The `<kind>_labels_expression` fields have the same purpose of the
matching `<kind>_labels` fields, but support predicate expressions instead
of label matchers.
They can be used in the following fields of role `spec.allow` and `spec.deny`
conditions:

- `node_labels_expression`
- `app_labels_expression`
- `cluster_labels_expression`
- `kubernetes_labels_expression`
- `db_labels_expression`
- `db_service_labels_expression`
- `windows_desktop_labels_expression`
- `group_labels_expression`

Check out our
[predicate language](../reference/predicate-language.mdx#label-expressions)
guide for a more in depth explanation of the language.

Typically you will only want to use one of `<kind>_labels` or
`<kind>_labels_expression` in a single role, but you are allowed to specify
both.
If you have both in a deny rule, the matching is greedy, if either one matches
access will be denied.
In an allow rule, the matching is not greedy, if both are set they both have to
match for access to be allowed.

## Teleport resources

RBAC lets teams limit what resources are available to Teleport users. This can be helpful if, for example,
Expand Down
53 changes: 41 additions & 12 deletions docs/pages/includes/role-spec.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,19 @@ spec:
windows_desktop_logins: [Administrator, '{{internal.logins}}']

# node_labels: a user with this role will be allowed to connect to
# SSH nodes, which labels match expressions below.
# SSH nodes with labels matching below.
node_labels:
# literal strings:
'env': 'test'
# the wildcard ('*') means "any node"
'*': '*'
# a list of alternative options:
'region': ['us-west-1', 'eu-central-1']
# regular expressions start with ^ and end with $
# Teleport uses golang regexp syntax.
# of the list example above can be expressed as:
# Regular expressions start with ^ and end with $.
# Teleport uses Go's regular expression syntax:
# https://github.com/google/re2/wiki/Syntax
# The list example above can be expressed as:
# 'region': '^us-west-1|eu-central-1$'
'reg': '^us-west-1|eu-central-1$'

# kubernetes_groups specifies Kubernetes groups a user with this role will assume.
Expand All @@ -143,14 +145,17 @@ spec:
kubernetes_users: ['IAM#{{external.foo}};']

# kubernetes_labels: a user with this role will be allowed to connect to
# k8s clusters, which labels match expressions below.
# k8s clusters with labels matching below.
kubernetes_labels:
# A user can only access prod environments
'env': 'prod'
# User can access any region in us-west, e.g us-west-1, us-west-2
'region': 'us-west-*'
# regular expressions start with ^ and ending with $
# Teleport uses golang regexp syntax.
# Regular expressions start with ^ and end with $.
# Teleport uses Go's regular expression syntax:
# https://github.com/google/re2/wiki/Syntax
# The list example above can be expressed as:
# 'region': '^us-west-1|eu-central-1$'
'cluster_name': '^us.*\.example\.com$'

# kubernetes_resources indicates the Kubernetes resources that a user with
Expand Down Expand Up @@ -178,7 +183,7 @@ spec:
'env': '{{regexp.replace(external.access["env"], "^(staging)$", "$1")}}'

# app_labels: a user with this role will be allowed to connect to
# applications, which labels match expressions below.
# applications with labels matching below.
app_labels:
# A user can only access prod environments
'env': 'prod'
Expand All @@ -198,6 +203,30 @@ spec:
# A user is given group membership to production related groups.
'env': 'prod'

# cluster_labels: a user with this role will be allowed to connect to remote
# clusters with labels matching below.
cluster_labels:
'env': 'prod'

# node_labels_expression has the same purpose as node_labels but
# supports predicate expressions to configure custom logic.
# A user with this role will be allowed to access nodes if they are in the
# staging environment *or* if they belong to one of the user's own teams.
node_labels_expression: |
labels["env"] == "staging" ||
contains(user.spec.traits["teams"] , labels["team"])
# The below <kind>_labels_expression fields have the same purpose of the
# matching <kind>_labels fields, but support predicate expressions instead
# of label matchers.
app_labels_expression: 'labels["env"] == "staging"'
cluster_labels_expression: 'labels["env"] == "staging"'
kubernetes_labels_expression: 'labels["env"] == "staging"'
db_labels_expression: 'labels["env"] == "staging"'
db_service_labels_expression: 'labels["env"] == "staging"'
windows_desktop_labels_expression: 'labels["env"] == "staging"'
group_labels_expression: 'labels["env"] == "staging"'

# aws_role_arns allows a user with this role to assume AWS roles when
# accessing AWS console using UI or AWS API using CLI
aws_role_arns:
Expand Down Expand Up @@ -245,12 +274,12 @@ spec:

# the `claims_to_roles` mapping works the same as it does in
# the OIDC connector, with the added benefit that the roles being mapped to
# can also be matchers.
# can also be matchers.
#
# This example leverages Teleport's regular expression support, which allows
# This example leverages Teleport's regular expression support, which allows
# for dynamic mapping from claims. The below mapping says that users with
# claims that match "projects: project-(.*)" can request roles that match
# "$1-admin", where "$1" is the first capture group in the
# claims that match "projects: product-(.*)" can request roles that match
# "$1-admin", where "$1" is the first capture group in the
# regular expression.
# Example: the "projects: product-foo" claim allows a user to request the
# "foo-admin" role
Expand Down
59 changes: 56 additions & 3 deletions docs/pages/reference/predicate-language.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ The predicate language uses a slightly different syntax depending on whether it

- [Role resources](#scoping-allowdeny-rules-in-role-resources)
- [Resource filtering](#resource-filtering)
- [Label expressions](#label-expressions)

## Scoping allow/deny rules in role resources

Some fields in Teleport's role resources use the predicate language to define the scope of a role's permissions:
Some fields in Teleport's role resources use the predicate language to define
the scope of a role's permissions:

- [Dynamic Impersonation](../access-controls/guides/impersonation.mdx#filter-fields)
- [RBAC for sessions](../access-controls/reference.mdx#filter-fields)
Expand All @@ -37,8 +39,8 @@ The language also supports the following functions:

## Resource filtering

Both the [`tsh`](cli.mdx#tsh) and [`tctl`](cli.mdx#tctl) CLI tools allow you to filter nodes,
applications, databases, and Kubernetes resources using the `--query` flag. The `--query` flag allows you to
Both the [`tsh`](cli.mdx#tsh) and [`tctl`](cli.mdx#tctl) CLI tools allow you to filter nodes,
applications, databases, and Kubernetes resources using the `--query` flag. The `--query` flag allows you to
perform more sophisticated searches using the predicate language.

For common resource fields, we defined shortened field names that can easily be accessed by:
Expand Down Expand Up @@ -68,3 +70,54 @@ The language also supports the following functions:
| `search("foo", "bar", "some phrase")` | fuzzy match against common resource fields |

See some [examples](cli.mdx#filter-examples) of the different ways you can filter resources.

## Label expressions

<Admonition type="warning">
Label expressions are available starting in Teleport version `13.1.1`.
All components of your Teleport cluster must be upgraded to version `13.1.1`
or newer before you will be able to use label expressions.
This includes the Auth Service and **all** Teleport agents.
</Admonition>

Label expressions can be used in Teleport roles to define access to resources
with custom logic.
Check out the Access Controls
[reference page](../access-controls/reference.mdx#label-expressions)
for an overview of label expressions and where they can be used.

Label expressions support a predicate language with the following fields
available:

| Field | Type | Description |
|--------------------|-------------------------|-------------|
| `labels` | `map[string]string` | Combined static and dynamic labels of the resource (server, application, etc.) being accessed. |
| `user.spec.traits` | `map[string][]string` | All traits of the user accessing the resource (referred to as `external` or `internal` in role template expressions). |

The language supports the following functions:

| Syntax | Return type | Description | Example |
|--------|-------------|-------------|---------|
| `contains(list, item)` | Boolean | Returns true if `list` contains an exact match for `item` | `contains(user.spec.traits[teams], labels["team"])` |
| `regexp.match(list, re)` | Boolean | Returns true if `list` contains a match for `re` | `regexp.match(labels["team"], "dev-team-\d+$")` |
| `regexp.replace(list,` `re, replacement)` | `[]string` | Replaces all matches of `re` with replacement for all items in `list` | `contains(regexp.replace(user.spec.traits["allowed-env"],` `"^env-(.*)$", "$1"), labels["env"])`
| `email.local(list)` | `[]string` | Returns the local part of each email in `list`, or an error if any email fails to parse | `contains(email.local(user.spec.traits["email"]),` `labels["owner"])`
| `strings.upper(list)` | `[]string` | Converts all items of the list to uppercase | `contains(strings.upper(user.spec.traits["username"]),` `labels["owner"])`
| `strings.lower(list)` | `[]string` | Converts all items of the list to lowercase | `contains(strings.lower(user.spec.traits["username"]),` `labels["owner"])`
| `labels_matching(re)` | `[]string` | Returns the aggregate of all label values with keys matching `re`, which can be a glob or a regular expression | `contains(labels_matching("^project-(team\|label)$"),` `"security")`
| `contains_any(list, items)` | Boolean | Returns true if `list` contains an exact match for any element of `items` | `contains_any(user.spec.traits["projects"],` `labels_matching("project-*"))` |
| `contains_all(list, items)` | Boolean | Returns true if `list` contains an exact match for all elements of `items` | `contains_all(user.spec.traits["projects"],` `labels_matching("project-*"))` |

Above, any argument named `list` can accept a list of values (like the list of
values for a specific user trait) or a single value (like the value of a
resource label or a string literal).

The language also supports the following operators:

| Operator | Meaning | Example |
|----------|-------------------------------------|---------|
| == | equal to | `labels["env"] == "staging"` |
| != | not equal to | `labels["env"] != "production"` |
| \|\| | or (any one condition should match) | `labels["env"] == "staging" \|\| labels["env"] == "test"` |
| && | and (all conditions must match) | `labels["env"] == "staging" && labels["team"] == "dev"` |
| ! | not (logical negation) | `!regexp.match(user.spec.traits["teams"], "contractor")` |

0 comments on commit 8c0afc7

Please sign in to comment.