Websocketunnel is a service that allows its clients to publicly expose specific HTTP services without publicly exposing the entire host. "Clients" connect to the websocktunnel service with a specific ID, authenticating with a signed JWT, and upgrade the connection to a websocket. "Viewers" then connect to the service using a path containing that ID. The viewer's connection is then proxied to the client via its websocket. The critical characteristic is that all connections are made to the websocktunnel service, so the clients need not be publicly addressible.
Configuration is via environment variables:
HOSTNAMEgives the hostname of the websocktunnel service itself. This is used to construct URLs for use by viewers.
ENVdefines the runtime environment; use
ENV=productionfor production deployments.
SYSLOG_ADDR(optional) defines a syslog server to which log messages will be sent in production
TASKCLUSTER_PROXY_SECRET_Bdefine two secrets, either of which may be used to sign valid JWTs. Either secret is accepted, supporting downtime-free rotation of secrets.
TLS_CERTIFICATE(both optional) define a TLS certificate that is used for the main HTTP service. If not given, the service will default to plain HTTP. This is not recommended for production usage!
PORTgives the port on which the HTTP server should run, defaulting to 443 (or if not using TLS, 80).
AUDIENCE(aud) claim identifies the recipients that the JWT is intended for. Use of this is OPTIONAL.
In non-production mode, the service logs its activities to stdout in a human-readable format.
The websocktunnel service supports incoming connections from clients and from viewers, as described here.
To establish a new client connection, make a GET request to the service's hostname with path
/ and the usual websocket upgrade headers, as well as:
Bearer <jwt>; see below
x-websocktunnel-idcontaining the client ID
The connection will be upgraded to a websocket connection.
The response will contain the header
x-websocktunnel-client-url giving the URL viewers can use to reach this client.
Clients can pass this URL, or URLs derived from it, to viewers.
Clients should not make assumptions about the form of this URL.
The client ID must be URL-safe, specifically matching
It is recommended to urlencode any string to meet this requirement.
The token included in the
Authorization header must be a JWT with the following claims:
tid-- clientID for the tunnel
iat-- issuance time (epoch timestamp)
exp-- expiration time
nbf-- not-before (set to some time before iat to allow clock skew)
aud-- audience claim (identifies the recipients)
It must use method
HS256 and be signed with either of the secrets in the service configuration.
It must be valid at the current time, and must not be valid for more than 31 days (specifically, the
exp claims must be less than 31 days apart).
tid claim must match the client ID exactly.
aud claim is optional,if set on server then must be present in JWT token and must match.
The protocol used within the websocket connection between a client and the websocktunnel service is beyond the scope of this document.
It is implemented by the
github.com/taskcluster/websocktunnel/client package uses this to implement the client side of the connection.
Viewers are given a client URL based on that provided to the cient as described above. All access to such URLs will be tunneled to the client as described above. If the client is not available, the viewer will get a 404 error response.
No special considerations are required to access viewer URLs: the intent is that any HTTP client can do so. Both "normal" HTTP requests and websocket connections are supported.
Note that viewer connections do not require any kind of authentication. That is entirely up to the client.
wst-client command implements a client that will connect to a websocktunnel service and proxy all connections to a specific local port.
It takes a JWT either on the command line or (to enable replacing tokens without losing connections) on stdin.
See the command's
--help output for details.
In many deployment scenarios, there will be thousands of idle client connections waiting for incoming viewer requests. This number of connections can easily overwhelm a server, even if the total traffic bandwidth does not. To cope with this situation, create multiple Websocktunnel instances, each with a different hostname, and configure clients to connect to a specific instance. How clients are assigned to instances is up to you, but keep in mind that clients may reconnect on connection failure, but if they do not reconnect to the same Websocktunnel instance, then the URL for that client will change.
This service is tested only with go1.10.
To hack on this service, install it into your GOPATH with
go get -u github.com/taskcluster/websocktunnel.
Run the tests with the usual
go test invocation (for example,
go test github.com/taskcluster/websocktunnel).
We use golangci to run lint checks.
You can install this into your own GOPATH to run the same checks locally with
If you see different results from those in CI runs on your pull request, check that you are running the same version as required in
To add, remove, or update dependencies, use dep.
Do not manually edit anything under the
This service is deployed from a Docker image containing only the single, statically-linked binary.
You can rebuild the docker image with
docker build ..
The image should be run with the environment variables described above, and the container's port exposed on the Docker host.
Use the same value for PORT as the port exposed on the host (typically 443), as the service will include that port in the URLs it generates.