Nexus Proxy - Reference Backend Client
This application is a reference implementation of a backend service client for the Nexus Proxy Server.
It demonstrates how a local service can securely connect to the Nexus mesh and have traffic relayed to it for multiple hostnames and ports simultaneously. This project is structured as a library (client package) with a simple command-line wrapper (cmd/client/main.go), making it easy to integrate into your own applications.
How it Works
- Configuration Driven: The client reads a
config.yaml file to determine which services to expose.
- Connects Outbound: For each configured service, it initiates a WebSocket connection to a Nexus Proxy node.
- Presents Token: It sends a complete, pre-signed JSON Web Token (JWT) provided in the config. The client itself has no access to the JWT secret.
- Listens for Commands: It listens for simple JSON control messages from the proxy, primarily
connect and disconnect commands.
- Active Health Checks: The client actively monitors its connections. If a connection is idle for too long, it sends a
ping_client message to the Nexus Proxy to verify the connection is still alive on the proxy side. If no pong_client is received, the client cleans up the local connection, preventing zombies.
- Host-Aware Relaying:
portMappings can now route by port, hostname, or single-label wildcard (e.g. *.preview.example.com), allowing one backend connection to front multiple virtual hosts.
Configuration
The client is configured via a config.yaml file.
See config.example.yaml.
Key fields:
-
hostnames: list every FQDN (or *.example.com wildcard) this backend will serve. These must line up with the JWT claims you provision.
-
portMappings: maps the public Nexus port to a local target using a structured form that supports hostname-specific overrides:
portMappings:
443:
default: "localhost:8443"
hosts:
api.example.com: "localhost:9443"
"*.preview.example.com": "localhost:10443"
Exact hostnames take precedence over wildcards, and wildcards only match a single label (e.g. a.preview.example.com).
Example Usage
- Create your
config.yaml file.
- Run the client:
go run ./cmd/client -config config.yaml
Library Mode
Embedding the client inside another Go service is as simple as constructing a
client.ClientBackendConfig and passing optional behaviour overrides. The most
useful hook is client.WithConnectHandler, which lets you choose the local
destination for each inbound request (or even serve it with an in-memory
net.Conn). Returning client.ErrNoRoute falls back to the static
portMappings declared in the config.
router := func(ctx context.Context, req client.ConnectRequest) (net.Conn, error) {
if strings.HasSuffix(req.Hostname, ".pclo.example.com") {
return net.Dial("tcp", "localhost:5080")
}
// Defer to the default YAML-configured mapping.
return nil, client.ErrNoRoute
}
c := client.New(cfg, client.WithConnectHandler(router))
go c.Start(ctx)
This makes it straightforward to implement per-tenant routing rules, connect to
ephemeral processes, or hand back a net.Pipe() for fully in-memory handlers.