From ecf60b08e036affa7d93bf509687d68b7a5419b5 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 7 Dec 2025 04:35:44 +0000 Subject: [PATCH] feat: Add Orthrus documentation for Remote Socket Proxy Agent and its configuration --- docs/issues/orthrus.md | 149 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 docs/issues/orthrus.md diff --git a/docs/issues/orthrus.md b/docs/issues/orthrus.md new file mode 100644 index 00000000..e54604de --- /dev/null +++ b/docs/issues/orthrus.md @@ -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 +X-Orthrus-Version: 1.0.0 +X-Orthrus-ID: +``` + +### 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.