Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ DATABASE_URL=./data/keylessh.db
# ICE_SERVERS=stun:relay.example.com:3478
# TURN_SERVER: TURN relay fallback URL
# TURN_SERVER=turn:relay.example.com:3478
# TURN_SECRET: Shared secret for TURN REST API ephemeral credentials (HMAC-SHA256)
# TURN_SECRET: Shared secret for TURN REST API ephemeral credentials (HMAC-SHA1)
# TURN_SECRET=your-turn-secret-here

# Auth server override (optional, for testing)
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ The result: enterprise-grade SSH access control without any private keys to mana

## Features

- **Browser-side SSH** via `@microsoft/dev-tunnels-ssh` + `xterm.js`
- **Browser-side SSH** via `@microsoft/dev-tunnels-ssh` + `xterm.js`, with no private keys anywhere
- **SFTP file browser** - Browse, upload, download, rename, delete files via split-panel UI
- **Quorum-based RBAC, zero-knowledge OIDC login** with TideCloak - no passwords, no keys
- **Programmable policy encforcement** with Forseti contracts for SSH access
- **Simple, static, trustless SSH account access** (e.g., only `ssh:root` role holders can SSH as root)
- **Admin UX**: servers, users, roles, policy templates, change requests (access, roles, policies), sessions, logs
- **Optional external bastion** (`bridges/tcp-bridge`) for scalable WS↔TCP tunneling
- **NAT-traversing HTTP gateway** (`bridges/punchd-bridge`) with WebRTC P2P upgrade
- **Programmable policy enforcement** with Forseti contracts for SSH access
- **Admin UX**: servers, users, roles, policy templates, change requests, sessions, logs
- **Browser-based RDP** - full Windows remote desktop in a browser tab via [IronRDP](https://github.com/Devolutions/IronRDP) WASM. No client install, no ports to open, no VPN. See [RDP Architecture](bridges/punchd-bridge/docs/ARCHITECTURE.md#rdp-remote-desktop-ironrdp-wasm--rdcleanpath).
- **P2P DataChannel transport** - automatic upgrade from HTTP relay to direct peer-to-peer WebRTC, with a Service Worker that silently reroutes all browser fetches through the encrypted DataChannel. See [Connection Lifecycle](bridges/punchd-bridge/docs/ARCHITECTURE.md#connection-lifecycle).
- **Signal server** (`signal-server/`) - coordinates P2P connections between browsers and gateways via WebSocket signaling (SDP/ICE), relays HTTP traffic before DataChannel is ready, and generates ephemeral TURN credentials. Deployed with a coturn sidecar for STUN NAT discovery and TURN relay fallback. See [Architecture](bridges/punchd-bridge/docs/ARCHITECTURE.md#system-overview).
- **Multi-backend routing** (`bridges/punchd-bridge`) - proxy to multiple HTTP backends and RDP servers from a single gateway. See [Multi-Backend Routing](bridges/punchd-bridge/docs/ARCHITECTURE.md#multi-backend-routing).

## Project Structure

Expand All @@ -62,8 +63,8 @@ keylessh/

### Component docs

- **Punc'd Bridge**NAT-traversing HTTP reverse proxy that lets you expose local web apps through a public signal server without port forwarding. Starts with HTTP relay over WebSocket, then upgrades to peer-to-peer WebRTC DataChannels. See [bridges/punchd-bridge/docs/ARCHITECTURE.md](bridges/punchd-bridge/docs/ARCHITECTURE.md) for the full connection lifecycle, PlantUML diagrams, and multi-backend routing.
- **Signal Server**Public signaling hub that brokers WebSocket connections between gateways and clients. Handles gateway registration, ICE candidate exchange, HTTP request relay, and TURN credential provisioning. Deployed alongside a coturn sidecar for STUN/TURN. See [signal-server/deploy.sh](signal-server/deploy.sh) for the automated VM deployment script.
- [Punch'd Bridge](bridges/punchd-bridge/docs/ARCHITECTURE.md)[connection lifecycle](bridges/punchd-bridge/docs/ARCHITECTURE.md#connection-lifecycle) (portal → relay → P2P → SW takeover), [RDP/RDCleanPath](bridges/punchd-bridge/docs/ARCHITECTURE.md#rdp-remote-desktop-ironrdp-wasm--rdcleanpath), [multi-backend routing](bridges/punchd-bridge/docs/ARCHITECTURE.md#multi-backend-routing), [DataChannel messages](bridges/punchd-bridge/docs/ARCHITECTURE.md#datachannel-messages-gateway--browser), [API endpoints](bridges/punchd-bridge/docs/ARCHITECTURE.md#signal-server-api-routes), [security & rate limits](bridges/punchd-bridge/docs/ARCHITECTURE.md#security), [sequence diagrams](bridges/punchd-bridge/docs/diagrams/)
- [Signal Server](signal-server/deploy.sh)WebSocket signaling (SDP/ICE), HTTP relay, gateway registry, TURN credential generation, coturn sidecar for STUN/TURN

## Quickstart (Local Dev)

Expand Down
77 changes: 70 additions & 7 deletions bridges/punchd-bridge/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Punc'd
# Punch'd

NAT-traversing authenticated reverse proxy gateway. Access private web applications from anywhere through hole-punched WebRTC DataChannels.
NAT-traversing authenticated reverse proxy gateway. Access private web applications and remote desktops from anywhere through hole-punched WebRTC DataChannels — no port forwarding, no VPN, no public IP required.

## How it works

```
Browser → Signal Server (relay) → Gateway → Backend App
│ ↕ coturn │
Browser → Signal Server (relay) → Gateway → Backend App (HTTP)
│ ↕ coturn │ └→ RDP Server (RDCleanPath)
└──── WebRTC DataChannel (P2P) ────┘
(after hole punch)
```
Expand All @@ -19,6 +19,59 @@ The system has three components:

The signal server and gateway can be run by **different operators**. Clients connect through the signal server's HTTP relay, then upgrade to peer-to-peer WebRTC DataChannels via NAT hole punching (using coturn for STUN binding and TURN relay fallback). A Service Worker transparently routes browser fetches through the DataChannel.

## Features

### Connection Lifecycle

Connections progress through four phases automatically — the user just opens a URL:

1. **Portal selection** — pick a gateway and backend from the signal server's portal page
2. **HTTP relay** — all traffic tunneled through the signal server's WebSocket until WebRTC is ready
3. **WebRTC upgrade** — injected `webrtc-upgrade.js` performs ICE/STUN hole punching for a direct P2P DataChannel, with TURN relay fallback
4. **Service Worker takeover** — a Service Worker transparently intercepts browser fetches and routes them through the DataChannel instead of HTTP relay

See [Connection Lifecycle](docs/ARCHITECTURE.md#connection-lifecycle) for sequence diagrams.

### Multi-Backend Routing

A single gateway can proxy to multiple backends using path-based routing (`/__b/<name>/`). The gateway rewrites HTML responses (links, scripts, fetch/XHR calls) to maintain correct routing across backends. Backends can be marked `;noauth` to skip JWT validation.

```bash
BACKENDS="App=http://localhost:3000,Auth=http://localhost:8080;noauth"
```

See [Multi-Backend Routing](docs/ARCHITECTURE.md#multi-backend-routing) for path prefix system and HTML rewriting details.

### RDP Remote Desktop

Browser-based RDP via [IronRDP](https://github.com/Devolutions/IronRDP) WASM. The gateway implements the **RDCleanPath protocol** — it handles TLS termination with the RDP server so IronRDP WASM (which can't do raw TCP/TLS from a browser) can perform CredSSP/NLA authentication. RDP traffic flows through the same WebRTC DataChannel as HTTP. No WebSocket server needed — the DataChannel carries RDCleanPath PDUs directly using a virtual WebSocket shim.

```bash
BACKENDS="Web App=http://localhost:3000,My PC=rdp://localhost:3389"
```

Navigate to `/rdp?backend=My%20PC`, enter Windows credentials, and connect.

See [RDP Architecture](docs/ARCHITECTURE.md#rdp-remote-desktop-ironrdp-wasm--rdcleanpath) for the RDCleanPath protocol, ASN.1 wire format, and IronRDP WASM build instructions.

### Authentication

Gateway-side OIDC authentication via TideCloak. TideCloak traffic is reverse-proxied through the gateway so it never needs direct browser access. Features transparent token refresh, server-side cookie jars for both TideCloak and backend sessions (needed because DataChannel responses can't set cookies), and `dest:<gatewayId>:<backendName>` role-based access control.

See [Authentication Flow](docs/ARCHITECTURE.md#authentication-flow) for the OIDC login flow, token validation, and endpoint reference.

### Signal Server API

The signal server exposes HTTP endpoints for portal interaction, gateway listing, admin actions, and TideCloak SSO — plus WebSocket signaling for gateway registration, client pairing, ICE candidate exchange, and HTTP relay.

See [Signal Server API Routes](docs/ARCHITECTURE.md#signal-server-api-routes) and [Signaling Message Reference](docs/ARCHITECTURE.md#signaling-message-reference) for the full endpoint and message catalog.

### Security

JWT-validated requests at every entry point (relay, DataChannel, backend proxy). HTTP method whitelist, open redirect prevention, body size limits, CORS origin validation, timing-safe secret comparison, rate limiting per IP (20 connections, 100 msg/s), and automatic reconnection with exponential backoff.

See [Security](docs/ARCHITECTURE.md#security) for headers, rate limits, capacity, and resilience details.

## Quick start

```bash
Expand Down Expand Up @@ -62,7 +115,7 @@ This starts coturn, the signal server, and the gateway together. Set `TURN_SECRE
| Secret | Generated by | Shared with | Purpose |
|--------|-------------|-------------|---------|
| `API_SECRET` | Signal server operator | Gateway operators | Authenticates gateway registration (timing-safe) |
| `TURN_SECRET` | Signal server operator | Gateway operators | Generates ephemeral TURN credentials (HMAC-SHA256) |
| `TURN_SECRET` | Signal server operator | Gateway operators | Generates ephemeral TURN credentials (HMAC-SHA1) |

**Secret flow:**
1. Signal server operator deploys and generates secrets (or sets them manually)
Expand All @@ -85,15 +138,25 @@ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#configuration-reference) for the
|----------|-----------|-------------|
| `SIGNAL_SERVER_URL` | Gateway | WebSocket URL of the signal server |
| `BACKEND_URL` | Gateway | Backend to proxy to |
| `BACKENDS` | Gateway | Multiple backends: `"App=http://host:3000,Auth=http://host:8080;noauth"` |
| `BACKENDS` | Gateway | Multiple backends: `"App=http://host:3000,Desktop=rdp://host:3389"` |
| `API_SECRET` | Both | Shared secret for gateway registration |
| `TURN_SECRET` | Both + coturn | Shared secret for TURN credentials (HMAC-SHA256) |
| `TURN_SECRET` | Both + coturn | Shared secret for TURN credentials (HMAC-SHA1) |
| `EXTERNAL_IP` | coturn | Public IP for TURN relay addresses |
| `TIDECLOAK_CONFIG_B64` | Both | Base64 TideCloak config for authentication |
| `TC_INTERNAL_URL` | Gateway | Internal TideCloak URL when `KC_HOSTNAME` is public |
| `GATEWAY_DISPLAY_NAME` | Gateway | Name shown in the portal |
| `GATEWAY_DESCRIPTION` | Gateway | Description shown in the portal |

## Ports

| Component | Port | Protocol | Purpose |
|-----------|------|----------|---------|
| coturn | 3478 | UDP + TCP | STUN/TURN |
| Signal server | 9090 | HTTP/WS | Signaling, portal, admin, HTTP relay |
| coturn | 49152-65535 | UDP | TURN relay sockets |
| Gateway | 7891 | HTTP/HTTPS | Proxy server |
| Gateway | 7892 | HTTP | Health check |

## Documentation

- [Architecture & Protocol Details](docs/ARCHITECTURE.md) — full system docs with diagrams
Expand Down
Loading