Secure Access with the Mesh VPN
The mesh is an optional private network between your machine and your EasyRunner web hosts, built on WireGuard. Its main purpose is to let you lock SSH down to that private network so port 22 is no longer reachable from the public internet, while HTTP/HTTPS stay open for your apps.
Optional and advanced
The mesh is not required for the normal install → deploy path. Reach for it when you want to reduce your servers' SSH attack surface. The mesh CLI is currently developed and tested on macOS.
What Problem It Solves
A server with SSH open to the internet is a constant target for port scanners, brute-force attempts, and credential stuffing. The mesh lets you move SSH off the public internet entirely: after you lock a server, sshd only answers on the private WireGuard interface, and the public port 22 shows as filtered to the outside world.
How It Works
EasyRunner builds a hub-and-spoke WireGuard network. Your machine (the CLI) is the hub; each server you join is a spoke.
┌──────────────────────────┐
│ Your machine (CLI hub) │ 10.99.0.254
└────────────┬─────────────┘
│ WireGuard (UDP 51820)
┌────────────┼─────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ server │ │ server │ │ server │
│10.99.0.1│ │10.99.0.2│ │10.99.0.3│
└─────────┘ └─────────┘ └─────────┘
| Property | Value |
|---|---|
| Topology | Hub-and-spoke; your machine is the hub, servers are spokes |
| Mesh subnet | 10.99.0.0/24 |
| Your machine's mesh IP | 10.99.0.254 |
| Server mesh IPs | Assigned from 10.99.0.1 upward as servers join |
| Transport | WireGuard over UDP port 51820 |
| Local interface | wg0 |
Once a server is locked, EasyRunner automatically routes its SSH commands (er app deploy, er app logs, etc.) over the server's mesh IP. You do not change how you run commands.
Prerequisites
The mesh needs the wireguard-tools package on your machine. Check and install prerequisites with:
- Reports whether CLI prerequisites such as
wireguard-toolsare installed. - Attempts to install any that are missing.
On macOS you can also install it directly with brew install wireguard-tools. Mesh commands require sudo because they bring network interfaces up and down; the CLI prompts for it when needed.
1. Initialize the Mesh
Run this once on your machine. It generates the CLI's WireGuard keypair and brings up the local wg0 interface.
The output shows the mesh subnet, the listen port, and your machine's mesh IP (10.99.0.254).
2. Join a Server
Joining a registered server does several things for you:
- Installs and starts WireGuard on the server.
- Adds the server as a peer on your local
wg0interface and assigns it a mesh IP. - Opens UDP
51820on the server's host firewall and on the cloud-provider firewall. For Hetzner, EasyRunner reconciles the cloud firewall automatically (best-effort; it warns if it can't, for example when no Hetzner link is present).
Backfilling firewall rules
If a server was initialized by an older CLI version and is missing mesh-related firewall rules, run er server reapply-firewall my-server.
3. Check Status and Health
- Lists the mesh subnet, listen port, and every peer with its mesh IP, endpoint, and key. Your machine (the hub) is shown first.
- Diagnoses mesh health:
wireguard-toolsinstalled, mesh initialized, interface up, stored config in sync with the live interface, and — per peer — that a route is installed and a handshake was seen recently.
Confirm the tunnel is healthy with er mesh status and that you can reach the server over its mesh IP before locking SSH down.
4. Lock SSH to the Mesh
After this, the server's firewall only accepts SSH on the wg0 interface. Public port 22 becomes unreachable from the internet; ports 80 and 443 stay open. EasyRunner routes its own SSH for the server over the mesh from this point on.
Safety: the dead-man's switch
er mesh lock arms a timer on the server that automatically reverts the firewall if no WireGuard handshake is seen within roughly 150 seconds. A broken tunnel therefore re-opens public SSH on its own rather than locking you out permanently. The threshold is wider than WireGuard's natural rekey interval, so a healthy but idle tunnel still passes.
Use er mesh lock my-server --yes to skip the confirmation prompt in scripts.
5. Reopen Public SSH
Use this when you need public SSH back — for example before rotating keys or decommissioning the mesh on that server. While the lockdown is in force this command reaches the server over the mesh IP, so a healthy tunnel is required.
If the Tunnel Breaks While Locked
The dead-man's switch from er mesh lock reverts the firewall automatically if no handshake is observed within the freshness window. If you need access sooner, open a root shell through your VPS provider's web console / VNC and run:
Then SSH back in over the public IP and run er mesh unlock my-server from the CLI to clear the local lockdown state.
Day-to-Day
macOS does not persist the WireGuard interface across reboots. If er mesh doctor reports the interface is down, bring it back up:
Bring the local interface down without losing the mesh config or keys:
Command Summary
| Task | Command |
|---|---|
| Initialize the local mesh | er mesh init |
| Bring the local interface up / down | er mesh up / er mesh down |
| Join / remove a server | er mesh join <server> / er mesh leave <server> |
| Show status and peers | er mesh status |
| Diagnose mesh health | er mesh doctor |
| Lock / unlock SSH | er mesh lock <server> / er mesh unlock <server> |
See the CLI Command Reference for the full command list.