GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - Yundera/mesh-router-tunnel: MeshRouter: Seamlessly route domain requests to containers across networks using ENS, or custom names, secured by Wireguard VPN.

BranchesTags
Open more actions menu

Latest commit

 

History

61 Commits
61 Commits

Folders and files

NameName
Last commit message
Last commit date
.github/workflows
.github/workflows
 
 
dev-scripts/windows
dev-scripts/windows
 
 
nginx
nginx
 
 
src
src
 
 
.dockerignore
.dockerignore
 
 
.env
.env
 
 
.gitattributes
.gitattributes
 
 
.gitignore
.gitignore
 
 
.npmrc
.npmrc
 
 
Dockerfile
Dockerfile
 
 
LICENSE
LICENSE
 
 
README.MD
README.MD
 
 
entrypoint.sh
entrypoint.sh
 
 
package.json
package.json
 
 
pnpm-lock.yaml
pnpm-lock.yaml
 
 
pnpm-workspace.yaml
pnpm-workspace.yaml
 
 
tsconfig.json
tsconfig.json
 
 

Repository files navigation

Build and Push Docker Image

MeshRouter Tunnel

Sponsor

Thanks for sponsoring this project

Yundera : yundera.com - Easy to use cloud server for open source container applications. NSL.SH : nsl.sh - free domain for opensouce project.

Sibling project

CasaIMG - providing easy management of docker images and compatible with mesh router.

Architecture

MeshRouter is a WireGuard-based VPN tunneling solution that enables secure routing of traffic between a provider (public server) and requesters (private PCS instances).

┌─────────────────────────────────────────────────────────────────────┐
│                            PCS Instance                              │
│                                                                      │
│  ┌──────────────┐    ┌─────────────┐    ┌─────────────────────────┐ │
│  │ mesh-router  │───▶│   Caddy     │───▶│  Services (with labels) │ │
│  │  (port 80)   │    │  (port 80)  │    │  - casaos:8080          │ │
│  │              │    │             │    │  - app1:3000            │ │
│  │  WireGuard   │    │  Docker     │    │  - app2:8080            │ │
│  │  Tunnel ▲    │    │  Labels     │    │                         │ │
│  └──────────────┘    └─────────────┘    └─────────────────────────┘ │
│              │                                                       │
└──────────────│───────────────────────────────────────────────────────┘
               │
               ▼
        ┌─────────────┐
        │  Provider   │
        │  (nsl.sh)   │
        └─────────────┘

How It Works

  1. Mesh Router (Requester): Establishes WireGuard tunnel to provider and forwards all incoming traffic to Caddy
  2. Caddy: Uses caddy-docker-proxy to discover services via Docker labels and routes traffic accordingly
  3. Services: Containers with Caddy labels define their own routing rules

Traffic Flow

  1. External Request: User requests https://app.user.nsl.sh
  2. Provider: nsl.sh receives request, looks up user's VPN IP, forwards via WireGuard
  3. Mesh Router: Receives request on port 80, forwards to Caddy
  4. Caddy: Matches request against container labels, proxies to appropriate service
  5. Service: Container handles request and returns response

HTTP/HTTPS Scheme Preservation

The tunnel preserves the original request scheme (HTTP vs HTTPS) throughout the entire flow:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                         SCHEME PRESERVATION FLOW                                 │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                  │
│   ENTRY (Gateway)           TUNNEL                    EXIT (Requester → Caddy)  │
│        │                       │                              │                  │
│   HTTP request                 │                              │                  │
│   (port 80)                    │                              │                  │
│        │                       │      WireGuard               │    http://       │
│        ├──────────────────────►│◄════════════════════════════►│───────────────►  │
│        │ X-Forwarded-Proto:    │      (encrypted)             │    caddy:80      │
│        │ http                  │                              │                  │
│        │                       │                              │                  │
│   HTTPS request                │                              │                  │
│   (port 443, SSL terminated)   │                              │                  │
│        │                       │      WireGuard               │    https://      │
│        ├──────────────────────►│◄════════════════════════════►│───────────────►  │
│        │ X-Forwarded-Proto:    │      (encrypted)             │    caddy:443     │
│        │ https                 │                              │                  │
│                                                                                  │
└─────────────────────────────────────────────────────────────────────────────────┘

How it works:

  1. Gateway terminates SSL for HTTPS requests and sets X-Forwarded-Proto: https header
  2. Tunnel entry (provider) receives HTTP on port 80 and preserves the X-Forwarded-Proto header
  3. Traffic flows through WireGuard as HTTP (WireGuard provides network-layer encryption)
  4. Tunnel exit (requester) reads X-Forwarded-Proto and speaks the correct protocol to Caddy:
    • X-Forwarded-Proto: http → http://caddy:80
    • X-Forwarded-Proto: https → https://caddy:443 (nginx speaks TLS to Caddy)

Why this design:

  • Caddy can keep standard HTTP/HTTPS configuration without special proxy trust settings
  • The tunnel handles protocol translation transparently
  • WireGuard encrypts all internal traffic regardless of application-layer protocol
  • Scheme-aware routing ensures correct behavior for redirects, secure cookies, and HSTS

Domain Routing

The mesh-router operates in two modes:

  • Provider Mode: Runs on public servers, accepts incoming connections and routes traffic to registered requesters via WireGuard
  • Requester Mode: Runs on PCS instances, establishes VPN tunnel to provider and forwards traffic to Caddy for local routing

Domain Format

<name> : Name proposed by the user on registration (if accepted by the provider) <domain> : The domain of the provider (eg nsl.sh)

Full format: <service>.<name>.<domain> or <service>-<name>.<domain>

Example: nextcloud.mynas.nsl.sh or nextcloud-mynas.nsl.sh

Requester Configuration

Environment Variables

Variable Default Description
PROVIDER - Provider connection string: <url>,<userId>,<signature> (for single provider setup)
ROUTING_TARGET_HOST caddy Target container hostname for traffic forwarding
ROUTING_TARGET_PORT_HTTPS 443 Target container port for HTTPS traffic
ROUTING_TARGET_PORT_HTTP 80 Target container port for HTTP traffic

YAML Configuration (Multi-Provider)

For advanced setups with multiple providers, use a YAML configuration file mounted at /app/config/config.yml:

providers:
  - provider: https://nsl.sh,userId,signature
    defaultService: casaos
  - provider: http://custom-provider.com,userId2
    defaultService: myapp
    services:
      myapp:
        defaultPort: '3000'

The configuration file is watched for changes and will automatically reconnect to providers when modified.

Connection Health Monitoring

The requester automatically monitors WireGuard handshakes every 5 minutes. If a connection becomes stale (no handshake within 5 minutes), it will:

  1. Log the connection issue
  2. Tear down the WireGuard interface
  3. Re-register with the provider
  4. Re-establish the tunnel

This ensures resilient connections without manual intervention.

Docker Compose Example

services:
  mesh-router:
    image: ghcr.io/yundera/mesh-router-tunnel:latest
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
    environment:
      - PROVIDER=https://nsl.sh,<userId>,<signature>
      - ROUTING_TARGET_HOST=caddy
    networks:
      - pcs
    depends_on:
      - caddy

  caddy:
    image: lucaslorentz/caddy-docker-proxy:ci-alpine
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - CADDY_INGRESS_NETWORKS=pcs
    networks:
      - pcs

  casaos:
    image: casa-img:latest
    labels:
      caddy: ":80"
      caddy.reverse_proxy: "{{upstreams 8080}}"
    networks:
      - pcs

networks:
  pcs:
    driver: bridge
    name: pcs

Caddy Docker Labels

Labels define how Caddy routes traffic to each container:

labels:
  caddy: ":80"                              # Match all requests on port 80
  caddy.reverse_proxy: "{{upstreams 8080}}" # Proxy to container port 8080

{{upstreams <port>}} Template

This template function:

  • Resolves to the container's IP address on the ingress network
  • Uses the specified port as the upstream port
  • Example: {{upstreams 8080}} → 172.20.0.2:8080

Routing Examples

Route all traffic to a service:

labels:
  caddy: ":80"
  caddy.reverse_proxy: "{{upstreams 3000}}"

Route by hostname:

labels:
  caddy: "myapp.example.com"
  caddy.reverse_proxy: "{{upstreams 8080}}"

Route with path matching:

labels:
  caddy: ":80"
  caddy.0_matcher: "path /api/*"
  caddy.0_reverse_proxy: "{{upstreams 8080}}"

Provider Configuration

Provider mode is used on public servers to accept incoming VPN connections.

Environment Variables

Variable Default Description
PROVIDER_ANNONCE_DOMAIN - Domain to announce (e.g., nsl.sh) - presence of this variable enables provider mode
AUTH_API_URL - URL for user authentication API (optional)
VPN_IP_RANGE 10.77.0.0/16 IP range for VPN clients
VPN_PORT 51820 WireGuard listen port
VPN_ENDPOINT_ANNOUNCE - Public endpoint for VPN connections (IP or hostname)
SSL false Enable HTTPS with self-signed certificate on port 443
TUNNEL_FORWARD_FROM - Comma-separated IPs/CIDRs (the gateways on this host) allowed to reach the peers' tcp/443 through wg0 (routed tunnel); masqueraded to the provider's address
PROVIDER_API_TLS_PORT - Also serve the registration API (/api/) over TLS (self-signed) on this port, for a provider without a reverse proxy in front, e.g. on a second gateway's host. Port 80 is unchanged. Requesters reach it through the backend's TUNNEL_PROVIDERS list (https://<host>:<port>)

Provider API Endpoints

The provider exposes an internal API on port 3000 (used by Nginx for routing):

Endpoint Method Description
/api/ping GET Health check - returns ok
/api/get_ip/<host> GET Resolves domain to backend VPN IP for routing
/api/register POST Peer registration endpoint for requesters

Registration Request Body:

{
  "userId": "username",
  "vpnPublicKey": "WireGuard public key",
  "authToken": "signature or auth token"
}

Registration Response:

{
  "wgConfig": { "interface": {...}, "peers": [...] },
  "serverIp": "10.77.0.1",
  "serverDomain": "nsl.sh",
  "domainName": "username",
  "domain": "username.nsl.sh"
}

Docker Compose Example

services:
  routing:
    image: ghcr.io/yundera/mesh-router-tunnel:latest
    ports:
      - "80:80"
      - "443:443"
      - "51820:51820/udp"
    environment:
      - PROVIDER_ANNONCE_DOMAIN=domain.com
      - VPN_ENDPOINT_ANNOUNCE=x.x.x.x  # Use direct IP (not behind Cloudflare)
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
    volumes:
      - ./config.lua:/etc/nginx/lua/config.lua  # optional

Cloudflare

It is recommended to set up Cloudflare for TLS management and DDoS protection. The provider container will use a self-signed certificate for end-to-end encryption.

Alternative

Tailscale Funnel Cloudflare Tunnels https://github.com/hintjen/selfhosted-gateway : Docker native self-hosted alternative to Cloudflare Tunnels, Tailscale Funnel, ngrok and others.

Development

To start development, use the scripts in ./dev-scripts/windows/simple folder, which contains everything needed to run mesh-router in a basic environment. For detailed instructions, see simple example.

About

MeshRouter: Seamlessly route domain requests to containers across networks using ENS, or custom names, secured by Wireguard VPN.

nsl.sh

Topics

gatewayself-hostedvpn

Resources

Readme
MIT license

Security policy

Security policy
Activity
Custom properties

Stars

13 stars

Watchers

3 watching

Forks

0 forks
Report repository

Releases

Packages

Used by

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.