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
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Teleport SSH SSO admin dashboard for the Nawasara superapp framework. It lists nodes/users/roles, launches browser-based SSH terminals with auto-login impersonation from Keycloak, and audits every access for forensic accountability.
Features
- Node inventory: lists all SSH nodes registered in the Teleport cluster with online/offline status, hostname, address, labels, and version
- User and role browser: lists Teleport users with their roles and traits, and lists roles with the full RBAC YAML expanded into a detail modal
- Browser SSH terminal: click [Connect] on any node and the terminal opens in a new tab with
xterm.js, logged in automatically 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 and reconnect attempt is 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: the
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 the WebSocket to the 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. It 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 are auto-assigned to the developer role by the seeder.
teleport.session.view is intentionally separate from teleport.ssh.connect so auditors can read who accessed what without gaining the ability 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, and 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, and OS login - Sidecar mints an ed25519 ephemeral keypair, signs an SSH cert via the Teleport
GenerateUserCertsAPI with the username field set to the Keycloak identity, and issues a single-use UUID v7 ticket - Browser opens a new tab with the ticket in the URL; the backend serves a standalone xterm.js page; JS opens a WebSocket to the sidecar
- Sidecar dials the 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 the WebSocket 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 the sidecar to Laravel.
Author
Pringgo J. Saputro <odyinggo@gmail.com>
License
MIT