Teleport Cheat Sheet
Overview
Teleport is an open-source, zero-trust access platform that provides engineers and security teams with a secure, auditable gateway to infrastructure resources including Linux servers, Kubernetes clusters, databases, and web applications. It implements the principle of least-privilege access through Role-Based Access Control (RBAC), requires strong authentication with MFA, integrates with identity providers via SSO, and records all sessions for compliance and forensic purposes.
Unlike traditional VPN-based access models, Teleport uses short-lived cryptographic certificates instead of long-lived SSH keys or passwords. Every access request results in a certificate issued for a specific duration (typically hours, not months), scoped to the resources the user’s role permits. When the certificate expires, access is automatically revoked without any manual key rotation. This model eliminates the persistent credential problem that makes SSH key management burdensome and creates security risks.
The Teleport architecture centers on the Auth Server (certificate authority and policy engine), the Proxy Service (public access point, handles all external connections), and Nodes/Agents (lightweight agents running on protected resources). The tsh CLI is the user-facing tool for connecting to resources, and tctl is the administrative CLI for managing users, roles, tokens, and cluster configuration. Teleport also provides a Web UI for browser-based terminal access, session replay, and resource discovery.
Installation
Quick Start (Docker)
# Run a standalone Teleport for testing
docker run --rm -d \
--name teleport \
-p 3080:3080 \
-p 3022:3022 \
-p 3025:3025 \
-v /tmp/teleport:/var/lib/teleport \
public.ecr.aws/gravitational/teleport:latest \
start --roles=proxy,auth,node \
--insecure-no-tls
# Access Web UI at http://localhost:3080
Auth/Proxy Server (Linux)
# Add Teleport repository
curl https://goteleport.com/static/install.sh | bash -s 15.0.0
# Install
sudo apt install -y teleport # Ubuntu/Debian
sudo dnf install -y teleport # RHEL/Fedora
# Generate initial configuration
sudo teleport configure \
--cluster-name=mycluster.example.com \
--public-addr=mycluster.example.com:443 \
--cert-file=/etc/ssl/certs/server.crt \
--key-file=/etc/ssl/private/server.key \
-o /etc/teleport.yaml
# Enable and start
sudo systemctl enable --now teleport
# Create first admin user (run on auth server)
sudo tctl users add admin --roles=editor,access --logins=root,ubuntu
# Follow the printed URL to set password and MFA
Node Agent (SSH Node)
# On the node to be enrolled
# 1. Install teleport binary (same as above)
# 2. Generate join token on auth server
sudo tctl tokens add --type=node --ttl=5m
# Copy the token
# 3. Configure node to join cluster
cat > /etc/teleport.yaml << 'EOF'
teleport:
data_dir: /var/lib/teleport
join_params:
token_name: "TOKEN_FROM_ABOVE"
method: token
proxy_server: mycluster.example.com:443
auth_service:
enabled: false
proxy_service:
enabled: false
ssh_service:
enabled: true
labels:
env: production
team: ops
EOF
sudo systemctl enable --now teleport
tsh Client Installation
# macOS
brew install teleport
# Linux
curl -O https://cdn.teleport.dev/teleport-v15.0.0-linux-amd64-bin.tar.gz
tar -xzf teleport-v15.0.0-linux-amd64-bin.tar.gz
sudo mv tsh /usr/local/bin/
# Windows (via winget)
winget install Teleport.tsh
# Verify
tsh version
Configuration
Teleport Auth Server Config (/etc/teleport.yaml)
teleport:
nodename: auth.example.com
data_dir: /var/lib/teleport
log:
output: stderr
severity: INFO
auth_service:
enabled: true
cluster_name: mycluster.example.com
listen_addr: 0.0.0.0:3025
tokens:
- "proxy,node:REPLACE_WITH_STRONG_TOKEN"
# GitHub SSO
authentication:
type: github
second_factor: on
webauthn:
rp_id: mycluster.example.com
# Session recording
session_recording: node
proxy_service:
enabled: true
public_addr: mycluster.example.com:443
listen_addr: 0.0.0.0:3080
https_keypairs:
- key_file: /etc/ssl/private/server.key
cert_file: /etc/ssl/certs/server.crt
kube_listen_addr: 0.0.0.0:3026
ssh_service:
enabled: true
labels:
env: production
RBAC Role Definition
# roles/developer-role.yaml
kind: role
version: v7
metadata:
name: developer
spec:
allow:
# SSH access to nodes with these labels
node_labels:
env: ["staging", "dev"]
logins: ["ubuntu", "ec2-user"]
# Kubernetes access
kubernetes_groups: ["developers"]
kubernetes_labels:
env: ["staging"]
kubernetes_resources:
- kind: pod
namespace: "{{internal.traits.team}}"
name: "*"
verbs: ["get", "list", "exec"]
# Database access
db_labels:
env: ["staging"]
db_names: ["app_db"]
db_users: ["readonly"]
# App access
app_labels:
env: ["staging", "dev"]
rules:
- resources: ["session"]
verbs: ["list", "read"]
deny:
# Deny access to production
node_labels:
env: ["production"]
options:
max_session_ttl: 8h
require_session_mfa: false
forward_agent: true
# Apply the role
tctl create -f roles/developer-role.yaml
# Assign role to user
tctl users update alice --set-roles=developer,access
Core Commands (tctl — Admin)
| Command | Description |
|---|---|
tctl status | Show cluster status |
tctl users ls | List all users |
tctl users add NAME --roles=ROLES --logins=LOGINS | Create a new user |
tctl users update NAME --set-roles=ROLES | Update user roles |
tctl users rm NAME | Delete a user |
tctl users reset NAME | Reset user password |
tctl roles ls | List all roles |
tctl roles get NAME | Get role details |
tctl create -f role.yaml | Create/update resource from YAML |
tctl get roles/NAME -o yaml | Export role as YAML |
tctl tokens add --type=node | Create a node join token |
tctl tokens add --type=db | Create a database join token |
tctl tokens ls | List active tokens |
tctl tokens rm TOKEN | Remove a token |
tctl nodes ls | List registered nodes |
tctl nodes rm UUID | Remove a node |
tctl auth sign --user=NAME --out=certs | Issue client certificate |
tctl auth export | Export cluster CA |
tctl audit-log | Query audit log |
tctl request ls | List access requests |
tctl request approve ID | Approve an access request |
tctl request deny ID | Deny an access request |
Core Commands (tsh — User)
| Command | Description |
|---|---|
tsh login --proxy=CLUSTER:443 | Authenticate to cluster |
tsh logout | Clear local certificates |
tsh status | Show current authentication status |
tsh ls | List accessible SSH nodes |
tsh ls -l | List nodes with labels |
tsh ssh USER@NODE | SSH into a node |
tsh ssh USER@NODE COMMAND | Run command on node |
tsh scp FILE USER@NODE:PATH | Copy file to node |
tsh sessions ls | List active sessions |
tsh sessions export SESSION_ID | Export session recording |
tsh play SESSION_ID | Play a recorded session |
tsh kube ls | List Kubernetes clusters |
tsh kube login CLUSTER | Configure kubectl for K8s cluster |
tsh kube sessions ls | List Kubernetes sessions |
tsh db ls | List accessible databases |
tsh db login DB_NAME | Log in to a database |
tsh db connect DB_NAME | Connect to database via proxy |
tsh apps ls | List accessible web apps |
tsh apps login APP_NAME | Get app access certificate |
tsh request create --roles=admin | Submit an access request |
tsh request ls | List my access requests |
tsh mfa add | Add an MFA device |
tsh mfa ls | List registered MFA devices |
tsh config | Show tsh configuration |
Advanced Usage
Kubernetes Access
# List available K8s clusters
tsh kube ls
# Log in to a Kubernetes cluster
tsh kube login my-k8s-cluster
# Now use kubectl as normal
kubectl get pods
kubectl exec -it mypod -- bash
# Run kubectl with specific Teleport identity
tsh kubectl get pods -n production
# List active Kubernetes sessions
tsh kube sessions ls
# Join an active session (session sharing)
tsh kube sessions join SESSION_ID
Database Access
# List accessible databases
tsh db ls
# Log in to PostgreSQL
tsh db login --db-user=readonly --db-name=appdb postgres-prod
# Connect via proxy (auto-starts local proxy tunnel)
tsh db connect postgres-prod
# Or use standard psql with the tunnel
eval $(tsh db env postgres-prod)
psql
# For MySQL
tsh db login --db-user=readonly --db-name=appdb mysql-prod
tsh db connect mysql-prod
# For MongoDB
tsh db login --db-user=readonly --db-name=appdb mongo-prod
tsh db connect mongo-prod
Access Requests (Privileged Access)
# Request temporary elevated access
tsh request create \
--roles=admin \
--reason="Investigating production outage" \
--reviewers=alice@example.com,bob@example.com
# Watch request status
tsh request ls
# Admin: approve a request
tctl request approve REQUEST_ID --reason="Approved for incident response"
# Admin: deny a request
tctl request deny REQUEST_ID --reason="Requires change management approval"
# User: activate approved request
tsh login --request-id=REQUEST_ID
# Check elevated session
tsh status
Session Recording and Playback
# List recorded sessions
tsh sessions ls --from=2024-01-01 --to=2024-12-31
# List sessions for a specific user
tsh sessions ls --user=alice
# Play a session in terminal
tsh play SESSION_ID
# Export session to asciicast format
tsh sessions export SESSION_ID > session.cast
# Then play with: asciinema play session.cast
# Search audit log for events
tctl audit-log query \
--from=2024-01-15T00:00:00Z \
--to=2024-01-16T00:00:00Z \
--event=session.start,session.end
SSO Integration (GitHub)
# github-connector.yaml
kind: github
version: v3
metadata:
name: github
spec:
client_id: GITHUB_OAUTH_APP_CLIENT_ID
client_secret: GITHUB_OAUTH_APP_CLIENT_SECRET
redirect_url: https://mycluster.example.com/v1/webapi/github/callback
display: "GitHub Login"
teams_to_roles:
- organization: myorg
team: engineering
roles: ["developer", "access"]
- organization: myorg
team: devops
roles: ["editor", "access"]
- organization: myorg
team: security
roles: ["auditor"]
# Apply GitHub SSO connector
tctl create -f github-connector.yaml
# Test SSO login
tsh login --proxy=mycluster.example.com:443 --auth=github
Enrolling Databases
# Generate database join token
tctl tokens add --type=db --ttl=10m
# Self-hosted PostgreSQL config snippet for teleport.yaml on database host
cat >> /etc/teleport.yaml << 'EOF'
db_service:
enabled: true
databases:
- name: postgres-prod
description: "Production PostgreSQL"
protocol: postgres
uri: localhost:5432
labels:
env: production
team: backend
EOF
# For RDS (add to proxy/auth server config)
db_service:
enabled: true
aws:
- types: ["rds"]
regions: ["us-east-1"]
tags:
teleport: "true"
# Grant database access to Teleport
# On PostgreSQL:
CREATE USER teleport_monitor WITH LOGIN;
GRANT CONNECT ON DATABASE appdb TO teleport_monitor;
GRANT pg_monitor TO teleport_monitor;
Audit Log Integration
# Stream audit events to S3 (in config.yaml)
# audit_events_uri: s3://my-teleport-logs/events
# audit_sessions_uri: s3://my-teleport-sessions/recordings
# Query recent auth events
tctl audit-log query --event=user.login --last=24h
# Export audit log as JSON
tctl audit-log query \
--from=2024-01-01T00:00:00Z \
--to=2024-01-31T23:59:59Z \
--event=session.start \
--format=json > sessions-january.json
# Watch live audit events
tctl audit-log stream --event=session.start,session.end
Common Workflows
Onboarding a New Engineer
# Admin: create user with appropriate roles
tctl users add alice@example.com \
--roles=developer,access \
--logins=ubuntu,ec2-user
# Admin: send the onboarding URL to Alice (printed by above command)
# Alice: sets password and registers MFA device
# Alice: log in
tsh login --proxy=mycluster.example.com:443
# Alice: browse accessible resources
tsh ls
tsh db ls
tsh kube ls
tsh apps ls
# Alice: connect to a dev server
tsh ssh ubuntu@dev-server-01
Emergency Production Access Workflow
# Engineer: request temporary admin access
tsh request create \
--roles=prod-admin \
--reason="SEV-1 production database is unresponsive, need root access" \
--reviewers=cto@example.com,oncall@example.com
# On-call lead: review and approve
tctl request approve REQUEST_ID \
--reason="Approved for SEV-1 response"
# Engineer: activate approved access
tsh login --request-id=REQUEST_ID
tsh ssh root@prod-db-01
# After incident: revoke access
tctl request deny REQUEST_ID # or let TTL expire
Node Inventory Management
# List all nodes with labels
tsh ls -l
# Filter by label
tsh ls env=production
tsh ls team=backend,env=staging
# Get nodes as JSON for automation
tsh ls --format=json | jq '.[] | select(.spec.labels.env=="production") | .metadata.name'
# Remove stale node registrations
tctl nodes ls --format=json | jq '.[] | select(.metadata.name=="old-node") | .metadata.name'
tctl nodes rm NODE_UUID
Tips and Best Practices
| Practice | Details |
|---|---|
| Short certificate TTLs | Keep max_session_ttl at 8-12h; never issue certificates for days or months |
| Require MFA | Set second_factor: on and require_session_mfa: true for production roles |
| Node labels for RBAC | Label every node with env, team, region — RBAC relies on label matching |
| Use access requests | Don’t grant standing production access; use request.roles with reviewers |
| Record all sessions | Set session_recording: node or proxy globally; store in S3 for compliance |
| Rotate join tokens | Create tokens with --ttl=5m; delete after nodes enroll |
| Audit log to S3 | Configure audit_events_uri: s3://bucket/events before going to production |
| Separate auth/proxy | Run Auth Server on an internal-only host; Proxy is the public-facing component |
| Restrict tctl access | Only trusted ops staff should have SSH access to the auth server |
| Regular tsh upgrades | Client and server must be within 1 major version — upgrade clients when upgrading cluster |
| Use Teleport Cloud | For smaller teams, Teleport Cloud handles auth server HA and upgrades |
| Review sessions regularly | Use tsh sessions ls and replay suspicious sessions promptly |