teleport/lib/reversetunnel/api_with_roles.go
Sasha Klizhentas c623aa4dc5 Add cluster labels
Fixes #3604

This commit adds support for cluster_labels
role parameter limiting access to remote clusters by label.
New tctl update rc provides interface to set labels on remote clusters.

Consider two clusers, `one` - root and `remote` - leaf.

```bash
$ tsh clusters
Cluster Name Status
------------ ------
one          online
two          online
```

Create the trusted cluster join token with labels:

```bash
$ tctl tokens add --type=trusted_cluster --labels=env=prod
```

Every cluster joined using this token will inherit env:prod labels.

Alternatively, update remote cluster labels by modifying
`rc` command. Letting remote clusters to propagate their labels
creates a problem of rogue clusters updating their labels to bad values.

Instead, administrator of root cluster control the labels
using remote clusters API without fear of override:

```bash
$ tctl get rc

kind: remote_cluster
metadata:
  name: two
status:
  connection: online
  last_heartbeat: "2020-09-14T03:13:59.35518164Z"
version: v3
```

```bash
$ tctl update rc/two --set-labels=env=prod

cluster two has been updated
```

```bash
$ tctl get rc
kind: remote_cluster
metadata:
  labels:
    env: prod
  name: two
status:
  connection: online
  last_heartbeat: "2020-09-14T03:13:59.35518164Z"
```

Update the role to deny access to prod env:

```yaml
kind: role
metadata:
  name: dev
spec:
  allow:
    logins: [root]
    node_labels:
      '*': '*'

    # Cluster labels control what clusters user can connect to. The wildcard ('*') means
    # any cluster. If no role in the role set is using labels and cluster is not labeled,
    # the cluster labels check is not applied. Otherwise, cluster labels are always enforced.
    # This makes the feature backwards-compatible.
    cluster_labels:
      'env': 'staging'
  deny:
    # cluster labels control what clusters user can connect to. The wildcard ('*') means
    # any cluster. By default none is set in deny rules to preserve backwards compatibility
    cluster_labels:
      'env': 'prod'
```

```bash
$ tctl create -f dev.yaml
```

Cluster two is now invisible to user with `dev` role.

```bash
$ tsh clusters
Cluster Name Status
------------ ------
one          online
```
2020-11-03 16:10:15 -08:00

90 lines
2.3 KiB
Go

/*
Copyright 2020 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package reversetunnel
import (
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/trace"
)
// NewTunnelWithRoles returns new authorizing tunnel
func NewTunnelWithRoles(tunnel Tunnel, roles services.RoleSet, ap auth.AccessPoint) *TunnelWithRoles {
return &TunnelWithRoles{
tunnel: tunnel,
roles: roles,
ap: ap,
}
}
// TunnelWithRoles authorizes requests
type TunnelWithRoles struct {
tunnel Tunnel
// roles is a set of roles used to check RBAC permissions.
roles services.RoleSet
ap auth.AccessPoint
}
// GetSites returns a list of connected remote sites
func (t *TunnelWithRoles) GetSites() ([]RemoteSite, error) {
clusters, err := t.tunnel.GetSites()
if err != nil {
return nil, trace.Wrap(err)
}
out := make([]RemoteSite, 0, len(clusters))
for _, cluster := range clusters {
if _, ok := cluster.(*localSite); ok {
out = append(out, cluster)
continue
}
rc, err := t.ap.GetRemoteCluster(cluster.GetName())
if err != nil {
return nil, trace.Wrap(err)
}
if err := t.roles.CheckAccessToRemoteCluster(rc); err != nil {
if !trace.IsAccessDenied(err) {
return nil, trace.Wrap(err)
}
continue
}
out = append(out, cluster)
}
return out, nil
}
// GetSite returns remote site this node belongs to
func (t *TunnelWithRoles) GetSite(clusterName string) (RemoteSite, error) {
cluster, err := t.tunnel.GetSite(clusterName)
if err != nil {
return nil, trace.Wrap(err)
}
if _, ok := cluster.(*localSite); ok {
return cluster, nil
}
rc, err := t.ap.GetRemoteCluster(clusterName)
if err != nil {
return nil, trace.Wrap(err)
}
if err := t.roles.CheckAccessToRemoteCluster(rc); err != nil {
return nil, trace.Wrap(err)
}
return cluster, nil
}