Categorygithub.com/refraction-networking/water
modulepackage
0.7.0-alpha
Repository: https://github.com/refraction-networking/water.git
Documentation: pkg.go.dev

# README

W.A.T.E.R.: WebAssembly Transport Executables Runtime

Apache-2.0 FOSSA CI Go Doc

WATER-go provides a Go runtime for WebAssembly Transport Modules(WATM) as a pluggable application-layer transport protocol provider. It is designed to be highly portable and lightweight, allowing for rapidly deployable pluggable transports. While other pluggable transport implementations require a fresh client deployment (and app-store review) to update their protocol WATER allows dynamic delivery of new transports in real time over the network or out-of-band.

WATER wasm transport

To build a WATM in Go, please refer to watm for examples and helper libraries interfacing Pluggable Transports-like interfaces. Official Go compiler is currently not supported until further notice.

You can contact one of developers personally via [email protected], or simply opening an issue.

The Rust implementation of the runtime library and information about writing, building, and using WebAssembly Transport Modules(WATM) from Rust can be found in water-rs.

Cite our work

If you quoted or used our work in your own project/paper/research, please cite our paper Just add WATER: WebAssembly-based Circumvention Transports, which is published in the proceedings of Free and Open Communications on the Internet (FOCI) in 2024 issue 1, pages 22-28.

BibTeX
@inproceedings{water-foci24,
    author = {Erik Chi and Gaukas Wang and J. Alex Halderman and Eric Wustrow and Jack Wampler},
    title = {Just add {WATER}: {WebAssembly}-based Circumvention Transports},
    booktitle = {Free and Open Communications on the Internet},
    publisher = {},
    year = {2024},
    url = {https://www.petsymposium.org/foci/2024/foci-2024-0003.pdf},
}

Be Water

Empty your mind, be formless, shapeless, like water. If you put water into a cup, it becomes the cup. You put water into a bottle and it becomes the bottle. You put it in a teapot, it becomes the teapot. Now, water can flow or it can crash. Be water, my friend.

-- Bruce Lee

License

This project is licensed under Apache 2.0 license.

Contents

This repo contains a Go package water, which implements the runtime library used to interact with .wasm WebAssembly Transport Modules(WATM).

Usage

Based on WASI Snapshot Preview 1 (wasip1), currently W.A.T.E.R. provides a set of net-like APIs via Dialer, Listener and Relay.

Versioning

W.A.T.E.R. is designed to be future-proof with the automated multi-version WebAssembly Transport Module(WATM) support. In order to minimize the size of compiled application binaries importing water, the support for each WATM version is implemented in separate sub-packages and by default none will be enabled. The developer MUST manually enable each version to be supported by importing the corresponding package:

import (
	// ...

	_ "github.com/refraction-networking/water/transport/v0"

	// ...
)

Otherwise, it is possible that the W.A.T.E.R. runtime cannot determine the version of the WATM and therefore fail to select the corresponding runtime:

panic: failed to listen: water: listener version not found

Customizable Version

TODO: add documentations for customizable WATM version.

Components

Dialer

A Dialer connects to a remote address and returns a net.Conn to the caller if the connection can be successfully established. The net.Conn then provides tunnelled read and write to the remote endpoint with the WebAssembly module wrapping / encrypting / transforming the traffic.

Dialer is used to encapsulate the caller's connection into an outbound, transport-wrapped connection.

	wasm, _ := os.ReadFile("./examples/v0/plain/plain.wasm")

	config := &water.Config{
		TransportModuleBin: wasm,
	}

	dialer, _ := water.NewDialerWithContext(context.Background(), config)
	conn, _ := dialer.DialContext(context.Background(),"tcp", remoteAddr)
	// ...

Listener

A Listener listens on a local address for incoming connections which it Accept()s, returning a net.Conn for the caller to handle. The WebAssembly Module is responsible for the initial accpt allowing it to implement both the server side handshake as well as any unwrap / decrypt /reform operations required to validate and re-assemble the stream. The net.Conn returned provides the normalized stream, and allows the caller to write back to the client with the WebAssembly module encoding / encrypting / transforming traffic to be obfuscated on the wire on the way to the remote client.

Listener is used to decapsulate incoming transport-wrapped connections for the caller to handle, managing the tunnel obfuscation once a connection is established.

	wasm, _ := os.ReadFile("./examples/v0/plain/plain.wasm")

	config := &water.Config{
		TransportModuleBin: wasm,
	}

	lis, _ := config.ListenContext(context.Background(), "tcp", localAddr)
	defer lis.Close()
	log.Printf("Listening on %s", lis.Addr().String())

	for {
		conn, err := lis.Accept()
		handleConn(conn)
	}

	// ...

Relay

A Relay combines the role of Dialer and Listener. It listens on a local address Accept()-ing incoming connections and Dial()-ing the remote endpoints on establishment. The connecions are tunneled through the WebAssembly Transport Module allowing the module to handshake, encode, transform, pad, etc. without any caller interaction. The internal Relay manages the incoming connections as well as the associated outgoing connectons.

Relay is a managed many-to-many handler for incoming connections that uses the WebAssemble module to tunnel traffic.

	wasm, _ := os.ReadFile("./examples/v0/plain/plain.wasm")

	config := &water.Config{
		TransportModuleBin: wasm,
	}

	relay, _ := water.NewRelayWithContext(context.Background(), config)

	relay.ListenAndRelayTo("tcp", localAddr, "tcp", remoteAddr) // blocking

Example

See examples for example usecase of W.A.T.E.R. API, including Dialer, Listener and Relay.

Submodules

watm has its own licensing policy, please refer to watm for more information.

Cross-platform Support

W.A.T.E.R. is designed to be cross-platform (and cross-architecture). Currently, it supports the following platforms:

TargetCompiles?Tests Pass?
linux/amd64
linux/arm64
linux/riscv64
macos/amd64
macos/arm64
windows/amd64
windows/arm64
others

Acknowledgments

  • We thank GitHub.com for providing GitHub Actions runners for all targets below:

  • We thank FlyCI.net for providing GitHub Actions runners on macos/arm64 (Apple M1) in the past. (We switched to GitHub's macos-14 runner as of Jan 31 2024)

We are currently actively looking for a CI provider for more target platforms. Please reach out and let us know if you would like to help.

# Packages

No description provided by the author
No description provided by the author
No description provided by the author

# Functions

NewCore creates a new Core with the given config.
NewCoreWithContext creates a new Core with the given context and config.
NewDialer creates a new [Dialer] from the given [Config].
NewDialerWithContext creates a new [Dialer] from the [Config] with the given [context.Context].
No description provided by the author
NewListener creates a new [Listener] from the given [Config].
NewListenerWithContext creates a new [Listener] from the [Config] with the given [context.Context].
NewRelay creates a new [Relay] from the given [Config].
NewRelayWithContext creates a new [Relay] from the [Config] with the given [context.Context].
NewWazeroModuleConfigFactory creates a new WazeroModuleConfigFactory.
NewWazeroRuntimeConfigFactory creates a new WazeroRuntimeConfigFactory.
RegisterWATMDialer is a function used by Transport Module drivers (e.g., `transport/v0`) to register a function that spawns a new [Dialer] from a given [Config] for a specific version.
No description provided by the author
RegisterWATMListener is a function used by Transport Module drivers (e.g., `transport/v0`) to register a function that spawns a new [Listener] from a given [Config] for a specific version.
RegisterWATMRelay is a function used by Transport Module drivers (e.g., `transport/v0`) to register a function that spawns a new [Relay] from a given [Config] for a specific version.
SetDefaultLogger sets the logger to be used by the package if no logger is specifically configured for each component.
SetDefaultLogHandler sets the handler to be used by the package if no logger is specifically configured for each component.
SetGlobalCompilationCache sets the global CompilationCache for the WebAssembly runtime.
TransportModuleConfigFromBytes creates a TransportModuleConfig from the given byte slice.
TransportModuleConfigFromFile creates a TransportModuleConfig from the given file path.

# Variables

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
RelayTo and ListenAndRelayTo may return this error if a relay was reused.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

Config defines the configuration for the WATER Dialer/Config interface.
UnimplementedConn is used to provide forward compatibility for implementations of Conn, such that if new methods are added to the interface, old implementations will not be required to implement each of them.
UnimplementedDialer is a Dialer that always returns errors.
UnimplementedFixedDialer is a FixedDialer that always returns errors.
UnimplementedListener is a Listener that always returns errors.
UnimplementedRelay is a Relay that always returns errors.
WazeroModuleConfigFactory is used to spawn wazero.ModuleConfig.
WazeroRuntimeConfigFactory is used to spawn wazero.RuntimeConfig.

# Interfaces

Conn is an abstracted connection interface which is expected to encapsulate a Core.
Core provides the low-level access to the WebAssembly runtime environment.
Dialer dials a remote network address upon caller calling Dial() and returns a net.Conn which is upgraded by the WebAssembly Transport Module.
FixedDialer acts like a dialer, despite the fact that the destination is managed by the WebAssembly Transport Module (WATM) instead of specified by the caller.
Listener listens on a local network address and upon caller calling Accept(), it accepts an incoming connection and returns the net.Conn upgraded by the WebAssembly Transport Module.
Relay listens on a local network address and handles requests on incoming connections by passing the incoming connection to the WebAssembly Transport Module and dial corresponding outbound connections to a pre-defined destination address.
TransportModuleConfig defines the configuration file used by the WebAssembly Transport Module.