Skip to main content

Command Palette

Search for a command to run...

Access Control, Actually: Teleport To the Rescue

Simplifying Kubernetes RBAC Using Teleport

Updated
15 min read
Access Control, Actually: Teleport To the Rescue
M

Working as a solutions architect while going deep on Kubernetes security — prevention-first thinking, open source tooling, and a daily rabbit hole of hands-on learning. I make the mistakes, then figure out how to fix them (eventually).

Last time, we walked the whole chain of Kubernetes access — from SSH on the node to the default service account that every Pod inherits. That exercise made one thing clear: Kubernetes doesn’t have a single front door. It has a series of loosely connected locks, and most of them assume you’ll do the right thing.

RBAC gives us policy, not identity. It answers what someone can do, not who they are. And that’s where setups can easily stop. Developers authenticate to Okta or GitHub, grab a kubeconfig from somewhere, and the cluster happily trusts whatever cert that file presents. In other words, Kubernetes leaves the identity problem unsolved.

There has to be a better way, right?

What if every access request were tied to a real human identity — backed by short-lived credentials and logged end to end — without touching the Kubernetes API?

That’s what Teleport does. It takes the same basic primitives (certificates, RBAC, and Kubernetes’ native API) and layers an auditable, identity-aware access proxy on top.

In this post, we’ll set up a local Teleport instance, connect it to a cluster, and replace our kubeconfig with short-lived, verifiable identity. Sounds pretty good.

Note on Licensing Teleport’s open-source edition is released under AGPL-3.0, which (according to my research) means that if you modify and run it as a network service for others, you’re expected to share your source code. For most personal labs and internal deployments, this isn’t an issue. I’ll cover open-source licensing in more detail in a separate post. It’s a surprisingly interesting topic.


The Lab Setup (Welcome to Hell)

Ugh, this was not a fun exercise. Of course at the end it was so easy to understand. If you want to do this in a local environment the instructions should work a charm. The instructions from the Teleport docs mostly work, but of course not perfectly.

I started simple: one Teleport proxy in Docker and a self-signed certificate with mkcert. Nothing fancy, no external dependencies. This was all done on my kubeadm controlplane node.

Install mkcert

sudo apt install mkcert
mkcert -install

Next create a cert folder where we store certs and share with our Docker container. Also add the mkcert CA to that folder. The trickiest part was getting the cert right. setting it to the IP address of the Controlplane node was the key.

mkdir teleport-tls
cd teleport-tls
mkcert 192.168.64.4 #Or your node IP
cp "$(mkcert -CAROOT)/rootCA.pem" .

Spin Up Docker

Now we have to spin up our Teleport Docker instance. Of course make sure Docker is installed on your node.

docker run -it -v .:/etc/teleport-tls -p 3080:443 ubuntu:22.04

Install Teleport inside the container

apt-get update && apt-get install -y curl
cp /etc/teleport-tls/rootCA.pem /etc/ssl/certs/mkcertCA.pem
curl https://cdn.teleport.dev/install.sh | bash -s 18.2.4

Then generate a config file with the generated certs:

teleport configure -o file \
  --cluster-name=teleport \
  --public-addr=192.168.64.4:3080 \
  --cert-file=/etc/teleport-tls/192.168.64.4.pem \
  --key-file=/etc/teleport-tls/192.168.64.4-key.pem

Finally, start it up:

teleport start --config=/etc/teleport.yaml

That’s your full access proxy running locally. The Teleport web UI comes up at https://192.168.64.4:3080 (or whatever IP address you have).

Create your first user

Fire up another terminal and connect to your Docker container, which you can find by the usual Docker command:

matt@controlplane:~$ docker ps
CONTAINER ID   IMAGE          COMMAND       CREATED      STATUS          PORTS                                       NAMES
7867833a79e8   ubuntu:22.04   "/bin/bash"   3 days ago   Up 27 minutes   0.0.0.0:3080->443/tcp, [::]:3080->443/tcp   heuristic_sammet
matt@controlplane:~$ docker exec -it 7867833a79e8 bash
root@7867833a79e8:/#

Then create a user for the Teleport UI (I kept the logins from the docs, but you don't need them).

tctl users add teleport-admin --roles=editor,access --logins=root,ubuntu,ec2-user

You’ll get a signup link. Open it in your browser to complete the setup and enable OTP. Annoying, but it gets worse later.

We're making good progress.


Integrating Kubernetes with Teleport

You'll notice a lot of resource options in the UI, but we'll stick with Kubernetes. On the Kubernetes side, it just requires you to follow the resource enrollment process.

Enroll Kubernetes Resource

Install the helm chart.

helm repo add teleport https://charts.releases.teleport.dev && helm repo update

Configure the cluster values and you'll get a command like follows, that you run in your terminal.

cat << EOF > prod-cluster-values.yaml
roles: kube,app,discovery
authToken: 3bdc40c408f1cd8809daeadfd83202e4
proxyAddr: 192.168.64.4:3080
kubeClusterName: kubernetes
labels:
    teleport.internal/resource-id: d5319a0c-5db5-4916-9984-8a598f2ae740

EOF

helm install teleport-agent teleport/teleport-kube-agent -f prod-cluster-values.yaml --version 18.2.4 \
--create-namespace --namespace teleport

You might change it to helm upgrade --install instead. You never know if you'll have to run it again.

Once the agent registers successfully, it appears in the Teleport UI under Kubernetes Clusters.
We'll come back to this.

Connect Client

Now we need to install our client. I went to a completely different Ubuntu machine that had no kubeconfig but was still on the same network.

Install Teleport client

sudo apt install -y apt-transport-https
curl https://deb.releases.teleport.dev/teleport-pubkey.asc | sudo tee /usr/share/keyrings/teleport-archive-keyring.asc
echo "deb [signed-by=/usr/share/keyrings/teleport-archive-keyring.asc] https://deb.releases.teleport.dev/ stable main" | sudo tee /etc/apt/sources.list.d/teleport.list
sudo apt update && sudo apt install teleport

Verify:

matt@linux-server-1:~$ tsh version
Teleport v18.2.4 git:v18.2.4-0-gb7ab869 go1.24.7

Cool we're all set. Except you probably don't have kubectl! So one more step.

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.34/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo chmod 644 /etc/apt/sources.list.d/kubernetes.list 
sudo apt-get update
sudo apt-get install -y kubectl

Login via Teleport

Now for the finale. In the Teleport UI, select your Kubernetes resource and copy the tsh command shown under “Connect”.

Run it to connect:

matt@linux-server-1:~$ tsh login --proxy=192.168.64.4:3080 --auth=local --user=teleport-admin teleport
ERROR: WARNING:

  The proxy you are connecting to has presented a certificate signed by a
  unknown authority. This is most likely due to either being presented
  with a self-signed certificate or the certificate was truly signed by an
  authority not known to the client.

  If you know the certificate is self-signed and would like to ignore this
  error use the --insecure flag.

  If you have your own certificate authority that you would like to use to
  validate the certificate chain presented by the proxy, set the
  SSL_CERT_FILE and SSL_CERT_DIR environment variables respectively and try
  again.

  If you think something malicious may be occurring, contact your Teleport
  system administrator to resolve this issue.

Oops bad cert, so let's bypass that and login in with your password and OTP. Note: --insecure only skips TLS validation to your local proxy, your Kubernetes traffic is still fully encrypted.

matt@linux-server-1:~$ tsh login --proxy=192.168.64.4:3080 --auth=local --user=teleport-admin teleport --insecure
Enter password for Teleport user teleport-admin:
WARNING: You are using insecure connection to Teleport proxy https://192.168.64.4:3080
Enter an OTP code from a device:
> Profile URL:        https://192.168.64.4:3080
  Logged in as:       teleport-admin
  Cluster:            teleport
  Roles:              access, editor
  Logins:             root, ubuntu, ec2-user
  Kubernetes:         enabled
  Kubernetes cluster: "kubernetes"
  Kubernetes users:   teleport-admin
  Kubernetes groups:  system:masters
  Valid until:        2025-10-19 02:17:46 -0700 PDT [valid for 12h0m0s]
  Extensions:         login-ip, permit-agent-forwarding, permit-port-forwarding, permit-pty, private-key-policy

Switch Kubernetes context and you can see the nodes:

matt@linux-server-1:~$ tsh kube login kubernetes --insecure
matt@linux-server-1:~/teleport$ kubectl get nodes
NAME           STATUS   ROLES           AGE    VERSION
controlplane   Ready    control-plane   366d   v1.31.8
kubeworker1    Ready    <none>          258d   v1.31.8

And there it is — the aha moment. No static ~/.kube/config, just short-lived, identity-based access that expires when it should. I promise this would be way easier with an EKS cluster.


Dissecting the Teleport-Generated Kubernetes Context

After logging in with:

tsh login --proxy=192.168.64.4:3080 --auth=local --user=teleport-admin --insecure

You’ve now got a short-lived identity with these traits:

  • Teleport Cluster: teleport

  • User: teleport-admin

  • Roles: access, editor

  • Kubernetes: enabled

  • Kubernetes Cluster: kubernetes

  • Kubernetes Groups: system:masters

  • Validity: 12 hours

As we saw before, running kubectl get nodes confirms access:

controlplane   Ready    control-plane   366d   v1.31.8
kubeworker1    Ready    <none>          258d   v1.31.8

What Teleport Did

Teleport issued short-lived client certificates and injected a Kubernetes context into your kubeconfig that points kubectl to Teleport’s Kubernetes proxy (:3026). Grab it as follows.

matt@linux-server-1:~$ cat .kube/config

The Teleport-generated kubeconfig section

apiVersion: v1
clusters:
- name: teleport-kube
  cluster:
    server: https://192.168.64.4:3026
    certificate-authority-data: <base64 PEM>
contexts:
- name: teleport-admin@teleport-kube
  context:
    cluster: teleport-kube
    user: teleport-admin@teleport-kube
    namespace: default
current-context: teleport-admin@teleport-kube
users:
- name: teleport-admin@teleport-kube
  user:
    client-certificate-data: <base64 PEM>
    client-key-data: <base64 PEM>

The server field shows that kubectl talks to the Teleport proxy rather than directly to the Kubernetes API server. Teleport validates your cert, maps your roles → K8s groups, and forwards the request securely.

Parsing Your Teleport-Injected kubeconfig

Here’s the kubeconfig your test box is using after tsh login (redacted):

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: <redacted>
    server: https://192.168.64.4:3080
    tls-server-name: kube-teleport-proxy-alpn.teleport.cluster.local
  name: teleport
contexts:
- context:
    cluster: teleport
    extensions:
    - extension: kubernetes
      name: teleport.kube.name
    user: teleport-kubernetes
  name: teleport-kubernetes
current-context: teleport-kubernetes
kind: Config
preferences: {}
users:
- name: teleport-kubernetes
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - kube
      - credentials
      - --kube-cluster=kubernetes
      - --teleport-cluster=teleport
      - --proxy=192.168.64.4:3080
      - --insecure
      command: /opt/teleport/system/bin/tsh
      env: null
      provideClusterInfo: false

Reading the kubeconfig (mostly right)

Teleport injects three major blocks — cluster, context, and user — each representing a layer in the connection chain.

Cluster

clusters:
- name: teleport
  cluster:
    server: https://192.168.64.4:3080
    tls-server-name: kube-teleport-proxy-alpn.teleport.cluster.local
    certificate-authority-data: <base64 PEM>
  • server: Teleport proxy URL, not direct API server.
  • tls-server-name: SNI/ALPN hint so the proxy knows you’re targeting Kubernetes.
  • certificate-authority-data: CA bundle trusted for proxy cert validation.

Context

contexts:
- name: teleport-kubernetes
  context:
    cluster: teleport
    user: teleport-kubernetes
    extensions:
    - name: teleport.kube.name
      extension: kubernetes
current-context: teleport-kubernetes
  • context: Binds Teleport cluster to Kubernetes user.
  • extensions: Teleport hint for cluster name.
  • current-context: The one kubectl will use.

User

users:
- name: teleport-kubernetes
  user:
    exec:
      command: /opt/teleport/system/bin/tsh
      args:
      - kube
      - credentials
      - --kube-cluster=kubernetes
      - --teleport-cluster=teleport
      - --proxy=192.168.64.4:3080
      - --insecure
      apiVersion: client.authentication.k8s.io/v1beta1
  • exec.command: tsh is your auth plugin.
  • args: Mint short-lived credentials on demand.
  • apiVersion: Defines plugin schema for Kubernetes.

Each time kubectl runs, it shells out to tsh to request new ephemeral credentials. The proxy validates, maps your Teleport roles to Kubernetes groups, and forwards the request to the actual API server.

Confirming Role Mapping

Teleport roles map to Kubernetes RBAC groups. In your case:

tsh status
...
  Kubernetes users:   teleport-admin
  Kubernetes groups:  system:masters
...

This gives you cluster-admin privileges because system:masters maps to the built-in cluster-admin ClusterRoleBinding. RBAC we've already learned, but good to check.

Check it with a hacky grep — we’re searching ClusterRoleBindings for any subject bound to system:masters:

matt@controlplane:~$ kubectl get clusterrolebindings -o yaml | grep -B20 -A5 "system:masters"
    name: calico-typha
    namespace: calico-system
- apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRoleBinding
  metadata:
    annotations:
      rbac.authorization.kubernetes.io/autoupdate: "true"
    creationTimestamp: "2024-10-17T19:53:04Z"
    labels:
      kubernetes.io/bootstrapping: rbac-defaults
    name: cluster-admin
    resourceVersion: "134"
    uid: 640338d1-5f25-4c59-bdca-893969ecb818
  roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: cluster-admin
  subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: Group
    name: system:masters
- apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRoleBinding
  metadata:
    annotations:
      meta.helm.sh/release-name: gatekeeper

Then you can prove this capability out.

matt@linux-server-1:~$ kubectl auth can-i --list | head -n 20
Resources                                       Non-Resource URLs   Resource Names   Verbs
*.*                                             []                  []               [*]
                                                [*]                 []               [*]
selfsubjectreviews.authentication.k8s.io        []                  []               [create]
selfsubjectaccessreviews.authorization.k8s.io   []                  []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                  []               [create]
globalnetworkpolicies.projectcalico.org         []                  []               [get list watch create update patch delete deletecollection]
networkpolicies.projectcalico.org               []                  []               [get list watch create update patch delete deletecollection]
                                                [/api/*]            []               [get]
                                                [/api]              []               [get]
                                                [/apis/*]           []               [get]
                                                [/apis]             []               [get]
                                                [/healthz]          []               [get]
                                                [/healthz]          []               [get]
                                                [/livez]            []               [get]
                                                [/livez]            []               [get]
                                                [/openapi/*]        []               [get]
                                                [/openapi]          []               [get]
                                                [/readyz]           []               [get]
                                                [/readyz]           []               [get]
matt@linux-server-1:~$ kubectl auth can-i delete nodes
Warning: resource 'nodes' is not namespace scoped

yes

Not too bad so far.


Comparing Before and After Teleport

Before Teleport, your kubeconfig looked like this:

apiVersion: v1
clusters:
- cluster:
    server: https://192.168.64.4:6443
    certificate-authority-data: <base64 PEM>
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
users:
- name: kubernetes-admin
  user:
    client-certificate-data: <base64 PEM>
    client-key-data: <base64 PEM>

This default kubeadm config uses static client certs with no central control or expiration. If it leaks, it’s effectively unlimited admin access.

After Teleport, things are much better (no forever pass):

  • Short-lived credentials via tsh
  • Proxy-mediated access
  • Role-based identity enforcement
  • Automatic expiry & audit
AspectBefore TeleportAfter Teleport
Credential typeStatic admin certShort-lived cert via tsh
EndpointDirect to API serverThrough Teleport proxy
IdentityHardcoded userRole-based (teleport-admin)
ExpiryManual rotationAuto-expires (12h)
AuditNoneCentralized logs & sessions

Create New Roles and Users

We’ll now create a minimal setup:

  1. Teleport role → maps to a Kubernetes group
  2. Teleport user → inherits that role
  3. ClusterRoleBinding → grants the group permissions

This follows from the documentation, but also goes a little deeper.

1) Teleport Role: junior-devs → maps to K8s view.

Save the following as junior-devs.yaml. This creates Teleport role that will give our user Kubernetes access.

kind: role
version: v7
metadata:
  name: junior-devs
spec:
  allow:
    logins: ['{{internal.logins}}']
    kubernetes_groups: ['{{internal.kubernetes_groups}}']
    node_labels:
      '*': '*'
    kubernetes_labels:
      '*': '*'
    kubernetes_resources:
      - kind: '*'
        namespace: '*'
        name: '*'
        verbs: ['*']

Apply on the Auth host with tctl:

root@7867833a79e8:~# tctl create junior-devs.yaml
role "junior-devs" has been created

2) Teleport User: jimbo with role junior-devs.

Save the following as jimbo.yaml. Creates a Teleport user that binds to the Teleport role of junior-devs and the Kubernetes group of teleport-view.

kind: user
version: v2
metadata:
  name: jimbo
spec:
  roles: ['junior-devs']
  traits:
    kubernetes_groups: ['teleport-view']

Apply on the Auth host with tctl:

root@7867833a79e8:~# tctl create -f jimbo.yaml
user "jimbo" has been created

To log in as jimbo, you'll need to repeat the user enrollment process again. After creating the user, go to the UI and reset authentication on the jimbo user account. This gave a new link to sign up with OTP (damn OTP, I now have way too many Teleport tokens).

Now for the actual log in:

matt@linux-server-1:~/teleport$ tsh login --proxy=192.168.64.4:3080 --auth=local --user=jimbo --insecure
Enter password for Teleport user jimbo:
WARNING: You are using insecure connection to Teleport proxy https://192.168.64.4:3080
Enter an OTP code from a device:
> Profile URL:        https://192.168.64.4:3080
  Logged in as:       jimbo
  Cluster:            teleport
  Roles:              junior-devs
  Kubernetes:         enabled
  Valid until:        2025-10-20 22:27:09 -0700 PDT [valid for 12h0m0s]
  Extensions:         login-ip, permit-port-forwarding, permit-pty, private-key-policy

matt@linux-server-1:~/teleport$ tsh kube ls
Kube Cluster Name Labels Selected
----------------- ------ --------
kubernetes

matt@linux-server-1:~/teleport$ tsh kube login kubernetes --insecure
Logged into Kubernetes cluster "kubernetes". Try 'kubectl version' to test the connection.

3) Kubernetes ClusterRoleBinding for the view group

If you don't have the ClusterRoleBinding you are sort of in a bind.

matt@linux-server-1:~/teleport$ kubectl get po
Error from server (Forbidden): pods is forbidden: User "jimbo" cannot list resource "pods" in API group "" in the namespace "default"

Since Teleport injects kubernetes_groups: ["teleport-view"], you'll need to bind that group to the built‑in view role.

Create the ClusterRoleBinding in a separate terminal.

matt@controlplane:~$ kubectl create clusterrolebinding teleport-view   --clusterrole=view   --group=teleport-view
clusterrolebinding.rbac.authorization.k8s.io/teleport-view created

Then verify you can use the view role.

matt@linux-server-1:~/teleport$ kubectl get po
NAME                        READY   STATUS    RESTARTS       AGE
flask-app-ccb7dbb5b-5x5qw   1/1     Running   5 (45h ago)    19d
nginx-676b6c5bbc-45cmn      1/1     Running   13 (45h ago)   137d

And that's that.

Teleport UI

Teleport Role: Go to Zero Trust Access -> Roles and choose Create New Role. Then supply the name and choose Kubernetes Access with a default Kubernetes resource:

Teleport User: Go to Zero Trust Access -> Users and choose Create New User. Then fill in the name, role, and trait (kubernetes_groups).

K8s Binding: Still done in Kubernetes as above.

Status Check

This completes a minimal identity → group → RBAC pipeline: Teleport defines who Jimbo is, Kubernetes RBAC decides what Jimbo can do. Easily done via code or UI.


Teleport Kubernetes Audit

Teleport logs every Kubernetes request. You can see this both in proxy logs and the UI. Let's take a look.

This is for a simple request.

matt@linux-server-1:~$ kubectl get deployment
NAME                 STATUS   AGE
calico-apiserver     Active   367d
calico-system        Active   367d
default              Active   367d
...

Proxy round‑trip (reverse proxy)

You can find this in the running teleport Docker instance, when you submit a request. The following is a concise parsing of your proxy round‑trip log and the corresponding kube.request audit event (both the raw line and the UI JSON).

2025-10-20T05:03:04.563Z INFO [PROXY:PRO] Round trip completed pid:17.1 method:GET url:https://kube-teleport-proxy-alpn.teleport.cluster.local/apis/apps/v1/namespaces/default/deployments?limit=500 code:200 duration:18.776759ms tls.version:772 tls.resume:false tls.csuite:4865 tls.server:kube-teleport-proxy-alpn.teleport.cluster.local reverseproxy/reverse_proxy.go:255

Key fields (what they mean):

  • method: GET — HTTP verb kubectl used.

  • url: .../apis/apps/v1/namespaces/default/deployments?limit=500 — Exact Kubernetes API path.

  • code: 200 — Upstream API server response.

  • tls.version / csuite / server: TLS details for the upstream hop inside Teleport (ALPN → Kube proxy).

This is the raw transport layer evidence: Teleport successfully proxied a K8s API call and got a 200 back.

UI JSON (same event)

Access the Teleport UI and you can view nicely formatted audit logs.

And clicking into details you see the JSON.

Here it is the actual request in all its glory.

{
  "addr.remote": "192.168.64.8:33222",
  "cluster_name": "teleport",
  "code": "T3009I",
  "ei": 0,
  "event": "kube.request",
  "kubernetes_cluster": "kubernetes",
  "kubernetes_groups": [
    "system:masters",
    "system:authenticated"
  ],
  "kubernetes_labels": {
    "teleport.internal/resource-id": "11beab83-a0fa-48b5-8e1f-fd454a7f714c"
  },
  "kubernetes_users": [
    "teleport-admin"
  ],
  "login": "teleport-admin",
  "namespace": "default",
  "proto": "kube",
  "request_path": "/apis/apps/v1/namespaces/default/deployments",
  "resource_api_group": "apps/v1",
  "resource_kind": "deployments",
  "resource_namespace": "default",
  "response_code": 200,
  "server_hostname": "teleport",
  "server_id": "785ed329-bb9b-4d16-8904-1b50d75377b5",
  "server_labels": {
    "teleport.internal/resource-id": "11beab83-a0fa-48b5-8e1f-fd454a7f714c"
  },
  "server_version": "18.2.4",
  "sid": "",
  "time": "2025-10-20T05:03:04.565Z",
  "uid": "9544742b-2cfa-49d9-abab-edd7e7b06554",
  "user": "teleport-admin",
  "user_cluster_name": "teleport",
  "user_kind": 1,
  "user_roles": [
    "access",
    "editor"
  ],
  "user_traits": {
    "kubernetes_groups": [
      "system:masters"
    ],
    "kubernetes_users": [
      "teleport-admin"
    ],
    "logins": [
      "root",
      "ubuntu",
      "ec2-user"
    ]
  },
  "verb": "GET"
}

A Logic of Sorts for the JSON:

Although you can surely decipher most of these, here is a rough map from a less technical point of view. Simple, clean, and fully auditable.

CategoryFields
Whouser, login, user_roles, kubernetes_groups
Whatverb, resource_kind, resource_api_group
Wherenamespace, cluster_name, addr.remote
Whentime, uid
Resultresponse_code
Serverserver_id, server_hostname, server_version

Wrap-Up: Access Control, Actually Done

That’s a wrap — for now — on Teleport and Kubernetes access.

What started as a quick experiment to make kubeconfig a little safer turned into an interesting, albeit time consuming exercise. The more I used Teleport, the more I see how it’s replacing an entire trust model.

  • Short-lived certs instead of forever-tokens
  • Centralized user and role management
  • Audit trails that are at your fingertips

Teleport is not just for Kubernetes, but it is clearly useful for leveling up Kubernetes RBAC. Teleport isn’t trying to reinvent Kubernetes security; it’s trying to make identity-aware access sane.

I’ll probably revisit this when I start layering in SSO and stuff, but for now? It’s a clean, comprehensible access model that’s hard not to like.