{"token_count": 11058}

# Generate Teleport Roles from an External RBAC System

You can use the Teleport gRPC API to generate roles automatically based on an external role-based access control (RBAC) system, such as GitHub or AWS Identity and Access Management.

This is especially useful for:

- Setting up a new Teleport cluster, since you can preserve your existing authorization levels or categories while letting Teleport handle access control.
- Ensuring that your Teleport cluster stays up to date with the RBAC systems of the infrastructure it manages access to. This way, Teleport roles do not unexpectedly gain or lose permissions if your teams reconfigure your external RBAC systems.

## How it works

In this guide, we will build a small demo application to show you how to generate Teleport roles using Teleport's API client library.

The application authenticates to the Teleport Auth Service gRPC API as well as your Kubernetes API server, and loads the role bindings and cluster role bindings from the Kubernetes API server. For each role binding and cluster role binding, the application generates a Teleport role using logic that maps the fields in the former to the fields in the latter.

---

DANGER

The program we will build in this guide is intended as a learning tool. **Do not connect it to your production Teleport cluster.** Use a demo cluster instead.

---

## Prerequisites

- A running Teleport cluster accessible at a hostname with a valid TLS certificate. If you want to get started with Teleport, [sign up](https://goteleport.com/signup) for a free trial or [set up a demo environment](https://goteleport.com/docs/get-started/deploy-community.md).

- The `tctl` and `tsh` clients.

  Installing `tctl` and `tsh` clients

  1. Determine the version of your Teleport cluster. The `tctl` and `tsh` clients must be at most one major version behind your Teleport cluster version. Send a GET request to the Proxy Service at `/v1/webapi/find` and use a JSON query tool to obtain your cluster version. Replace teleport.example.com:443 with the web address of your Teleport Proxy Service:

     **Mac/Linux**

     ```
     $ TELEPORT_DOMAIN=teleport.example.com:443
     $ TELEPORT_VERSION="$(curl -s https://$TELEPORT_DOMAIN/v1/webapi/find | jq -r '.server_version')"
     ```

     **Windows - Powershell**

     ```
     $ $TELEPORT_DOMAIN = "teleport.example.com:443"
     $ $TELEPORT_VERSION = (Invoke-RestMethod -Uri "https://${TELEPORT_DOMAIN}/v1/webapi/find").server_version
     ```

  2. Follow the instructions for your platform to install `tctl` and `tsh` clients:

     **Mac**

     Download the signed macOS .pkg installer for Teleport, which includes the `tctl` and `tsh` clients:

     ```
     $ curl -O https://cdn.teleport.dev/teleport-${TELEPORT_VERSION?}.pkg
     ```

     In Finder double-click the `pkg` file to begin installation.

     ---

     DANGER

     Using Homebrew to install Teleport is not supported. The Teleport package in Homebrew is not maintained by Teleport and we can't guarantee its reliability or security.

     ---

     **Windows - Powershell**

     ```
     $ curl.exe -O https://cdn.teleport.dev/teleport-v$TELEPORT_VERSION-windows-amd64-bin.zip
     Unzip the archive and move the `tctl` and `tsh` clients to your %PATH%
     NOTE: Do not place the `tctl` and `tsh` clients in the System32 directory, as this can cause issues when using WinSCP.
     Use %SystemRoot% (C:\Windows) or %USERPROFILE% (C:\Users\<username>) instead.
     ```

     **Linux**

     All of the Teleport binaries in Linux installations include the `tctl` and `tsh` clients. For more options (including RPM/DEB packages and downloads for i386/ARM/ARM64) see our [installation page](https://goteleport.com/docs/installation/single-machine.md).

     ```
     $ curl -O https://cdn.teleport.dev/teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gz
     $ tar -xzf teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gz
     $ cd teleport
     $ sudo ./install
     Teleport binaries have been copied to /usr/local/bin
     ```

  Connecting with TLS routing disabled

  This guide's commands assume your Teleport cluster uses TLS routing (`proxy_listener_mode: multiplex`), where the `tctl` and `tsh` clients reach every Teleport service through the Proxy Service's web address on port `443`. If you're not sure whether this applies to your cluster, check with whoever manages it.

  If your cluster uses separate listener ports instead, adjust ports as follows:

  - **`tsh` commands** (e.g., `tsh login --proxy=...`): continue using the Proxy Service web address on port `3080` (or `443` if behind a load balancer). Do not change these to port `3025`.

  - **Direct `tctl` or Auth Service API commands**: use port `3025` for the Auth Service gRPC listener:

    ```
    $ tctl status --auth-server=teleport.example.com:3025
    ```

* Go version 1.25.12 or above installed on your workstation. See the [Go download page](https://go.dev/dl/). You will not need to be familiar with Go to complete this guide, though Go knowledge is required if you want to build a production-ready Teleport client application.

In a production scenario, you will already have a third-party RBAC solution to use as a basis for generating Teleport roles. In this guide, we will simulate this by deploying a local Kubernetes cluster using `minikube` and setting up some RBAC resources. We will then use this Kubernetes cluster to generate Teleport roles.

To run the local demo environment, ensure that you have the following tools installed on your workstation:

| Tool     | Purpose                          | Installation link                                             |
| -------- | -------------------------------- | ------------------------------------------------------------- |
| minikube | Local Kubernetes deployment tool | [Install minikube](https://minikube.sigs.k8s.io/docs/start/)  |
| Helm     | Kubernetes package manager       | [Install Helm](https://helm.sh/docs/intro/install/)           |
| kubectl  | Kubernetes admin CLI             | [Install kubectl](https://kubernetes.io/docs/tasks/tools/)    |
| Docker   | Required minikube driver         | [Get Started With Docker](https://www.docker.com/get-started) |

---

TIP

Even if you do not plan to set up the demo project, you can follow this guide to see which libraries, types, and functions you can use to automatically generate Teleport roles based on an external RBAC system.

---

- Check that you can connect to your Teleport cluster and verify that you can run `tctl` and `tsh` commands using your current credentials.

  1. Assign teleport.example.com to the domain name of the Teleport Proxy Service in your cluster and email\@example.com to your Teleport username.

  2. Authenticate to your Teleport cluster. This depends on whether your shell is interactive or not.

     **In an interactive shell:** Run the following command. By default, this triggers a multi-factor authentication prompt:

     ```
     $ tsh login --proxy=teleport.example.com --user=email@example.com
     $ tctl status
     Cluster  teleport.example.com
     Version  18.10.0
     CA pin   sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678
     ```

     **On non-interactive environments:** If you are running `tsh` and `tctl` as an AI agent, in a CI/CD environment, or similar, make sure the `TELEPORT_IDENTITY_FILE` environment variable is assigned to a valid file path with credentials for your cluster. `tsh` and `tctl` read the file path from the environment variable and do not require a separate authentication step. If there is no identity file available, we recommend that you [set up Machine ID](https://goteleport.com/docs/machine-workload-identity/getting-started.md) to provision one automatically.

     When executing `tctl` commands with an identity file, you must pass the `--auth-server` flag to provide the Teleport Auth Service address, which is not included in the identity file. If you provide the Proxy Service address, `tctl` connects to the Proxy Service, which forwards traffic to and from the Teleport Auth Service. Update 443 to `3025` if you are contacting the Auth Service directly with `tctl`:

     ```
     $ tctl status --auth-server=teleport.example.com:443
     ```

     For `tsh` commands that read an identity file, you must pass the `--proxy` flag, which points `tsh` to the address of the Teleport Proxy Service:

     ```
     $ tsh status --proxy=teleport.example.com
     ```

     Ensure client commands can access your identity file. Replace path/to/identity/file with the path to your identity file:

     ```
     $ export TELEPORT_IDENTITY_FILE="${TELEPORT_IDENTITY_FILE:-path/to/identity/file}"
     ```

     Add the `--auth-server` or `--proxy` flags to all subsequent `tctl` and `tsh` commands.

  If you can connect to the cluster and run the `tctl status` command, you can use your current credentials to run subsequent `tctl` commands from your workstation. If you host your own Teleport cluster, you can also run `tctl` commands on the computer that hosts the Teleport Auth Service for full permissions.

## Step 1/4. Set up your Kubernetes cluster

In this step, we will launch a local Kubernetes cluster and set up role-based access controls within it. We will then use this Kubernetes cluster as a basis for generating Teleport roles.

### Start minikube

Start minikube with the Docker driver, which boots a local Kubernetes cluster on a single Docker container:

```
$ minikube start --driver=docker
```

This command should start a local Kubernetes cluster and set your context (i.e., the Kubernetes cluster you are currently interacting with) to `minikube`. To verify this, run the following command:

```
$ kubectl config current-context
minikube
```

### Define demo Kubernetes RBAC resources

Next, we will set up RBAC resources in your local `minikube` cluster to use as a basis for generating Teleport roles.

In Kubernetes, you can divide a cluster into logically isolated **namespaces**. A **role** defines a set of permissions for manipulating resources in a specific namespace. A **cluster role** is a role that applies to all namespaces in a cluster. You can use a **role binding** or **cluster role binding** to attach a role or cluster role to Kubernetes users and groups.

Define a Kubernetes role and role binding that allows users in the `app-developer` group to read and list pods in the `app` namespace. Add the following to a file called `pod-reader.yaml`:

```
apiVersion: v1
kind: Namespace
metadata:
  name: app
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: app
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: app
  annotations:
    'create-teleport-role': 'true'
subjects:
- kind: Group
  name: app-developer
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

```

Create the resources:

```
$ kubectl apply -f pod-reader.yaml
namespace/app created
role.rbac.authorization.k8s.io/pod-reader created
rolebinding.rbac.authorization.k8s.io/read-pods created
```

---

TIP

You can also create and edit roles using the Web UI. Go to **Access -> Roles** and click **Create New Role** or pick an existing role to edit.

---

Next, define a cluster role and cluster role binding that allow users in the `ops` group to read, create, and execute commands on pods in all namespaces. Add the following to a file called `pod-ops.yaml`:

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pod-ops
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list", "create", "exec", "logs"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: pod-ops
  annotations:
    'create-teleport-role': 'true'
subjects:
- kind: Group
  name: ops
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: pod-ops
  apiGroup: rbac.authorization.k8s.io

```

Create the resources:

```
$ kubectl apply -f pod-ops.yaml
clusterrole.rbac.authorization.k8s.io/pod-ops created
clusterrolebinding.rbac.authorization.k8s.io/pod-ops created
```

Later in this guide, we will show you how to automatically generate Teleport roles based on the Kubernetes RBAC resources you created.

### Define RBAC resources for the client application

Next, ensure that your API client can read the RBAC resources you created. Create a file called `rbac-sync.yaml` with the following content:

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: rbac-sync
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["roles", "clusterroles", "rolebindings", "clusterrolebindings"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: rbac-sync
subjects:
- kind: User
  name: sync-kubernetes-rbac
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: rbac-sync
  apiGroup: rbac.authorization.k8s.io

```

Apply the changes:

```
$ kubectl apply -f rbac-sync.yaml
clusterrole.rbac.authorization.k8s.io/rbac-sync created
clusterrolebinding.rbac.authorization.k8s.io/rbac-sync created
```

## Step 2/4. Set up Teleport

In this step, you will configure Teleport to enable your API client application to interact with your Kubernetes cluster.

### Create a user and role for the client application

Give the client application a Teleport user and role that can retrieve information about a Kubernetes cluster that is registered with Teleport, authenticate to the cluster, and create or update Teleport roles.

Create a file called `sync-kubernetes-rbac.yaml` with the following content:

```
kind: role
version: v7
metadata:
  name: sync-kubernetes-rbac
spec:
  allow:
    kubernetes_labels:
      '*': '*'
    kubernetes_users:
      - sync-kubernetes-rbac
    kubernetes_resources:
      - kind: pod
        name: '*'
        namespace: '*'
    rules:
      - resources: ['kubernetes_cluster']
        verbs: ['read']
      - resources: ['role']
        verbs: ['create', 'update']
---
kind: user
metadata:
  name: sync-kubernetes-rbac
spec:
  roles: ['sync-kubernetes-rbac']
version: v2

```

Create the user and role:

```
$ tctl create -f sync-kubernetes-rbac.yaml
role 'sync-kubernetes-rbac' has been created
user "sync-kubernetes-rbac" has been created
```

### Enable impersonation of the client application

As with all Teleport users, the Teleport Auth Service authenticates the `sync-kubernetes-rbac` user by issuing short-lived TLS credentials. In this case, we will request the credentials manually by *impersonating* the `sync-kubernetes-rbac` role and user.

If you are running a self-hosted Teleport Enterprise deployment and are using `tctl` from the Auth Service host, you will already have impersonation privileges.

To grant your user impersonation privileges for `sync-kubernetes-rbac`, define a role called `sync-kubernetes-rbac-impersonator` by pasting the following YAML document into a file called `sync-kubernetes-rbac-impersonator.yaml`:

```
kind: role
version: v5
metadata:
  name: sync-kubernetes-rbac-impersonator
spec:
  allow:
    impersonate:
      roles:
      - sync-kubernetes-rbac
      users:
      - sync-kubernetes-rbac

```

Create the `sync-kubernetes-rbac-impersonator` role:

```
$ tctl create -f sync-kubernetes-rbac-impersonator.yaml
```

Assign the `sync-kubernetes-rbac-impersonator` role to your Teleport user. The commands to run depend on how you authenticate to Teleport, that is, whether you have a local Teleport user, a single sign-on authentication connector, or a non-interactive user:

**Local User**

1. Retrieve your local user's roles as a comma-separated list:

   ```
   $ ROLES=$(tsh status -f json | jq -r '.active.roles | join(",")')
   ```

2. Edit your local user to add the new role:

   ```
   $ tctl users update $(tsh status -f json | jq -r '.active.username') \
     --set-roles "${ROLES?},sync-kubernetes-rbac-impersonator"
   ```

3. Sign out of the Teleport cluster and sign in again to assume the new role.

**GitHub**

1. Open your `github` authentication connector in a text editor:

   ```
   $ tctl edit github/github
   ```

2. Edit the `github` connector, adding `sync-kubernetes-rbac-impersonator` to the `teams_to_roles` section.

   The team you should map to this role depends on how you have designed your organization's role-based access controls (RBAC). However, the team must include your user account and should be the smallest team possible within your organization.

   Here is an example:

   ```
     teams_to_roles:
       - organization: octocats
         team: admins
         roles:
           - access
   +       - sync-kubernetes-rbac-impersonator

   ```

3. Apply your changes by saving and closing the file in your editor.

4. Sign out of the Teleport cluster and sign in again to assume the new role.

**SAML**

1. Retrieve your `saml` configuration resource:

   ```
   $ tctl get --with-secrets saml/mysaml > saml.yaml
   ```

   Note that the `--with-secrets` flag adds the value of `spec.signing_key_pair.private_key` to the `saml.yaml` file. Because this key contains a sensitive value, you should remove the saml.yaml file immediately after updating the resource.

2. Edit `saml.yaml`, adding `sync-kubernetes-rbac-impersonator` to the `attributes_to_roles` section.

   The attribute you should map to this role depends on how you have designed your organization's role-based access controls (RBAC). However, the group must include your user account and should be the smallest group possible within your organization.

   Here is an example:

   ```
     attributes_to_roles:
       - name: "groups"
         value: "my-group"
         roles:
           - access
   +       - sync-kubernetes-rbac-impersonator

   ```

3. Apply your changes:

   ```
   $ tctl create -f saml.yaml
   ```

4. Sign out of the Teleport cluster and sign in again to assume the new role.

**OIDC**

1. Retrieve your `oidc` configuration resource:

   ```
   $ tctl get oidc/myoidc --with-secrets > oidc.yaml
   ```

   Note that the `--with-secrets` flag adds the value of `spec.signing_key_pair.private_key` to the `oidc.yaml` file. Because this key contains a sensitive value, you should remove the oidc.yaml file immediately after updating the resource.

2. Edit `oidc.yaml`, adding `sync-kubernetes-rbac-impersonator` to the `claims_to_roles` section.

   The claim you should map to this role depends on how you have designed your organization's role-based access controls (RBAC). However, the group must include your user account and should be the smallest group possible within your organization.

   Here is an example:

   ```
     claims_to_roles:
       - name: "groups"
         value: "my-group"
         roles:
           - access
   +       - sync-kubernetes-rbac-impersonator

   ```

3. Apply your changes:

   ```
   $ tctl create -f oidc.yaml
   ```

4. Sign out of the Teleport cluster and sign in again to assume the new role.

**Non-Interactive**

For non-interactive users such as AI agents and CI/CD runners, follow these steps.

If you are using Machine ID to issue identity files to your user:

1. Add the new role to your bot. Assign mybot to the name of the Machine ID bot that issues your certificate:

   ```
   $ tctl bots update mybot --add-roles sync-kubernetes-rbac-impersonator
   ```

2. Force `tbot` to regenerate the certificate:

   ```
   $ pkill -HUP tbot
   ```

   The bot will also generate a certificate with the new role on its next renewal.

Otherwise, update your user and regenerate your identity file:

1. Retrieve your local user's roles as a comma-separated list. Make sure username is the Teleport user you want to assign the new role to.

   ```
   $ ROLES=$(tctl get user/username --format=json | jq -r '.[].spec.roles | join(",")')
   ```

2. Edit your local user to add the new role:

   ```
   $ tctl users update username \
     --set-roles "${ROLES?},sync-kubernetes-rbac-impersonator"
   ```

3. Regenerate the identity file. Make sure path/to/identity/file corresponds to the correct output path:

   ```
   $ tctl auth sign \
     --user=username \
     --out=path/to/identity/file \
     --overwrite
   ```

You will now be able to generate signed certificates for the `sync-kubernetes-rbac` role and user.

### Install the Teleport Kubernetes Service

We will enable your client application to communicate with your Kubernetes cluster via the Teleport Kubernetes Service, which forwards requests after authorizing them. While this step is not strictly necessary with a local `minikube` cluster, it demonstrates one way to use Teleport to securely access your external RBAC system's API.

Configure Helm to fetch Teleport charts from the Teleport Helm repository:

```
$ helm repo add teleport https://charts.releases.teleport.dev
```

Refresh the local Helm cache by fetching the latest charts:

```
$ helm repo update
```

Request a token that the Kubernetes Service will use to join your Teleport cluster:

```
$ tctl tokens add --type=kube,app,discovery --format=text
```

Copy this token so you can use it when running the Teleport Kubernetes Service.

Ensure that you are connected to the right Kubernetes cluster (logging into Teleport earlier will have changed your Kubernetes context):

```
$ kubectl config use-context minikube
Switched to context "minikube".
```

Install the Teleport Kubernetes Service in your cluster, assigning proxy-address to the host **and port** of your Teleport Proxy Service (e.g., `mytenant.teleport.sh:443`) and token to the token you requested earlier:

```
$ helm install teleport-agent teleport/teleport-kube-agent \
  --set kubeClusterName=minikube \
  --set roles="kube\,app\,discovery" \
  --set proxyAddr=proxy-address \
  --set authToken=token \
  --create-namespace \
  --namespace=teleport-agent \
  --set labels.environment=demo \
  --version 18.10.0
```

After a few seconds, verify that you have deployed the Teleport Kubernetes Service by running the following command:

```
$ kubectl -n teleport-agent get pods
```

This should show that the Kubernetes Service is running:

```
NAME               READY   STATUS    RESTARTS   AGE
teleport-agent-0   1/1     Running   0          22s

```

`tsh` should indicate that the cluster has registered with Teleport:

```
$ tsh kube ls
Kube Cluster Name Labels           Selected
----------------- ---------------- --------
minikube          environment=demo
```

## Step 3/4. Write the client application

At this point, we have set up an external RBAC system to use for generating Teleport roles and configured Teleport to allow our API client to interact with our Kubernetes cluster and Teleport cluster. In this step, we will write our client application.

### Set up your Go project

Download the source code for the API client application:

```
$ git clone --depth=1 https://github.com/gravitational/teleport -b branch/v18
$ cd teleport/examples/api-sync-roles
```

For the rest of this guide, we will show you how to set up the client application and explore the ways it uses Teleport's API to automatically generate Teleport roles.

### Export identity files for the client application

The `sync-kubernetes-rbac` user needs signed credentials in order to connect to your Teleport cluster as well as your Kubernetes cluster. You will use the `tctl auth sign` command to request these credentials for your API client.

#### Connecting to your Teleport cluster

The following `tctl auth sign` command impersonates the `sync-kubernetes-rbac` user, generates signed credentials, and writes an identity file to the local directory:

```
$ tctl auth sign --user=sync-kubernetes-rbac --out=auth.pem
```

The identity file, `auth.pem`, includes both TLS and SSH credentials. Your client application uses the SSH credentials to connect to the Proxy Service, which establishes a reverse tunnel connection to the Auth Service. The client application uses this reverse tunnel, along with your TLS credentials, to connect to the Auth Service's gRPC endpoint.

#### Connecting to the Kubernetes cluster

You will also need to give the client application a way to authenticate to your Kubernetes cluster. To do this, use Teleport's certificate authority to sign credentials for the `sync-kubernetes-rbac` user. Your API client will present these credentials to authenticate to the Teleport Kubernetes Service, which will proxy requests to the Kubernetes cluster.

Run the following command, ensuring that proxy-address includes the host and port of your Proxy Service:

```
$ tctl auth sign --user=sync-kubernetes-rbac \
  --kube-cluster-name=minikube \
  --format=kubernetes \
  --proxy=https://proxy-address \
  --out=kubeconfig
```

### Imports

In the `api-sync-roles` directory, open `main.go`, which contains the API client program we demonstrate in this guide.

Here are the packages our client application imports from Go's standard library:

| Package   | Description                                                                                                                                                                                                                                               |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `context` | Includes the `context.Context` type. `context.Context` is an abstraction for controlling long-running routines, such as connections to external services, that might fail or time out. Programs can cancel contexts or assign them timeouts and metadata. |
| `fmt`     | Formatting data for printing, strings, or errors.                                                                                                                                                                                                         |
| `io`      | Dealing with I/O operations, e.g., reading files or network sockets.                                                                                                                                                                                      |
| `os`      | Interacting with the operating system, e.g., to open files.                                                                                                                                                                                               |
| `time`    | Dealing with time. We will use this to define a timeout for connecting to the Auth Service along with a ticker for executing our discovery logic in a loop.                                                                                               |

The client imports the following third-party code:

| Package                                        | Description                                                                                             |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `github.com/gravitational/teleport/api/client` | A library for authenticating to the Auth Service's gRPC API and making requests, aliased as `teleport`. |
| `github.com/gravitational/teleport/api/types`  | Types used in the Auth Service API, e.g., Application Service records.                                  |
| `github.com/gravitational/trace`               | Presenting errors with more useful detail than the standard library provides.                           |
| `google.golang.org/grpc`                       | The gRPC client and server library.                                                                     |
| `k8s.io/api/rbac/v1`                           | The Kubernetes RBAC API client library.                                                                 |
| `k8s.io/apimachinery/pkg/apis/meta/v1`         | Code common to Kubernetes' API client libraries.                                                        |
| `k8s.io/client-go/kubernetes`                  | Setting up an general-purpose Kubernetes client.                                                        |
| `k8s.io/client-go/kubernetes/typed/rbac/v1`    | Types for the Kubernetes RBAC API.                                                                      |
| `k8s.io/client-go/tools/clientcmd`             | Another general-purpose Kubernetes client library.                                                      |

### Constants

The program defines constants in a visible location so, later on, it's easier to make them configurable outside the program:

```
const (
	proxyAddr         string = ""
	initTimeout              = time.Duration(30) * time.Second
	identityFilePath  string = "auth.pem"
	kubeconfigPath    string = "kubeconfig"
	clusterName       string = "minikube"
	roleAnnotationKey string = "create-teleport-role"
)

```

We will use these constants later in the program. They define some values we may want to change later, including:

| Constant            | Description                                                                                                                                                                                                                     |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `proxyAddr`         | The host and port of the Teleport Proxy Service, e.g., `mytenant.teleport.sh:443`, which we will use to connect the client to your cluster. **Assign this to your own Proxy Service's host and port:** proxy-address            |
| `initTimeout`       | The timeout for connecting to the Teleport cluster. We have defined this as 30 seconds.                                                                                                                                         |
| `identityFilePath`  | The path to the Teleport identity file you created earlier.                                                                                                                                                                     |
| `clusterName`       | The name of the Kubernetes cluster you will fetch RBAC resources from. In this guide, the cluster's name is `minikube`.                                                                                                         |
| `roleAnnotationKey` | In Kubernetes, annotations are arbitrary key/value pairs that you can add to resources. The role and cluster role bindings we created earlier have the annotation key we specify here so our client application can fetch them. |

### Initializing a Kubernetes RBAC client

To contact the Kubernetes API, we will need to set up an HTTP client. The client authenticates to the API using mutual TLS, loading a client certificate, certificate authority, and private key from the file at `kubeconfigPath`. Earlier in the guide, we requested this from the Teleport Auth Service.

The program sets up a Kubernetes API client with the `getRBACClient` function:

```
func getRBACClient() (v1.RbacV1Interface, error) {
	f, err := os.Open(kubeconfigPath)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	kc, err := io.ReadAll(f)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	n, err := clientcmd.RESTConfigFromKubeConfig(kc)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	c, err := kubernetes.NewForConfig(n)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return c.RbacV1(), nil
}

```

`getRBACClient` opens and reads the Kubernetes credentials file at `kubeconfigPath`, then uses the file to set up a Kubernetes API client configuration (`clientcmd.RESTConfigFromKubeConfig(kc)`) and, with that, an HTTP client (`kubernetes.NewForConfig(n)`).

Finally, it returns an interface to the Kubernetes API dedicated to role-based access controls, which the rest of the program uses to interact with your Kubernetes cluster.

### Creating a Teleport role from a Kubernetes cluster role binding

The `createTeleportRoleFromClusterRoleBinding` function creates a Teleport role from a Kubernetes cluster role binding by populating fields in the former based on fields in latter:

```
func createTeleportRoleFromClusterRoleBinding(teleport *client.Client, k types.KubeCluster, r rbacv1.ClusterRoleBinding) error {
	if e, ok := r.Annotations[roleAnnotationKey]; !ok || e != "true" {
		return nil
	}

	role := types.RoleV6{}
	role.SetMetadata(types.Metadata{
		Name: k.GetName() + "-" + r.RoleRef.Name + "-" + "cluster",
	})

	b := k.GetStaticLabels()
	labels := make(types.Labels)
	for k, v := range b {
		labels[k] = []string{v}
	}
	role.SetKubernetesLabels(types.Allow, labels)
	role.SetKubeResources(types.Allow, []types.KubernetesResource{
		types.KubernetesResource{
			Kind:      "pod",
			Namespace: "*",
			Name:      "*",
		},
	})

	var g []string
	var u []string
	for _, s := range r.Subjects {
		if s.Kind == "User" || s.Kind == "ServiceAccount" {
			u = append(u, s.Name)
			continue
		}
		if s.Kind == "Group" {
			g = append(g, s.Name)
			continue
		}
	}
	role.SetKubeGroups(types.Allow, g)
	role.SetKubeUsers(types.Allow, u)
	if err := teleport.UpsertRole(
		context.Background(),
		&role,
	); err != nil {
		return trace.Wrap(err)
	}
	fmt.Println("Upserted Teleport role:", role.GetName())
	return nil
}

```

To avoid unexpected behavior, this function ignores Kubernetes-managed roles and roles for internal systems like the Teleport Kubernetes Service. This function checks the cluster role binding's metadata for an annotation with a specific key, `roleAnnotationKey`, and ignores any resource where this key is not set to `"true"`.

We also want a quick way to identify roles we created with this program. To do so, this function names all roles it generates based on cluster role bindings according to the following attributes:

- Kubernetes cluster name
- Kubernetes role name
- The suffix `-cluster`.

In our demo application, this function will create a Teleport role called `minikube-pod-ops-cluster`.

The rest of the function assigns fields to a `types.RoleV6`, the Teleport API client's role type, based on the cluster role binding:

| Role field                                             | Purpose                                                                                                                       | How we assign it                                                                              |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `allow.kubernetes_labels`                              | Labels for Teleport-registered Kubernetes clusters that a user with this role is allowed to access.                           | Based on the Teleport-registered Kubernetes cluster that the cluster role binding belongs to. |
| `allow.kubernetes_resources`                           | Kubernetes pods in specific namespaces that a user with this role is allowed to access.                                       | Allow access to all namespaces, since cluster role bindings are not restricted by namespace.  |
| `allow.kubernetes_users` and `allow.kubernetes_groups` | The Kubernetes groups and users that a Teleport user with this role will assume when interacting with the Kubernetes cluster. | Supply the names of any users or groups connected to the cluster role binding.                |

### Creating a Teleport role from a Kubernetes role binding

As with cluster role bindings, this program will also create Teleport roles based on Kubernetes role bindings:

```
func createTeleportRoleFromRoleBinding(teleport *client.Client, k types.KubeCluster, r rbacv1.RoleBinding) error {
	if e, ok := r.Annotations[roleAnnotationKey]; !ok || e != "true" {
		return nil
	}

	role := types.RoleV6{}
	role.SetMetadata(types.Metadata{
		Name: k.GetName() + "-" + r.RoleRef.Name + "-" + r.Namespace,
	})

	b := k.GetStaticLabels()
	labels := make(types.Labels)
	for k, v := range b {
		labels[k] = []string{v}
	}
	role.SetKubernetesLabels(types.Allow, labels)
	role.SetKubeResources(types.Allow, []types.KubernetesResource{
		types.KubernetesResource{
			Kind:      "pod",
			Namespace: r.Namespace,
			Name:      "*",
		},
	})
	var g []string
	var u []string
	for _, s := range r.Subjects {
		if s.Kind == "User" || s.Kind == "ServiceAccount" {
			u = append(u, s.Name)
			continue
		}
		if s.Kind == "Group" {
			g = append(g, s.Name)
			continue
		}
	}
	role.SetKubeGroups(types.Allow, g)
	role.SetKubeUsers(types.Allow, u)

	if err := teleport.UpsertRole(
		context.Background(),
		&role,
	); err != nil {
		return trace.Wrap(err)
	}
	fmt.Println("Upserted Teleport role:", role.GetName())
	return nil
}

```

While the overall behavior of this function is the same as `createTeleportRoleFromClusterRoleBinding`, Kubernetes role bindings require some differences in how we assign fields to Teleport roles:

- When setting the name of the role, we use the role binding's namespace as the suffix, rather than `-cluster`, to indicate the namespace that this role applies to.
- In the role's `kubernetes_resources` field, the value has the same namespace as the role binding, rather than applying to all namespaces.

### Creating Teleport roles based on Kubernetes resources

Now that we have functions to create Teleport roles based on individual Kubernetes RBAC resources, we can fetch all RBAC resources from our Kubernetes cluster and call these functions:

```
func createTeleportRolesForKubeCluster(teleport *client.Client, k types.KubeCluster) error {
	rbac, err := getRBACClient()
	if err != nil {
		return trace.Wrap(err)
	}

	crb, err := rbac.ClusterRoleBindings().List(
		context.Background(),
		metav1.ListOptions{},
	)
	if err != nil {
		return trace.Wrap(err)
	}

	for _, i := range crb.Items {
		if err := createTeleportRoleFromClusterRoleBinding(teleport, k, i); err != nil {
			return trace.Wrap(err)
		}
	}

	rb, err := rbac.RoleBindings("").List(
		context.Background(),
		metav1.ListOptions{},
	)
	if err != nil {
		return trace.Wrap(err)
	}

	for _, i := range rb.Items {
		if err := createTeleportRoleFromRoleBinding(teleport, k, i); err != nil {
			return trace.Wrap(err)
		}
	}
	return nil
}

```

`createTeleportRolesForKubeCluster` takes a Teleport client and a Teleport-registered Kubernetes cluster. It calls the `getRBACClient` function we defined earlier to set up a client for the Kubernetes cluster. It then:

- Lists Kubernetes cluster role bindings and creates a Teleport role for each one.
- Lists Kubernetes role bindings and creates a Teleport role for each one.

### Initializing clients and start the application

The functions we declared earlier require a Teleport API client and a Teleport-registered Kubernetes cluster, and we initialize these in the entrypoint of the program, the `main` function:

```
func main() {
	ctx, cancel := context.WithTimeout(context.Background(), initTimeout)
	defer cancel()
	creds := client.LoadIdentityFile(identityFilePath)

	teleport, err := client.New(ctx, client.Config{
		Addrs:       []string{proxyAddr},
		Credentials: []client.Credentials{creds},
	})
	if err != nil {
		panic(err)
	}
	fmt.Println("Connected to Teleport")

	ks, err := teleport.GetKubernetesServers(context.Background())
	if err != nil {
		panic(err)
	}
	for _, k := range ks {
		if k.GetCluster().GetName() != clusterName {
			continue
		}
		fmt.Println("Retrieved Kubernetes cluster", clusterName)

		if err := createTeleportRolesForKubeCluster(teleport, k.GetCluster()); err != nil {
			panic(err)
		}
		fmt.Println("Created roles for Kubernetes cluster", clusterName)
		return
	}
	panic("Unable to locate a Kubernetes Service instance for " + clusterName)
}

```

`client` is Teleport's library for setting up an API client. Our program initializes a Teleport client by calling `client.LoadIdentityFile` to obtain a `client.Credentials`. It then uses the `client.Credentials` to call `client.New`, which connects to the Teleport Proxy Service specified in the `Addrs` field using the provided identity file.

---

WARNING

This program does not validate your credentials or Teleport cluster address. Make sure that:

- The identity file you exported earlier does not have an expired TTL
- The value you supplied to the `Addrs` field in `teleport.Config` includes both the host **and** the web port of your Teleport Proxy Service, e.g., `mytenant.teleport.sh:443`

---

After initializing a Teleport client, the `main` function fetches all Kubernetes servers registered with Teleport (`teleport.GetKubernetesServers`) and checks if there is a registered Kubernetes cluster that matches the one you specified.

If a matching Kubernetes cluster exists, the code calls the `createTeleportRolesForKubeCluster` function we defined earlier. If not, the program prints an error message and a stack trace by calling Go's built-in `panic` function.

## Step 4/4. Test your client application

To test the client application, start it up from within its project directory:

```
$ go run main.go
```

You should see the following output:

```
Connected to Teleport
Retrieved Kubernetes cluster minikube
Upserted Teleport role: minikube-pod-ops-cluster
Upserted Teleport role: minikube-pod-reader-app
Created roles for Kubernetes cluster minikube

```

Examine the new `minikube-pod-ops-cluster` role by running the command below:

```
$ tctl get roles/minikube-pod-ops-cluster
```

You should see output similar to the following:

```
kind: role
metadata:
  id: 1678732494974032643
  name: minikube-pod-ops-cluster
spec:
  allow:
    kubernetes_groups:
    - ops
    kubernetes_labels:
      environment: demo
    kubernetes_resources:
    - kind: pod
      name: '*'
      namespace: '*'
  deny: {}
  options:
    cert_format: standard
    create_host_user: false
    desktop_clipboard: true
    desktop_directory_sharing: true
    enhanced_recording:
    - command
    - network
    forward_agent: false
    idp:
      saml:
        enabled: true
    max_session_ttl: 30h0m0s
    pin_source_ip: false
    ssh_port_forwarding:
      remote:
        enabled: true
      local:
        enabled: true
    record_session:
      default: best_effort
      desktop: true
    ssh_file_copy: true
version: v7

```

Compare this with the `minikube-pod-reader-app` role, which you can retrieve with the following command:

```
$ tctl get roles/minikube-pod-reader-app
```

Here is the role we created:

```
kind: role
metadata:
  id: 1678732495284493075
  name: minikube-pod-reader-app
spec:
  allow:
    kubernetes_groups:
    - app-developer
    kubernetes_labels:
      environment: demo
    kubernetes_resources:
    - kind: pod
      name: '*'
      namespace: app
  deny: {}
  options:
    cert_format: standard
    create_host_user: false
    desktop_clipboard: true
    desktop_directory_sharing: true
    enhanced_recording:
    - command
    - network
    forward_agent: false
    idp:
      saml:
        enabled: true
    max_session_ttl: 30h0m0s
    pin_source_ip: false
    ssh_port_forwarding:
      remote:
        enabled: true
      local:
        enabled: true
    record_session:
      default: best_effort
      desktop: true
    ssh_file_copy: true
version: v7

```

Since role bindings are namespaced, this role only allows access to pods in the `app` namespace, where this role binding was applied. The Kubernetes Service forwards traffic from users with this role using the `app-developer` Kubernetes group.

## Next steps

We have implemented a Teleport API client that generates Teleport roles based on the Kubernetes RBAC system. You can use Teleport's API to build similar applications that interact with other RBAC systems, such as GitHub teams or groups within your database management system.

Here are some starting points for building out your client application.

### Learn more about Teleport roles

To write an effective client application that generates Teleport roles from an external RBAC solution, you should understand the role fields that apply to infrastructure resources you want to manage access to.

See the links below for guides to fields related to different infrastructure resources:

- [Servers](https://goteleport.com/docs/enroll-resources/server-access/rbac.md)
- [Databases](https://goteleport.com/docs/enroll-resources/database-access/rbac.md)
- [Kubernetes clusters](https://goteleport.com/docs/enroll-resources/kubernetes-access/controls.md)
- [Windows Desktops](https://goteleport.com/docs/enroll-resources/desktop-access/rbac.md)
- [Applications](https://goteleport.com/docs/enroll-resources/application-access/configuration/controls.md)

For general guidance, read our [Access Controls Reference](https://goteleport.com/docs/reference/access-controls/roles.md).

### Register your cloud provider with Teleport

You can protect cloud provider APIs with Teleport and instruct your API client applications to connect to these APIs via the Teleport Application Service. Using Teleport-protected cloud provider APIs, you can generate Teleport roles based on your cloud provider's RBAC solution.

Read our guides for how to set up the Teleport Application Service for cloud provider APIs:

- [AWS](https://goteleport.com/docs/enroll-resources/application-access/cloud-apis/aws-console.md)
- [Google Cloud](https://goteleport.com/docs/enroll-resources/application-access/cloud-apis/google-cloud.md)
- [Azure](https://goteleport.com/docs/enroll-resources/application-access/cloud-apis/azure.md)

### Consult examples

The Teleport code repository contains [examples of production-ready Teleport API clients](https://github.com/gravitational/teleport/tree/v18.10.0/examples/). While we currently do not maintain plugins that generate Teleport roles, you can use these examples to see how to implement configuration parsing, retries, and other tasks.

### Provision the client application with short-lived credentials

In this example, we used the `tctl auth sign` command to fetch credentials for the program you wrote. For production usage, we recommend provisioning short-lived credentials via Machine & Workload Identity, which reduces the risk of these credentials becoming stolen. View our [Machine & Workload Identity documentation](https://goteleport.com/docs/machine-workload-identity/introduction.md) to learn more.
