Teleport Security Testing: SSH Certificate Bypass, Kubernetes Access Token Theft, and Session Recording Gaps

Teleport is a privileged access management platform that proxies SSH, Kubernetes, database, and application access through a centralized control plane. Because it sits in front of every privileged session, a security misconfiguration has outsized impact: a stolen identity certificate grants SSH access to every server the user can reach, over-broad Teleport roles grant Kubernetes cluster-admin equivalent access, and session recording gaps create audit trail blind spots. This guide covers systematic Teleport security assessment.

Table of Contents

  1. Teleport Attack Surface Overview
  2. SSH Certificate and Identity Security
  3. Teleport RBAC Role Audit
  4. Kubernetes Access Token Extraction
  5. Session Recording Gap Analysis
  6. Database Access Credential Testing
  7. Audit Log Integrity Testing
  8. Teleport Security Hardening

Teleport Attack Surface Overview

ComponentPortSecurity Attack Surface
Teleport Proxy443/3080Web UI auth bypass, certificate theft via MITM
Auth Server3025Certificate issuance, role assignment, token theft
SSH Node3022Direct SSH bypass if Teleport node accessible without proxy
Kubernetes proxy3026Kubeconfig token extraction, impersonation
Database proxy3036Database credential extraction via proxy
tctl (admin CLI)Token generation, role manipulation, CA export
# Check Teleport version (version fingerprinting)
curl -s https://teleport.example.com/webapi/ping | python3 -c "
import json,sys
data = json.load(sys.stdin)
print('Version:', data.get('server_version','?'))
print('Cluster name:', data.get('cluster_name','?'))
print('Auth type:', data.get('auth',{}).get('type','?'))
print('SSO:', data.get('auth',{}).get('oidc',{}).get('display','none'))
"

# Check if Teleport auth server is directly accessible
nc -z TARGET 3025 && echo "Auth server port accessible"
nc -z TARGET 3022 && echo "SSH node port accessible without proxy"
# Direct node access (port 3022) bypasses Teleport proxy recording/enforcement

SSH Certificate and Identity Security

# Teleport issues SSH certificates with TTL and role extensions
# Stolen certificates valid for their TTL give full SSH access

# Check certificate properties of a legitimate Teleport cert
ssh-keygen -L -f ~/.tsh/keys/teleport.example.com/username-ssh/teleport.example.com-cert.pub
# Key fields to check:
# - Valid before: short TTL = less exposure window
# - Critical Options: force-command restrictions?
# - Extensions: teleport-traits (roles, logins)

# Test: can certificates be used outside Teleport proxy (direct SSH)?
# If node allows both Teleport cert auth AND direct SSH connections:
ssh -i ~/.tsh/keys/teleport.example.com/username -p 3022 TARGET

# Check if nodes require Teleport CA only
# /etc/teleport.yaml on node should have:
# ssh_service:
#   enabled: yes
#   listen_addr: 127.0.0.1:3022  # Loopback only — not externally accessible
#
# If listen_addr: 0.0.0.0:3022 → direct SSH access bypasses proxy

# Test certificate renewal without MFA (if per-session MFA is not required)
tsh login --proxy=teleport.example.com
tsh status | grep -E "expiry|roles|logins"

# Check if certificate extends beyond MaxSessionTTL
# Role-defined MaxSessionTTL should limit cert validity
# If admin issues certs manually with --ttl=720h = 30 days of access with stolen cert

Teleport RBAC Role Audit

# Teleport roles define allowed logins, node access, and Kubernetes permissions
# Over-broad roles = full cluster access with a single compromised account

# List all roles (requires admin or read access)
tctl get roles

# Check for overly permissive roles
tctl get roles -o json | python3 -c "
import json,sys
roles = json.load(sys.stdin)
if isinstance(roles, dict): roles = [roles]
for role in roles:
    spec = role.get('spec',{})
    allow = spec.get('allow',{})
    logins = allow.get('logins',[])
    node_labels = allow.get('node_labels',{})
    k8s_groups = allow.get('kubernetes_groups',[])
    k8s_labels = allow.get('kubernetes_labels',{})
    print(f\"Role: {role.get('metadata',{}).get('name','?')}\")
    if 'root' in logins: print('  [!] Allows root login')
    if node_labels.get('*') == '*' or node_labels == {'*': ['*']}: print('  [!] Matches ALL nodes')
    if 'system:masters' in k8s_groups: print('  [!] Kubernetes cluster-admin via system:masters')
    if k8s_labels.get('*') == '*': print('  [!] Matches ALL Kubernetes clusters')
"

# Test for wildcard node label matching
# Role that allows access to nodes with ANY label = all nodes
# node_labels:
#   '*': '*'
# Bypass: even production nodes are accessible

# Test: can a regular user request an admin role via role request?
tsh request create --roles=admin --reason="Testing access" 2>&1
# If request is auto-approved or no approval workflow = privilege escalation

Kubernetes Access Token Extraction

# Teleport Kubernetes access uses impersonation and short-lived tokens
# The kubeconfig generated by `tsh kube login` contains a Teleport-issued credential

# Examine the generated kubeconfig
tsh kube login cluster-name 2>/dev/null
cat ~/.kube/config | python3 -c "
import yaml,sys
cfg = yaml.safe_load(sys.stdin)
for user in cfg.get('users',[]):
    exec_config = user.get('user',{}).get('exec',{})
    if exec_config:
        print(f\"User: {user['name']}\")
        print(f\"  Exec command: {exec_config.get('command','?')}\")
        print(f\"  Args: {exec_config.get('args','?')}\")
        # Teleport uses exec credential plugin — calls tsh kube credentials
"

# Extract the actual bearer token used for K8s API calls
kubectl config view --raw | grep token
# Or intercept via kubectl verbose output:
kubectl get pods -v=8 2>&1 | grep "Authorization: Bearer" | head -5
# Bearer token = Teleport-issued impersonation credential
# Valid for the kubeconfig TTL (default: 12h)

# Test what Kubernetes permissions the Teleport role grants
kubectl auth can-i --list --as="teleport-impersonated-user" 2>/dev/null | \
  grep -E "create|delete|get|list" | head -20

# Check if Teleport enforces Kubernetes RBAC or bypasses it
# Teleport can use impersonation (respects K8s RBAC) or direct token (bypasses it)
# Look for ClusterRoleBinding for Teleport service account
kubectl get clusterrolebinding | grep teleport
kubectl get clusterrolebinding teleport -o yaml 2>/dev/null | grep -A 3 roleRef

Session Recording Gap Analysis

# Teleport records SSH sessions and Kubernetes API calls
# Gaps: recording off for specific roles, storage failures, direct node access

# Check session recording configuration in cluster config
tctl get cluster_auth_preference -o json | python3 -c "
import json,sys
data = json.load(sys.stdin)
spec = data.get('spec',{})
print('Session recording:', spec.get('session_recording','node'))
# 'off' = no recording anywhere
# 'node' = recorded at node (default) — bypassed if node is compromised
# 'proxy' = recorded at proxy (stronger) — node compromise doesn't affect recording
# 'proxy-sync' = synchronous proxy recording
"

# Check individual roles for recording mode override
tctl get roles -o json | python3 -c "
import json,sys
roles = json.load(sys.stdin)
if isinstance(roles, dict): roles = [roles]
for role in roles:
    options = role.get('spec',{}).get('options',{})
    rec_mode = options.get('record_session',{})
    if rec_mode:
        print(f\"Role: {role.get('metadata',{}).get('name','?')}\")
        print(f\"  Session recording override: {rec_mode}\")
"

# Test: direct SSH connection bypasses proxy recording
# If Teleport node port 3022 is accessible directly:
ssh -i ~/.tsh/keys/PROXY/USER USER@NODE_IP -p 3022
# This session is NOT recorded in Teleport (bypasses proxy recording)

# Test: what happens if Teleport audit log storage is full?
# Teleport can be configured to 'block' or 'drop' on audit storage failure
# 'drop' = sessions continue without recording = audit gap

Database Access Credential Testing

# Teleport Database Access proxies connections to databases
# Credentials are issued as short-lived certificates or tokens
# Test for credential extraction and session bypass

# List accessible databases
tsh db ls

# Login to a database and check what credentials are issued
tsh db login db-name
ls -la ~/.tsh/keys/PROXY/USER/db/

# Check if database certificate includes username impersonation
openssl x509 -in ~/.tsh/keys/PROXY/USER/db/db-cert.pem -text -noout | \
  grep -A 5 "Subject:"
# CN=username maps to database user

# Test: can a Teleport database cert be used to connect directly (bypassing proxy)?
TELEPORT_DB_CERT=~/.tsh/keys/PROXY/USER/db/db-cert.pem
TELEPORT_DB_KEY=~/.tsh/keys/PROXY/USER/db/db-key.pem

# If database accepts cert auth directly:
psql "host=db.example.com sslcert=$TELEPORT_DB_CERT sslkey=$TELEPORT_DB_KEY user=admin dbname=postgres"
# If connects → Teleport proxy is bypassed = no session recording

# Check auto-provisioned database users
# Teleport can auto-create database users with matching roles
tctl get role developer -o json | python3 -c "
import json,sys
role = json.load(sys.stdin)
db_roles = role.get('spec',{}).get('allow',{}).get('db_roles',[])
db_names = role.get('spec',{}).get('allow',{}).get('db_names',[])
print(f'DB roles: {db_roles}')
print(f'DB names: {db_names}')
if 'superuser' in db_roles or 'rds_superuser' in db_roles:
    print('[!] Role grants database superuser access')
"

Audit Log Integrity Testing

# Teleport audit logs are critical for compliance and incident response
# Test for log completeness and tamper detection

# Check audit log destinations
tctl get cluster_auth_preference -o json | python3 -c "
import json,sys
data = json.load(sys.stdin)
# Audit log backend: filesystem, DynamoDB, Firestore, Athena
print('Check teleport.yaml for audit_events_uri and audit_sessions_uri')
"

# Query recent audit events
tctl events search --from=$(date -d '1 hour ago' -Iseconds 2>/dev/null || date -v-1H -Iseconds) \
  --type=session.start,session.end,auth,user.login 2>/dev/null | head -20

# Check if audit events include session.data for recordings
tctl events search --type=session.upload,session.end 2>/dev/null | head -10

# Test: what happens if an admin deletes an audit event?
# Teleport audit logs should be immutable — test whether admin can delete events
tctl delete events 2>/dev/null || echo "Delete not supported (correct)"

# Check if enhanced session recording is enabled (captures commands, not just sessions)
tctl get cluster_auth_preference -o json | python3 -c "
import json,sys
data = json.load(sys.stdin)
enhanced = data.get('spec',{}).get('enhanced_recording',{})
print('Enhanced recording:', enhanced)
# enhanced_recording.enabled: true = captures individual commands, not just terminal output
"

Teleport Security Hardening

Teleport Security Hardening Checklist:
Security TestMethodRisk
Node port 3022 directly accessiblenc -z NODE 3022Critical
Role grants system:masters K8s grouptctl get roles — check kubernetes_groupsCritical
Recording mode: node (bypassable)tctl get cluster_auth_preferenceHigh
Role with wildcard node label accessCheck node_labels: '*': '*' in rolesHigh
Role allows root SSH loginCheck logins: root in rolesHigh
Database cert usable outside proxyDirect psql/mysql with Teleport DB certHigh
Access requests auto-approvedtsh request create --roles=adminMedium

Automate Teleport PAM Security Testing

Ironimo tests Teleport deployments for node port exposure, session recording bypass paths, RBAC role privilege escalation, certificate TTL risks, database access credential extraction, and audit log completeness gaps.

Start free scan