connet
is a peer-to-peer reverse proxy for NAT traversal. It is inspired by ngrok, frp, rathole and others.
connet
helps expose a service running on a device to another device on the internet. Unlike the others,
the connet
client runs on both the device that exposes the service (called destination
in connet's terms)
and the device that wants to access the service (called source
). This means that communication between connet
clients is never public and visible to the rest of the internet, and in many cases peers can communicate directly.
Status
connet
is currently alpha software. We expect some issues and its APIs are subject to change.
- Peer-to-peer communication Because you run the
connet
client on both thedestination
and thesource
, the server is only used for sharing configuration. In many cases clients can communicate directly, which enables better privacy and performance. - Relay support There are cases when clients are unable to find a path to communicate directly. In such cases, they can use a relay server to maintain connectivity.
- Security Everything is private, encrypted with TLS. Public server and client certificates are exchanges between peers
and are required and verified to establish connectivity. Clients and relays need to present a mandatory token when communicating
with the control server, allowing tight control over who can use
connet
. - Embeddable In case you want
connet
running as part of another (golang) program (as opposed to a separate executable),connet
has a well defined api for running both the client and the server.
flowchart LR
subgraph Server
C(Control Server)
R(Relay Server)
C -.->|Relay Info| R
end
subgraph Client Source
S[Source] -->|Exchange Direct and Relay Info| C(Control Server)
SC(Client) -..-> S
end
subgraph Client Destination
D[Destination] -->|Exchange Direct and Relay Info| C(Control Server)
D -..-> DC(Server)
end
S ---|Direct Connection| D
S -->|Relay Connection| R
R -->|Relay Connection| D
For all communication connet
uses the QUIC protocol, which is build on top of UDP.
Latest builds of connet
can be acquired from our releases page.
We also provide docker images,
check our docker section to see how you can use them.
If you are using NixOS, check also the NixOS section.
To get started with connet
, you'll need 3 devices:
- Server which your clients can communicate with. In most cases, this server will have a public IP and be directly accessible by clients. A VPS instance at one of the many cloud providers goes a long way here.
- Device
D
that has thedestination
service you want to connect to, running at port3000
. - Device
S
(akasource
) which you want to connect to the service, at port8000
.
In the setup above, start connet server --config server.toml
with the following server.toml
:
[server]
tokens = ["client-d-token", "client-s-token"]
cert-file = "cert.pem"
key-file = "key.pem"
To run a connet
server, you'll need a TLS certificate. You have a few options to create such certificate:
- Recommended use an ACME client to provision one for you. We've had good experiences running lego.
- Buy a TLS certificate from a Certificate Authority like verisign, namecheap, etc.
- Use a self-signed TLS certificate, an option most appropriate for testing.
To create a self-signed certificate, you can use openssl. Alternatively, you can use a tool like
minica. When using self-signed certificate, you'll need your clients (and relays)
trusting the server's certificate. Copying the certificate (or CA) public key to the clients and using server-cas
configuration option is the easiest way to achieve this.
Then, on device D
run connet --config client-d.toml
with the following client-d.toml
:
[client]
token = "client-d-token"
server-addr = "SERVER_IP:19190"
server-cas = "cert.pem"
[client.destinations.serviceA]
addr = ":3000"
On device S
run connet --config client-s.toml
with the following client-s.toml
:
[client]
token = "client-s-token"
server-addr = "SERVER_IP:19190"
server-cas = "cert.pem"
[client.sources.serviceA]
addr = ":8000"
You can use both a toml config file as well as command line when running connet
. If you use both a config file and
command line options, the latter takes precence, overriding any config file options. For simplicity, command line options
only support a single destination
or source
configuration.
To run a client, use connet --config client-config.toml
command. Here is the full client client-config.toml
configuration spec:
[client]
token = "client-token-1" # the token which the client uses to authenticate against the control server
token-file = "path/to/relay/token" # a file that contains the token, one of token or token-file is required
server-addr = "localhost:19190" # the control server address to connect to
server-cas = "path/to/cert.pem" # the control server certificate
direct-addr = ":19192" # at what address this client listens for direct connections, defaults to :19192
status-addr = "127.0.0.1:19182" # at what address this client listens for status connections, disabled unless set
[client.destinations.serviceX]
addr = "localhost:3000" # where this destination connects to, required
route = "any" # what kind of routes to use, `any` will use both `direct` and `relay`
[client.destinations.serviceY]
addr = "192.168.1.100:8000" # multiple destinations can be defined, they are matched by name at the server
route = "direct" # force only direct communication between clients
[client.sources.serviceX] # matches destinations.serviceX
addr = ":8000" # the address at which to listen for incoming connections to be forwarded
route = "relay" # the kind of route to use
[client.sources.serviceY] # both sources and destinations can be defined in a single file
addr = ":8001" # again, mulitple sources can be defined
route = "direct" # force only direct communication between clients, even if other end allows any
To run a server (e.g. running both control and a relay server), use connet server --config server-config.toml
command.
Here is the full server server-config.toml
configuration specification:
[server]
addr = ":19190" # the address at which the control server will listen for connections, defaults to :19190
cert-file = "path/to/cert.pem" # the server certificate file, in pem format
key-file = "path/to/key.pem" # the server certificate private key file
tokens = ["client-token-1", "client-token-n"] # set of recognized client tokens
tokens-file = "path/to/client/tokens" # a file that contains a list of client tokens, one token per line
# one of tokens or tokens-file is required
relay-addr = ":19191" # the address at which the relay will listen for connectsion, defaults to :19191
relay-hostname = "localhost" # the public hostname (e.g. domain, ip address) which will be advertised to clients, defaults to localhost
status-addr = "127.0.0.1:19180" # at what address the server listens for status connections, disabled unless set
store-dir = "path/to/server-store" # where does this server persist runtime information, defaults to a /tmp subdirectory
[server.ip-restriction] # defines restriction applicable for all client tokens, checked before verifying the token
allow-cidrs = [] # set of networks in CIDR format, to allow client connetctions from
deny-cidrs = [] # set of networks in CIDR format, to deny client connetctions from
[[server.token-ip-restriction]] # defines restriction per client token, if specified must match the number of client tokens
allow-cidrs = [] # set of networks in CIDR format, to allow token client connetctions from
deny-cidrs = [] # set of networks in CIDR format, to deny token client connetctions from
To run a control server, use connet control --config control-config.toml
command. Here is the full control server
control-config.toml
configuration specification:
[control]
addr = ":19190" # the address at which the control server will listen for connections, defaults to :19190
cert-file = "path/to/cert.pem" # the server certificate file, in pem format
key-file = "path/to/key.pem" # the server certificate private key file
client-tokens = ["client-token-1", "client-token-n"] # set of recognized client tokens
client-tokens-file = "path/to/client/tokens" # a file that contains a list of client tokens, one token per line
# one of client-tokens or client-tokens-file is required
relay-tokens = ["relay-token-1", "relay-token-n"] # set of recognized relay tokens
relay-tokens-file = "path/to/relay/token" # a file that contains a list of relay tokens, one token per line
# one of relay-tokens or relay-tokens-file is necessary when connecting relays
status-addr = "127.0.0.1:19180" # at what address the control server listens for status connections, disabled unless set
store-dir = "path/to/control-store" # where does this control server persist runtime information, defaults to a /tmp subdirectory
[control.client-ip-restriction] # defines restriction applicable for all client tokens, checked before verifying the token
allow-cidrs = [] # set of networks in CIDR format, to allow client connetctions from
deny-cidrs = [] # set of networks in CIDR format, to deny client connetctions from
[[control.client-token-ip-restriction]] # defines restriction per client token, if specified must match the number of client tokens
allow-cidrs = [] # set of networks in CIDR format, to allow client connetctions from
deny-cidrs = [] # set of networks in CIDR format, to deny client connetctions from
[control.relay-ip-restriction] # defines restriction applicable for all relay tokens, checked before verifying the token
allow-cidrs = [] # set of networks in CIDR format, to allow relay connetctions from
deny-cidrs = [] # set of networks in CIDR format, to deny relay connetctions from
[[control.relay-token-ip-restriction]] # defines restriction per relay token, if specified must match the number of relay tokens
allow-cidrs = [] # set of networks in CIDR format, to allow relay connetctions from
deny-cidrs = [] # set of networks in CIDR format, to deny relay connetctions from
To run a relay server, use connet relay --config relay-config.toml
command. Here is the full relay server
relay-config.toml
configuration specification:
[relay]
token = "relay-token-1" # the token which the relay server uses to authenticate against the control server
token-file = "path/to/relay/token" # a file that contains the token, one of token or token-file is required
addr = ":19191" # the address at which the relay will listen for connectsion, defaults to :19191
hostname = "localhost" # the public hostname (e.g. domain, ip address) which will be advertised to clients, defaults to localhost
control-addr = "localhost:19190" # the control server address to connect to, defaults to localhost:19191
control-cas = "path/to/ca/file.pem" # the public certificate root of the control server, no default, required when using self-signed certs
status-addr = "127.0.0.1:19181" # at what address the relay server listens for status connections, disabled unless set
store-dir = "path/to/relay-store" # where does this relay persist runtime information, defaults to a /tmp subdirectory
You can restrict clients and relays to connect only from specific IPs using different ip-restriction
options.
They accept allow/deny list of strings in CIDR format, as defined by RFC 4632 and
RFC 4291, for example (to restrict the set of client IPs that can connect to the server):
[server.ip-restriction]
allow-cidrs = ["192.0.2.0/24"]
deny-cidrs = ["198.51.100.0/24"]
If these options are specified, an IP will be rejected if:
- it matches any of the CIDRs in the
deny-cidrs
list - it matches none of the CIDRs in the
allow-cidrs
list. If theallow-cidrs
list is empty, the IP will not be rejected.
connet
servers (both control and relay servers) store runtime state on the file system. If you don't explicitly specify
store-dir
, they will use a new subdirectory in /tmp
by default, which means that every time they restart they'll loose
any state and identity. To prevent this, you can specify an explicit store-dir
location, which can be reused between runs.
At the root of the config file, you can configure logging (connet
uses slog internally):
log-level = "info" # supports debug, info, warn, error, defaults to info
log-format = "text" # supports text and json, defaults to text
On some systems, if you might see the following line in the logs:
failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 7168 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details.
In which case, we recommend visiting the wiki page and applying the recommended changes.
connet
contains NixOS modules that you can use for running:
- client via client-module.nix
- server via server-module.nix
- control server via control-server-module.nix
- relay server via relay-server-module.nix
To configure the client as a service:
# configuration.nix
{ config, pkgs, ... }:
let
connet-repo = builtins.fetchGit {
url = "https://github.com/connet-dev/connet";
ref = "main";
};
in
{
imports = [
# ...
"${connet-repo}/nix/client-module.nix"
];
# ...
services.connet = {
enable = true;
tokenFile = "/run/keys/connet.token";
serverAddr = "localhost:19190";
sources.example.addr = ":9000";
};
}
To configure the client as a service:
# flake.nix
{
inputs = {
# ...
connet.url = "github.com/connet-dev/connet";
};
outputs = { connet, ... }: {
nixosConfigurations.example = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
# ...
connet.nixosModules.default
{
services.connet = {
enable = true;
tokenFile = "/run/keys/connet.token";
serverAddr = "localhost:19190";
sources.example.addr = ":9000";
};
}
];
};
};
}
Docker images are available at ghcr.io and you can pull them via:
docker pull ghcr.io/connet-dev/connet:latest
To run the client you can use something like:
docker run -p 19192:19192 -p 9000:9000 connet \
--server-addr "localhost:19190" --token "CLIENT_TOKEN" \
--src-name "example" --src-addr ":9000"
Or if you are using a config file on your system:
docker run -p 19192:19192 -p 9000:9000 \
--mount "type=bind,source=/path/to/config,target=/data" \
connet --config "/data/config.toml"
TBD
If you want to use connet
, but you don't want to run the server yourself, we have also built a hosted service
at connet.dev. It is free when clients connect directly, builds upon the open source components
by adding account management and it is one of the easiest way to start.
- UDP support
- Use quic-go tracer, instead of ping (and duration estimation)
- Stateless reset key for the server