nawasara / teleport
Teleport SSO admin dashboard for Nawasara — list nodes/users/roles via Go sidecar, browser-based SSH terminal with Keycloak identity propagation.
Requires
- php: ^8.1
- illuminate/support: ^10.0|^12.0
- livewire/livewire: ^3.0
- nawasara/ui: *
- nawasara/vault: *
- spatie/laravel-permission: ^6.0
README
Teleport SSH SSO admin dashboard for the Nawasara superapp framework — list nodes/users/roles, launch browser-based SSH terminals with auto-login impersonation from Keycloak, and audit every access for forensic accountability.
Features
- Node inventory — list all SSH nodes registered in the Teleport cluster with online/offline status, hostname, address, labels, and version
- User & role browser — list Teleport users with their roles + traits, list roles with full RBAC YAML expanded into a detail modal
- Browser SSH terminal — click [Connect] on any node, terminal opens in a new tab with
xterm.js, auto-login as the admin's Keycloak identity (no password, no key, no separate Teleport credential) - Reconnect button — when the WebSocket drops (network blip, shell exit, idle timeout), an in-place reconnect mints a fresh cert and rewires the WebSocket without leaving the page
- SSH session audit log — every launch + reconnect attempt logged to
nawasara_teleport_sessionswith reason, IP, user agent, ticket UUID, and (on close) duration in seconds - Sessions viewer page —
/nawasara-teleport/sessionswith status filter, node filter, actor filter, search, time-window, hero stats (total/issued/failed/avg duration), and CSV export - Cross-package audit —
nawasara/auditimpersonation log shows Teleport sessions alongside webmail/cPanel launch-as events for unified review
Architecture
This package is the Laravel half of a two-tier system. The other half is nawasara/teleport-bridge — a Go sidecar that wraps the Teleport API and bridges WebSocket ↔ SSH session.
Browser Laravel (this package) Sidecar (Go) Teleport
│ │ │ │
│ [Connect] click │ │ │
├─────────────────────▶│ POST /api/connect │ │
│ ├─────────────────────────▶│ GenerateUserCerts │
│ │ ├──────────────────────▶│
│ │ │◀──────────────────────│
│ │◀─ {ticket_id, ws_url} │ │
│ open new tab + ws │ │ │
├──────────────────────┼─────────────────────────▶│ ALPN dial proxy:443 │
│ │ ├──────────────────────▶│
│ ◀═══════════════════╪══════════════════════════╪═══ SSH session ═══════│
Installation
composer require nawasara/teleport
php artisan migrate
php artisan db:seed --class="Nawasara\Teleport\Database\Seeders\PermissionSeeder" --force
The package is auto-discovered by Laravel.
Sidecar setup
This package will not function without the Go sidecar reachable. Set up the sidecar first:
- Clone
nawasara/teleport-bridgeand follow its setup guide - The sidecar needs:
BRIDGE_SECRET— shared HMAC secret (also stored in Vault on the Laravel side)TELEPORT_PROXY— your Teleport proxy hostname:port (e.g.teleport.example.com:443)identity.pem— Teleport identity file generated viatsh loginfor a dedicated bot user
- Verify the sidecar is reachable:
curl http://127.0.0.1:9181/api/healthshould return200withcluster_name
Storing credentials in Vault
- Open Nawasara →
/nawasara-vault - Choose the Teleport group
- Fill in:
- Bridge URL — sidecar HTTP endpoint, e.g.
http://127.0.0.1:9181 - Bridge Secret — same HMAC secret set in
BRIDGE_SECRETon the sidecar - Proxy Address — Teleport proxy hostname:port, used for display only
- Bridge URL — sidecar HTTP endpoint, e.g.
- Save and click Test Connection — should return
Connected to cluster: <cluster_name>
The package picks up credentials from Vault automatically.
Verification
Open Teleport → Nodes in the sidebar. Your registered SSH nodes should appear with their online status. Click [Connect] on any node to launch a browser SSH terminal.
If something fails:
- Check the sidecar is running:
curl http://127.0.0.1:9181/api/health - Check the identity file is valid: on the sidecar host, run the
check-identity-ttl.shscript bundled with the bridge - Check Vault credentials match the sidecar
.env - Check the admin user has the
teleport.ssh.connectpermission
Permissions
| Permission | Description |
|---|---|
teleport.node.view |
List Teleport nodes |
teleport.user.view |
List Teleport users |
teleport.role.view |
List Teleport roles |
teleport.ssh.connect |
Launch browser SSH terminal (impersonate as Keycloak username) |
teleport.session.view |
View SSH session audit log (read-only — meant for compliance reviewers) |
All permissions auto-assigned to the developer role by the seeder.
teleport.session.view is intentionally separate from teleport.ssh.connect — auditors can read who accessed what without gaining the capability to launch new SSH sessions.
Routes
| Method | URL | Permission | Purpose |
|---|---|---|---|
| GET | /nawasara-teleport/nodes |
teleport.node.view |
Node inventory + Connect launcher |
| GET | /nawasara-teleport/users |
teleport.user.view |
Teleport user listing |
| GET | /nawasara-teleport/roles |
teleport.role.view |
Teleport role listing |
| GET | /nawasara-teleport/sessions |
teleport.session.view |
SSH session audit log |
| GET | /nawasara-teleport/terminal/{ticket} |
teleport.ssh.connect |
Fullscreen terminal page (xterm.js + ws) |
| POST | /nawasara-teleport/terminal/{ticket}/reissue |
teleport.ssh.connect |
Mint fresh ticket for in-place reconnect |
| POST | /api/internal/teleport/session-closed |
HMAC bearer (sidecar only) | Webhook receiver for ws-close enrichment (duration_seconds) |
How auto-login works
- Admin logs in to Nawasara via Keycloak SSO (handled by
nawasara/core) - Admin clicks [Connect] on a node — Livewire opens a confirmation modal asking for an access reason
- On submit, Laravel calls the sidecar
/api/connectwith the admin's Keycloak username + target node + OS login - Sidecar mints an ed25519 ephemeral keypair, signs an SSH cert via Teleport
GenerateUserCertsAPI with the username field set to the Keycloak identity, issues a single-use UUID v7 ticket - Browser opens a new tab with the ticket in the URL; backend serves a standalone xterm.js page; JS opens a WebSocket to the sidecar
- Sidecar dials Teleport proxy via TLS routing (ALPN protocol
teleport-proxy-ssh), runs theproxy:host:0@clustersubsystem, stacks a second SSH client over the pipe, and bridges bytes between WS and the SSH session
The audit row is written before the cert is minted (so failed launches still appear), and updated on ws.onclose with duration_seconds and ended_reason via a webhook from sidecar to Laravel.
Author
Pringgo J. Saputro <odyinggo@gmail.com>
License
MIT