erebus

A VPN client that runs entirely in user space — no root, no kernel tunnels, no global routing.

erebus speaks OpenVPN and IPsec/IKEv2 itself, builds its own IP packets, runs its own minimal TCP/IP stack, and hands you VPN access through an ordinary local HTTP proxy. It's a VPN agent, not a system-wide VPN.

Rootless No TUN/TAP OpenVPN static-key IPsec / IKEv2 UDP & TCP Inbound & outbound
Quick start View on GitHub

The pitchWhat is erebus?

Traditional VPN clients assume they own the machine: they create a kernel tun/tap interface, rewrite your routing table, and need root to do it. That model breaks in containers, CI runners, sandboxes, and other locked-down hosts.

erebus takes the opposite approach. It does all the VPN work — the OpenVPN protocol, IP packets, TCP state, encryption — inside a single user-space process. Nothing about the host's networking is touched. Connectivity is explicit and opt-in: you point a client at a local proxy, and only that traffic crosses the VPN.

Outbound

A local HTTP/1.x proxy forwards requests from local clients to resources living on the VPN.

Inbound

Port forwards expose a chosen local service back to VPN peers — bidirectional, still rootless.

InstallGrab the package

A Debian package (Debian/Ubuntu and derivatives) is attached to every GitHub Release. Download it and install:

sudo apt install ./erebus_*_amd64.deb

That gives you the erebus binary, the man page (man erebus), and an annotated config under /usr/share/doc/erebus/. Prefer to build it yourself? See the README.

Quick startFive lines of config

erebus is driven by a single INI file. A minimal outbound proxy over UDP:

[erebus]
client-ip = 10.8.0.2

[openvpn-server]
proto  = udp
host   = vpn.example.com
port   = 1194
secret = /etc/erebus/static.key
cipher = AES-256-CBC
auth   = SHA256

[proxy-out]
address = 127.0.0.1
port    = 11023

Run it, then use it like any other HTTP proxy:

$ erebus --config erebus.ini &
$ http_proxy=http://127.0.0.1:11023 curl http://10.8.0.1/

Want to expose a local service to the VPN too? Add a forward — a peer reaches your 127.0.0.1:8080 at 10.8.0.2:8080 over the tunnel:

[proxy-in]
web = 8080 127.0.0.1:8080

Full reference: the erebus(1) man page (make man) and the annotated default-config.ini.

How it worksEverything in user space

A conventional VPN leans on the kernel for routing and TCP. erebus replaces that whole column with its own code, so the only privilege it needs is the ability to open a socket.

local HTTP client            VPN peer
       │                        │
       ▼                        ▼
  HTTP proxy            accept & relay        ← outbound / inbound
       │                        │
       ▼                        ▼
   user-space TCP/IP stack  ◄───┘             ← keeps connections reliable
       │
       ▼
   IPv4 / TCP / ICMP packets                  ← built & parsed by hand
       │
       ▼
   OpenVPN encrypt + HMAC + packet-id         ← static-key (wire format v1)
       │
       ▼
   UDP datagram  /  TCP frame                 ← transport-agnostic
       │
       ▼
   OpenVPN server

The same minimal TCP stack serves both directions; outbound and inbound differ only in who opens the connection. The transport layer is genuinely abstract — UDP and TCP share all protocol logic.

Deeper dive, with the design rationale and the trade-offs behind it: doc/architecture.md.

Honest positioningWhen to reach for it — and when not to

Where it shines

  • Reaching an internal HTTP service from inside an unprivileged container
  • CI jobs that need a private VPN endpoint without NET_ADMIN
  • Sandboxed or multi-tenant hosts where editing global routes is off-limits
  • Exposing one local port to a VPN peer, opt-in
  • Quick back-and-forth requests where the responses aren't huge
  • Reading actual, legible code to understand how OpenVPN works

Where it's the wrong tool

  • A system-wide VPN that routes all host traffic
  • Moving large amounts of data fast — it's built for small, snappy requests, not bulk transfer
  • TLS-mode OpenVPN, certificates, or pushed config — not yet
  • A drop-in replacement for the full OpenVPN client
  • HTTPS CONNECT tunnelling or SOCKS — explicit HTTP/1.x only
  • Anything needing a complete, tuned TCP/IP stack

These aren't oversights — they're the other side of the rootless coin. Correctness, interoperability and clarity come first; performance is explicitly deferred. See the measured performance.

FAQQuestions you probably have

Does it really not need root?

Correct. erebus never creates a network interface, never edits a routing table, and needs no capabilities beyond opening an ordinary client socket. All VPN logic — protocol, IP packets, TCP state, crypto — lives in one user-space process.

Which OpenVPN modes are supported?

Static-key mode only (pre-shared key, also called wire format v1), over UDP or TCP. CBC ciphers — AES, ARIA and Camellia at 128/192/256 — and a wide range of HMAC digests. The cipher and auth values must match the server. TLS mode, certificates and the control channel are planned but not yet implemented.

Does it really do IPsec too?

Yes — an IKEv2 control plane and an ESP data plane that interoperate with strongSwan, all in the same rootless user space. Authentication is pre-shared key for now; the suite is MODP-2048 Diffie-Hellman with AES-256-CBC and HMAC-SHA-256. Since a rootless process can't send raw ESP, everything is UDP-encapsulated (NAT-T), and your VPN address is assigned by the server during the handshake. Certificate and EAP authentication are not implemented yet.

Why is it a proxy instead of a normal VPN connection?

Because there's no kernel interface to route into. Without a tun device, the way to hand VPN connectivity to a local app is through an explicit endpoint — a local HTTP proxy for outbound, port forwards for inbound. That explicitness is a feature: only the traffic you point at erebus crosses the VPN.

How fast is it?

Fast enough for what it's built for — quick, small requests — and deliberately not tuned for moving large amounts of data. To keep the code simple, it sends data one piece at a time and waits for each piece to be confirmed before sending the next, so big downloads run several times slower than a normal proxy. Numbers and methodology are in stress-results.md.

Can it proxy HTTPS?

Not yet. The outbound proxy speaks plain HTTP/1.x; there's no HTTPS CONNECT tunnelling, SOCKS, or TLS-to-backend. Those are future work.

What's it written in?

Common Lisp (SBCL). The codebase is intentionally small and readable — implementing the protocols incrementally in user-space code is part of the point.

Is it production-ready?

No — it's an early-stage, experimental project. It prioritizes correctness and clarity over completeness, and the roadmap is explicit about what's deferred. Treat it as a capable tool for specific constrained-environment use cases, not a hardened VPN client.

Go deeperDocumentation