Skip to content

Orthrus: Remote Socket Proxy Agent #369

@github-actions

Description

@github-actions

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)

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 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

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.).

/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.

9. Packaging & Install Options

Orthrus should be distributed in multiple formats so users can choose one that fits their environment and security posture.

9.1 Supported Distribution Formats

  • Docker / Docker Compose: easiest for container-based hosts.
  • Standalone static binary (recommended): small, copy to /usr/local/bin, run via systemd.
  • Deb / RPM packages: for managed installs via apt/yum.
  • Homebrew formula: for macOS / Linuxbrew users.
  • Tarball with installer: for offline or custom installs.
  • Kubernetes DaemonSet: for fleet deployment inside clusters.

9.2 Quick Install Snippets (copyable)

  1. Docker Compose
version: "3.8"
services:
  orthrus:
    image: wikid82/orthrus:latest
    restart: always
    environment:
      - ORTHRUS_NAME=remote-media-server
      - CHARON_LINK=100.x.y.z:8080
      - AUTH_KEY=REPLACE_WITH_AUTH_KEY
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
  1. Standalone binary + systemd (Linux)
# download and install
curl -L https://example.com/orthrus/latest/orthrus-linux-amd64 -o /usr/local/bin/orthrus
chmod +x /usr/local/bin/orthrus

# systemd unit (/etc/systemd/system/orthrus.service)
cat > /etc/systemd/system/orthrus.service <<'EOF'
[Unit]
Description=Orthrus agent
After=network.target

[Service]
Environment=ORTHRUS_NAME=remote-media-server
Environment=CHARON_LINK=100.x.y.z:8080
Environment=AUTH_KEY=REPLACE_WITH_AUTH_KEY
ExecStart=/usr/local/bin/orthrus
Restart=on-failure
User=root

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now orthrus
  1. Tarball + install script
curl -L -o orthrus.tar.gz https://example.com/orthrus/vX.Y.Z/orthrus-linux-amd64.tar.gz
sha256sum orthrus.tar.gz  # compare with UI-provided hash
tar -xzf orthrus.tar.gz -C /usr/local/bin
chmod +x /usr/local/bin/orthrus
# then use the systemd unit above
  1. Homebrew (macOS / Linuxbrew)
brew tap wikid82/charon
brew install orthrus
  1. Kubernetes DaemonSet

Provide a DaemonSet YAML referencing the orthrus image and the required env vars (AUTH_KEY, CHARON_LINK), optionally mounting the Docker socket or using hostNetworking.

9.3 Security & UX Notes

  • Provide SHA256 checksums and GPG signatures for binary downloads.
  • Avoid recommending curl | sh; prefer explicit steps and checksum verification.
  • The Hecate UI should present each snippet as a selectable tab with a copy button and an inline checksum.
  • Offer a one-click AUTH_KEY regenerate action in the UI and mark old keys revoked.

Auto-created from orthrus.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions