feat: Add Orthrus documentation for Remote Socket Proxy Agent and its configuration

This commit is contained in:
GitHub Actions
2025-12-07 04:35:44 +00:00
parent 502bc24b8c
commit ecf60b08e0

149
docs/issues/orthrus.md Normal file
View File

@@ -0,0 +1,149 @@
# Orthrus: Remote Socket Proxy Agent
## 1. Overview
**Orthrus** is a lightweight, standalone agent designed to run on remote servers. Named after the brother of Cerberus, its job is to guard the remote resource and securely transport it back to Charon.
It eliminates the need for SSH tunneling or complex port forwarding by utilizing the tunneling protocols managed by Hecate.
## 2. Operational Logic
Orthrus operates in **Reverse Mode**. It does not listen on a public port. Instead, it dials *out* to the tunneling network to connect with Charon.
### 2.1 Core Functions
1. **Docker Socket Proxy:** Securely proxies the remote server's `/var/run/docker.sock` so Charon can auto-discover containers on the remote host.
2. **Service Proxy:** Proxies specific localhost ports (e.g., a database on port 5432) over the tunnel.
## 3. Technical Implementation
### 3.1 Tech Stack
* **Language:** Go (Golang)
* **Base Image:** `scratch` or `alpine` (Goal: < 20MB image size)
### 3.2 Configuration (Environment Variables)
Orthrus is configured entirely via Environment Variables for easy Docker Compose deployment.
| Variable | Description |
| :--- | :--- |
| `ORTHRUS_NAME` | Unique identifier for this agent (e.g., `vps-london-01`) |
| `ORTHRUS_MODE` | `socket` (Docker Socket) or `port` (Specific Port) |
| `CHARON_LINK` | The IP/DNS of the main Charon server (e.g., `100.x.y.z:8080` or `charon.example.com`) |
| `AUTH_KEY` | A shared secret or JWT generated by Charon to authorize this agent |
### 3.3 External Connectivity
**Orthrus does NOT manage VPNs or network tunnels internally.**
It relies entirely on the host operating system for network connectivity.
1. **User Responsibility**: The user must ensure the host running Orthrus can reach the `CHARON_LINK` address.
2. **VPNs**: If you are using Tailscale, WireGuard, or ZeroTier, you must install and configure the VPN client on the **Host OS** (or a sidecar container). Orthrus simply dials the IP provided in `CHARON_LINK`.
3. **Reverse Mode**: Orthrus initiates the connection. Charon waits for the incoming handshake. This means you do not need to open inbound ports on the Orthrus side, but Charon must be reachable.
### 3.4 The "Leash" Protocol (Communication)
Orthrus communicates with Charon via a custom gRPC stream or WebSocket called "The Leash."
1. **Handshake**: Orthrus connects to `Charon:InternalIP`.
2. **Auth**: Orthrus presents the `AUTH_KEY`.
3. **Registration**: Orthrus tells Charon: *"I have access to Docker Network X and Port Y."*
4. **Tunneling**: Charon requests a resource; Orthrus pipes the data securely over "The Leash."
## 4. Deployment Example (Docker Compose)
```yaml
services:
orthrus:
image: wikid82/orthrus:latest
container_name: orthrus-agent
restart: always
environment:
- ORTHRUS_NAME=remote-media-server
- CHARON_LINK=100.x.y.z:8080
- AUTH_KEY=ch_xxxxx_secret
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# No ports required!
```
## 5. Security Considerations
* **Read-Only Socket**: By default, Orthrus mounts the Docker socket as Read-Only to prevent Charon (or a compromised Charon) from destroying the remote server.
* **Mutual TLS (mTLS)**: All communication between Charon and Orthrus should be encrypted via mTLS if not running inside an encrypted VPN (like Tailscale).
## 6. Implementation Details
### 6.1 Communication Architecture
Orthrus uses a **Reverse Tunnel** architecture established via **WebSockets** with **Yamux** multiplexing.
1. **Transport**: Secure WebSocket (`wss://`) initiates the connection from Orthrus to Charon. This bypasses inbound firewall rules on the remote network.
2. **Multiplexing**: [Yamux](https://github.com/hashicorp/yamux) is used over the WebSocket stream to create multiple logical channels.
* **Control Channel (Stream ID 0)**: Handles heartbeats, configuration updates, and command signals.
* **Data Channels (Stream ID > 0)**: Ephemeral streams created for each proxied request (e.g., a single HTTP request to the Docker socket or a TCP connection to a database).
### 6.2 Authentication & Security
* **Token-Based Handshake**: The `AUTH_KEY` is passed in the `Authorization` header during the WebSocket Upgrade request.
* **mTLS (Mutual TLS)**:
* **Charon as CA**: Charon maintains an internal Certificate Authority.
* **Enrollment**: On first connect with a valid `AUTH_KEY`, Orthrus generates a private key and sends a CSR. Charon signs it and returns the certificate.
* **Rotation**: Orthrus monitors certificate expiry and initiates a renewal request over the Control Channel 24 hours before expiration.
* **Encryption**: All traffic is TLS 1.3 encrypted.
### 6.3 Docker Socket Proxying (The "Muzzle")
To prevent security risks, Orthrus does not blindly pipe traffic to `/var/run/docker.sock`. It implements an application-level filter (The "Muzzle"):
1. **Parser**: Intercepts HTTP requests destined for the socket.
2. **Allowlist**: Only permits safe methods/endpoints (e.g., `GET /v1.xx/containers/json`, `GET /v1.xx/info`).
3. **Blocking**: Rejects `POST`, `DELETE`, `PUT` requests (unless explicitly configured to allow specific actions like "Restart Container") with a `403 Forbidden`.
### 6.4 Heartbeat & Health
* **Mechanism**: Orthrus sends a custom "Ping" packet over the Control Channel every 5 seconds.
* **Timeout**: Charon expects a "Ping" within 10 seconds. If missed, the agent is marked `Offline`.
* **Reconnection**: Orthrus implements exponential backoff (1s, 2s, 4s... max 30s) to reconnect if the link is severed.
## 7. Protocol Specification ("The Leash")
### 7.1 Handshake
```http
GET /api/v1/orthrus/connect HTTP/1.1
Host: charon.example.com
Upgrade: websocket
Connection: Upgrade
Authorization: Bearer <AUTH_KEY>
X-Orthrus-Version: 1.0.0
X-Orthrus-ID: <ORTHRUS_NAME>
```
### 7.2 Message Types (Control Channel)
Messages are Protobuf-encoded for efficiency.
* `HEARTBEAT`: `{ timestamp: int64, load_avg: float, memory_usage: int }`
* `PROXY_REQUEST`: Sent by Charon to request a new stream. `{ stream_id: int, target_type: "docker"|"tcp", target_addr: "localhost:5432" }`
* `CONFIG_UPDATE`: Sent by Charon to update allowlists or rotation policies.
### 7.3 Data Flow
1. **Charon** receives a request for a remote container (e.g., user views logs).
2. **Charon** sends `PROXY_REQUEST` on Control Channel.
3. **Orthrus** accepts, opens a new Yamux stream.
4. **Orthrus** dials the local Docker socket.
5. **Orthrus** pipes the stream, applying "The Muzzle" filter in real-time.
## 8. Repository Structure (Monorepo)
Orthrus resides in the **same repository** as Charon to ensure protocol synchronization and simplified CI/CD.
### 8.1 Directory Layout
To maintain a lightweight footprint (< 20MB), Orthrus uses a separate Go module within the `agent/` directory. This prevents it from inheriting Charon's heavy backend dependencies (GORM, SQLite, etc.).
```text
/projects/Charon
├── go.work # Manages the workspace (includes ./backend and ./agent)
├── backend/ # The Main Server (Heavy)
│ ├── go.mod
│ └── ...
├── agent/ # Orthrus (Lightweight)
│ ├── go.mod # Separate dependencies (Standard Lib + Yamux)
│ ├── main.go
│ └── Dockerfile # Separate build process
└── protocol/ # Shared Definitions (Protobufs)
├── go.mod
└── leash.proto
```
### 8.2 Build Strategy
* **Charon**: Built from `backend/Dockerfile`.
* **Orthrus**: Built from `agent/Dockerfile`.
* **CI/CD**: A single GitHub Action workflow builds and pushes both images (`charon:latest` and `orthrus:latest`) synchronously.