diff --git a/docs/pages/access-controls/reference.mdx b/docs/pages/access-controls/reference.mdx
index 64f70654605e9..281480195623b 100644
--- a/docs/pages/access-controls/reference.mdx
+++ b/docs/pages/access-controls/reference.mdx
@@ -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
@@ -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).
@@ -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
@@ -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:
@@ -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).
-### Extended labels syntax
+### Extended label matching syntax
Below are a few examples for more complex filtering using various regexes.
@@ -228,6 +231,65 @@ spec:
'environment': '^test|staging$'
```
+### Label expressions
+
+
+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.
+
+
+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=`, where `` 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 `_labels_expression` fields have the same purpose of the
+matching `_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 `_labels` or
+`_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,
diff --git a/docs/pages/includes/role-spec.mdx b/docs/pages/includes/role-spec.mdx
index 3ecdb82b34931..6c3842e11de38 100644
--- a/docs/pages/includes/role-spec.mdx
+++ b/docs/pages/includes/role-spec.mdx
@@ -120,7 +120,7 @@ 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'
@@ -128,9 +128,11 @@ spec:
'*': '*'
# 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.
@@ -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
@@ -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'
@@ -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 _labels_expression fields have the same purpose of the
+ # matching _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:
@@ -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
diff --git a/docs/pages/reference/predicate-language.mdx b/docs/pages/reference/predicate-language.mdx
index 28099c77fd48e..4d70344ecb2b3 100644
--- a/docs/pages/reference/predicate-language.mdx
+++ b/docs/pages/reference/predicate-language.mdx
@@ -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)
@@ -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:
@@ -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
+
+
+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.
+
+
+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")` |