flexaccess.dev / ezvpn

ezvpn

The easy-setup VPN for reaching private networks.

  • IP layer
  • needs root
  • CLI
  • Linux
  • macOS
  • Windows
  • iOS
Access level
IP routing — whole subnets, any protocol
Privileges
Root / Administrator (creates the network interface)
Tunnel modes
Split tunnel; full tunnel on CLI and Windows (experimental)
Authentication
Per-client Ed25519 keys, authorized-keys style
Server exposure
No public IP, no open inbound port
Topology
Client ↔ server only — no client-to-client, no site-to-site

how it works

One encrypted connection, dialled by identity

Only the prefixes you route go through the tunnel; the rest of your traffic never touches it.

ezvpn creates a virtual network interface and routes IP packets — IPv4, IPv6, or both — through an encrypted QUIC connection. It runs as a split tunnel, carrying only the private prefixes you ask for; a full tunnel is available on the CLI and the Windows app but is still experimental. Clients dial the server by its stable endpoint identity, so the server needs no public IP and no open inbound port: hole punching finds a direct path through NAT, and an encrypted relay carries traffic when it can't.

There is no VPN subnet to plan, either. The server assigns client addresses dynamically, so nothing has to be kept collision-free by hand. A typical deployment is a small ezvpn server inside a private network — an AWS VPC, a homelab — that clients join temporarily to reach private resources.

Access control is a per-client Ed25519 keypair in the shared flexaccess-keys format: each client generates its own key, and the server keeps the public keys in an ssh-style authorized-keys file. No certificates, no PKI, no shared secret to rotate.

features

What ezvpn gives you

Reachability

  • No inbound ports

    Clients dial a stable endpoint identity — no port forwarding, no dynamic DNS, and it works from behind CGNAT.

  • Bring your own relays

    Point both ends at two or more custom relays and public infrastructure is out of the picture. A server that loses its home relay re-homes on another in place — same identity, same sockets, nothing torn down.

  • Waits out outages

    Auto-reconnect with exponential backoff, from the very first attempt: a server that's down or not up yet is the ordinary case, not an exit. Status shows failed attempts, the last error, and when the next try is due.

Addressing

  • No subnet planning

    Client VPN addresses are assigned dynamically by the server, so there are no static IPs to keep collision-free.

  • Dual stack

    Run the tunnel over IPv4, IPv6, or both at once — clients receive an address in each family the server enables. IPv6-only mode works but is still experimental.

  • Client isolation by design

    Clients reach the server's network, never each other: inter-client packets are dropped in userspace before they touch the TUN device. Nothing to firewall, no stale-IP bookkeeping.

  • Forwarding rules can be permanent

    Reaching hosts beyond the server itself is ordinary OS routing: enable IP forwarding and add a MASQUERADE rule once. The rules are inert while the tunnel is down, so there are no PostUp / PostDown hooks to script.

Routing & DNS

  • Won't swallow your own LAN

    A split-tunnel route that overlaps the network you're sitting on is refused at connect, and again if that network appears mid-session — on desktop and iOS alike — so the tunnel never cuts off the gateway that carries it.

  • The underlay stays off the tunnel

    The server publishes its own transport addresses and the client pins host routes for any a routed prefix would capture, so even a full tunnel can't deadlock on itself. Running on top of another VPN? --exclude-direct-path keeps the tunnel off that overlay.

  • Split DNS on iOS

    The iOS app can send DNS for chosen match domains through the tunnel, so private names resolve on the phone without capturing all of its DNS. On desktop and macOS, DNS stays with the OS.

Security & performance

  • Keys, not certificates

    Each client authenticates with its own Ed25519 keypair; the server lists the public keys it accepts in an ssh-style authorized-keys file. Generate keys once with flexaccess-keys — no PKI, no shared token.

  • End-to-end encrypted

    Traffic rides QUIC with TLS 1.3 from client to server. Relays that carry it can't decrypt it.

  • No tuning knobs

    MTU 1280, Cubic, fixed windows: WireGuard-style protocol constants on both ends. Linux gets automatic TUN GSO offload; the macOS CLI data path was recently reworked for roughly 3× the upload and 2× the download throughput.

quick start

Up and running in four steps

The CLI flow, condensed from the README. You'll need ezvpn on both ends and flexaccess-keys on each client.

  1. Generate keys

    The server gets an identity key, which is what clients dial. Each client gets its own Ed25519 auth key.

    # on the server
    ezvpn generate-server-key --output ./vpn-server.key
    
    # on each client
    flexaccess-keys generate-auth-key "alice laptop" -o client.key
    flexaccess-keys show-auth-key --private-key-file client.key
    #   → ed25519-pub:… alice laptop
  2. Write the server config

    Paste each client's ed25519-pub line into ./authorized_keys, one per line, and pick a VPN network. That is the whole config.

    vpn_server.toml
    role = "vpnserver"
    
    [network]
    network = "10.0.0.0/24"
    
    [auth]
    authorized_keys_file = "./authorized_keys"
    
    [iroh]
    secret_file = "./vpn-server.key"
  3. Start the server

    It prints its endpoint ID on startup. No port to open, no IP to publish.

    sudo ezvpn server start -c vpn_server.toml

    To reach hosts beyond the server itself, enable net.ipv4.ip_forward and add a MASQUERADE rule for 10.0.0.0/24 on the server's outbound interface. The rules are harmless while the tunnel is down, so they can simply stay.

  4. Connect a client

    Dial the server by its endpoint ID and route the private prefixes you need. Leave --route off to reach only the server itself.

    sudo ezvpn client start \
      --server-node-id <SERVER_ENDPOINT_ID> \
      --auth-key-file ./client.key \
      --route 192.168.1.0/24
    
    ping 10.0.0.1   # the server's VPN address

    On a Mac, iPhone, or Windows PC, the native app does the same: generate a key in-app, paste its public half into authorized_keys, add a profile with the server's endpoint ID, connect.

install

Get the CLI

Linux / macOS
curl -sSL https://flexaccessdev.github.io/ezvpn/install.sh | sudo bash
Windows (elevated PowerShell)
irm https://flexaccessdev.github.io/ezvpn/install.ps1 | iex

The CLI also needs wintun.dll from wintun.net next to ezvpn.exe. The Windows app bundles it.

The installer downloads the latest release binary and verifies its checksum. Prebuilt binaries cover Linux amd64/arm64, Apple Silicon macOS, and Windows amd64. Client keys are generated with flexaccess-keys. Set up flexaccess-keys

apps & repos

Pick your platform

  • CLI · client and serverezvpn

    The core — command-line client and server for Linux / macOS / Windows.

    one-line installer, or cargo build
  • macOS / iOS appezvpn-apple

    Native SwiftUI client with a packet-tunnel extension: dual-stack split tunnel (no full tunnel), multiple saved profiles, keys generated in-app and kept in the Keychain, split DNS on iOS, menu-bar controls on macOS. On a network change it disconnects rather than migrating the session — reconnect manually. The macOS build is a signed, notarized drag-to-Applications .dmg that needs no Apple developer account to run; iOS is built from source under your own team.

    macOS: signed .dmg · iOS: build from source
  • Windows appezvpn-windows

    Native WinUI 3 app driving the ezvpn core through its C FFI: dual-stack split tunnel, multiple profiles, Ed25519 keys kept in Windows Credential Manager, live connection-path view. Closing the window hides it to the system tray with the tunnel still up. It runs elevated, so expect a UAC prompt at launch. The MSI bundles wintun.dll; it is unsigned and scoped for personal use on x64.

    unsigned .msi (x64)

good for

Where it fits

  • Private cloud subnets — instances in private or egress-only VPC subnets
  • Home-hosted servers behind dynamic IPs, NAT, or CGNAT
  • Temporary split-tunnel access without standing VPN infrastructure
  • Reaching a homelab or VPC from an iPhone or a Mac with the native app

keep in mind

Where it doesn't

A server-centred tunnel, on purpose

Every client talks only to the server gateway — never to another client — which is exactly what lets client addresses stay dynamic and conflict-free. The server and the desktop client do one thing, tunneling; firewall, NAT, and DNS stay with the OS.

Not a mesh, not site-to-site, not an anonymity tool

ezvpn gives clients the server's network and nothing else: no client-to-client traffic, and no bridging of two LANs. If you want every device to have a stable IP so peers can address each other, that's Tailscale's job; for a permanent link between two subnets, use WireGuard. And because relays can see connection metadata when they're involved, ezvpn is not for anonymity — only the payload is guaranteed private.

Pre-1.0: keep both ends on the same release

While ezvpn is in the 0.0.x series there is no backward compatibility between versions. Upgrade clients and servers together, and refresh configs from the current examples when you do. If an upgraded deployment still can't connect, regenerate the server identity key as a fallback — that changes the server's endpoint ID, so update every client profile with the new one.