Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ wstunnel-go server --tls-certificate cert.pem --tls-private-key key.pem --tls-cl

`wstunnel-go` can be configured via command-line flags, environment variables, or a YAML configuration file.

For a comprehensive YAML guide with annotated client/server examples and option descriptions, see [`docs/CONFIG.md`](docs/CONFIG.md).

### CLI Flags

#### Global Flags
Expand All @@ -181,7 +183,7 @@ wstunnel-go server --tls-certificate cert.pem --tls-private-key key.pem --tls-cl
- `-R, --remote-to-local`: Define a remote-to-local (reverse) tunnel.
- `--http-upgrade-path-prefix`: HTTP upgrade path prefix (default: "v1").
- `--jwt-secret`: Shared secret used to sign tunnel JWTs.
- `--http-upgrade-credentials`: Basic auth credentials for upgrade request.
- `--http-upgrade-credentials`: Raw `Authorization` header value for the upgrade request (for Basic auth, pass the full `Basic ...` value).

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README describes --http-upgrade-credentials as a raw Authorization header value, but the CLI flag help text still says “Basic auth credentials for upgrade request” (cmd/wstunnel-go/main.go). Please update the CLI help string or adjust the README line so the docs don’t contradict the actual --help output.

Suggested change
- `--http-upgrade-credentials`: Raw `Authorization` header value for the upgrade request (for Basic auth, pass the full `Basic ...` value).
- `--http-upgrade-credentials`: Basic auth credentials for upgrade request.

Copilot uses AI. Check for mistakes.
- `-H, --header`: Custom HTTP headers for upgrade request.
- `--http-headers-file`: File containing custom HTTP headers.
- `--tls-verify-certificate`: Enable/disable TLS cert verification.
Expand Down Expand Up @@ -222,6 +224,8 @@ server:
restrict_config: /etc/wstunnel/rules.yaml
```

The full set of YAML keys, tunnel examples, and detailed descriptions for both client and server config lives in [`docs/CONFIG.md`](docs/CONFIG.md).

## API Reference (Library Usage)

`wstunnel-go` is built with a modular design, making it easy to use as a library.
Expand Down
241 changes: 241 additions & 0 deletions docs/CONFIG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
# Configuration Guide

`wstunnel-go` supports CLI flags and YAML config files. This guide focuses on the YAML format used by `--config`.

## File structure

Configuration files use a top-level wrapper with `mode`, plus either a `client` or `server` section:

```yaml
mode: client
log_lvl: INFO
no_color: false

client:
remote_addr: wss://tunnel.example.com
local_to_remote:
- "tcp://127.0.0.1:8443:internal.service:443"
```

Top-level fields:

- `mode`: `client` or `server`. Must match the populated section.
- `log_lvl`: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, or `OFF`.
- `no_color`: disables colored log output.

Notes:

- Duration values use Go duration syntax such as `10s`, `5m`, or `1h`.
- Tunnel definitions in `local_to_remote` and `remote_to_local` use the same syntax as the CLI `-L` and `-R` flags.
- The config loader maps directly onto the current Go structs. Unknown keys are ignored by YAML parsing, so typos can silently do nothing.
- `mode` is used when invoking the binary with only `--config`. The current runtime does not yet apply `log_lvl` or `no_color` from YAML automatically; use CLI flags for those.

## Client configuration

Example client config with common options:

```yaml
mode: client
log_lvl: INFO
no_color: false

client:
remote_addr: wss://tunnel.example.com
http_upgrade_path_prefix: v1
mode: rust
jwt_secret: ""

http_headers:
X-Env: production
X-Cluster: edge-a
http_headers_file: /etc/wstunnel-go/client-headers.txt
http_upgrade_credentials: "Basic dXNlcjpwYXNzd29yZA=="

websocket_ping_frequency: 30s
websocket_mask_frame: false

tls_verify_certificate: true
tls_certificate: /etc/wstunnel-go/client.crt
tls_private_key: /etc/wstunnel-go/client.key
tls_sni_override: tunnel.example.com
tls_sni_disable: false
tls_ech_enable: false

socket_so_mark: 0
connection_min_idle: 2
connection_retry_max_backoff: 5m
reverse_tunnel_connection_retry_max_backoff: 1m

http_proxy: http://proxy.example.net:3128
http_proxy_login: ""
http_proxy_password: ""

dns_resolver:
- dns://1.1.1.1
- dns://8.8.8.8
dns_resolver_prefer_ipv4: false

local_to_remote:
- "tcp://127.0.0.1:8443:internal.service:443"
- "udp://127.0.0.1:1053:1.1.1.1:53?timeout_sec=10"
- "socks5://127.0.0.1:1080?login=admin&password=secret"
- "http://127.0.0.1:8080?login=proxy&password=secret"
- "unix:///tmp/wstunnel.sock:/var/run/docker.sock"

remote_to_local:
- "tcp://0.0.0.0:2222:127.0.0.1:22"
- "unix://wstunnel.sock:/var/run/app.sock"
```

### Client options

- `remote_addr`: server URL. Use `ws://` / `wss://` for websocket transport and `http://` / `https://` for HTTP/2.
- `http_upgrade_path_prefix`: path prefix used for websocket upgrade / HTTP tunnel endpoints. Default is usually `v1`.
- `mode`: transport framing mode. Use `rust` for compatibility with the original Rust implementation, or `ws` for strict RFC 6455 websocket mode.
- `jwt_secret`: optional shared secret used to sign client tunnel JWTs. If unset, the client preserves legacy Rust-compatible behavior.
- `http_headers`: map of extra request headers added to the server request.
- `http_headers_file`: file with `Header: value` lines. If set, values from the file override duplicate keys in `http_headers`.
- `http_upgrade_credentials`: raw `Authorization` header value sent to the server. For HTTP Basic auth, provide the full header payload such as `Basic dXNlcjpwYXNzd29yZA==`.
- `websocket_ping_frequency`: websocket ping interval.
- `websocket_mask_frame`: enable masking on websocket frames.

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

websocket_mask_frame is documented as an active option, but the client/server configs don’t currently use this field anywhere (it’s only defined/parsed). Either implement masking behavior or document that this key is currently ignored/no-op.

Suggested change
- `websocket_mask_frame`: enable masking on websocket frames.
- `websocket_mask_frame`: currently ignored/no-op; kept for forward compatibility and may be used by future versions.

Copilot uses AI. Check for mistakes.
- `tls_verify_certificate`: verify server certificates when using TLS.
- `tls_certificate`: client certificate path for mTLS.
- `tls_private_key`: private key matching `tls_certificate`.
- `tls_sni_override`: override the SNI hostname sent during TLS handshake.
- `tls_sni_disable`: disable SNI.
- `tls_ech_enable`: enable ECH if supported by the runtime/server path.
Comment on lines +105 to +106

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tls_sni_disable and tls_ech_enable are described as working options here, but the client TLS config code only applies tls_sni_override and does not reference these fields. Please either implement these behaviors or mark them as currently unsupported/no-op in the YAML guide.

Suggested change
- `tls_sni_disable`: disable SNI.
- `tls_ech_enable`: enable ECH if supported by the runtime/server path.
- `tls_sni_disable`: currently a no-op; SNI is always enabled. Reserved for future use.
- `tls_ech_enable`: currently a no-op; ECH is not yet implemented in the client TLS stack.

Copilot uses AI. Check for mistakes.
- `socket_so_mark`: Linux `SO_MARK` value for outbound connections.
- `connection_min_idle`: keep that many idle client-to-server transport connections open.
- `connection_retry_max_backoff`: max backoff while retrying regular client connections.
- `reverse_tunnel_connection_retry_max_backoff`: max backoff while retrying reverse-tunnel control connections.
- `http_proxy`: upstream HTTP proxy URL used to reach the wstunnel server.
- `http_proxy_login`: optional proxy username override.
- `http_proxy_password`: optional proxy password override.
- `dns_resolver`: explicit resolvers to use.
- `dns_resolver_prefer_ipv4`: prefer IPv4 answers when both families are available.
Comment on lines +111 to +115

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

http_proxy / http_proxy_login / http_proxy_password and dns_resolver / dns_resolver_prefer_ipv4 are documented as functional, but there’s no code that uses these config fields to affect dialing or name resolution (they’re only parsed into the config struct). Consider marking them as not implemented yet, or add the missing plumbing so these keys actually take effect.

Suggested change
- `http_proxy`: upstream HTTP proxy URL used to reach the wstunnel server.
- `http_proxy_login`: optional proxy username override.
- `http_proxy_password`: optional proxy password override.
- `dns_resolver`: explicit resolvers to use.
- `dns_resolver_prefer_ipv4`: prefer IPv4 answers when both families are available.
- `http_proxy`: (not yet implemented) reserved for configuring an upstream HTTP proxy URL used to reach the wstunnel server.
- `http_proxy_login`: (not yet implemented) optional proxy username override.
- `http_proxy_password`: (not yet implemented) optional proxy password override.
- `dns_resolver`: (not yet implemented) reserved for configuring explicit DNS resolvers to use.
- `dns_resolver_prefer_ipv4`: (not yet implemented) reserved for preferring IPv4 answers when both families are available.

Copilot uses AI. Check for mistakes.
- `local_to_remote`: list of forward tunnels started locally.
- `remote_to_local`: list of reverse tunnels exposed on the server and forwarded back to the client.

### Client tunnel examples

- `tcp://127.0.0.1:8080:example.com:80`: expose local TCP port `8080`, forward to `example.com:80`.
- `udp://127.0.0.1:5353:1.1.1.1:53?timeout_sec=10`: forward UDP with a 10 second idle timeout.
- `socks5://127.0.0.1:1080`: local SOCKS5 proxy.
- `socks5://127.0.0.1:1080?login=admin&password=secret`: local SOCKS5 proxy requiring credentials.
- `http://127.0.0.1:8080`: local HTTP CONNECT proxy.
- `http://127.0.0.1:8080?login=proxy&password=secret`: local HTTP CONNECT proxy requiring credentials.
- `unix:///tmp/wstunnel.sock:/var/run/docker.sock`: local unix socket listener forwarding to a unix socket target.
- `tcp://0.0.0.0:2222:127.0.0.1:22` under `remote_to_local`: reverse TCP tunnel.

### Current client limitations

- Reverse UDP, reverse SOCKS5, and reverse HTTP proxy are not implemented in this Go version yet. Do not place those tunnel types in `remote_to_local`.

## Server configuration

Example server config with TLS, restrictions, and compatibility options:

```yaml
mode: server
log_lvl: INFO
no_color: false

server:
listen_addr: wss://0.0.0.0:8443
http_upgrade_path_prefix: v1
mode: rust

jwt_secret: ""
insecure_no_jwt_validation: false

websocket_ping_frequency: 30s
websocket_mask_frame: false

socket_so_mark: 0
dns_resolver:
- dns://1.1.1.1
dns_resolver_prefer_ipv4: false

restrict_to:
- "127.0.0.1:22"
- "internal.service:443"
restrict_http_upgrade_path_prefix_list:
- v1
- edge
restrict_config: /etc/wstunnel-go/restrictions.yaml

tls_certificate: /etc/wstunnel-go/server.crt
tls_private_key: /etc/wstunnel-go/server.key
tls_client_ca_certs: /etc/wstunnel-go/clients-ca.pem

http_proxy: ""
http_proxy_login: ""
http_proxy_password: ""

remote_to_local_server_idle_timeout: 3m
```

### Server options

- `listen_addr`: server bind URL. Examples: `ws://0.0.0.0:8080`, `wss://0.0.0.0:8443`, `http://0.0.0.0:8080`, `https://0.0.0.0:8443`.
- `http_upgrade_path_prefix`: path prefix used for incoming tunnel requests.
- `mode`: websocket compatibility mode. Use `rust` to match Rust framing/behavior or `ws` for RFC 6455 websocket handling.
- `jwt_secret`: optional verification secret for tunnel JWTs. In `mode: ws`, signatures are verified when this is set. In `mode: rust`, tokens are still parsed in Rust-compatible mode and are not cryptographically verified.
- `insecure_no_jwt_validation`: in `mode: ws`, allow Rust-compatible parsing of HS256 tunnel JWTs without signature verification when interoperability matters more than strict validation. It does not make `mode: rust` any stricter; that mode remains compatibility-oriented.
- `websocket_ping_frequency`: websocket ping interval sent by the server.
- `websocket_mask_frame`: enable websocket frame masking.

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Server websocket_mask_frame is documented as configurable, but server.Config.WebsocketMaskFrame is not referenced anywhere in the server implementation (only defined/parsed). Update the guide to note it’s currently a no-op, or wire it into the websocket handling if it’s intended to be supported.

Suggested change
- `websocket_mask_frame`: enable websocket frame masking.
- `websocket_mask_frame`: reserved for future use; currently a no-op and does not affect websocket frame masking.

Copilot uses AI. Check for mistakes.
- `socket_so_mark`: Linux `SO_MARK` value for outbound connections made by the server.
- `dns_resolver`: explicit resolvers the server should use.
- `dns_resolver_prefer_ipv4`: prefer IPv4 DNS results when both families are present.
- `restrict_to`: allow-list of destinations that clients may request.
- `restrict_http_upgrade_path_prefix_list`: allow-list of accepted path prefixes.
- `restrict_config`: path to restriction rules file.
- `tls_certificate`: PEM certificate used by the server.
- `tls_private_key`: PEM private key matching `tls_certificate`.
- `tls_client_ca_certs`: CA bundle for client-certificate verification (mTLS).
- `http_proxy`: upstream proxy the server should use for outbound requests when relevant.
- `http_proxy_login`: optional proxy username override.
- `http_proxy_password`: optional proxy password override.
- `remote_to_local_server_idle_timeout`: how long an idle reverse listener may stay bound without new client use.

## Restriction config example

The server also supports a separate restrictions file referenced by `restrict_config`. A minimal example looks like this:

```yaml
restrictions:
- name: allow-ssh
match:
- Any
allow:
- !Tunnel
protocol: [tcp]
host: "^127\\.0\\.0\\.1$"
port: ["22"]

- name: allow-edge-prefix
match:
- PathPrefix: "^/edge/events$"
allow:
- !Tunnel
protocol: [tcp]
host: ".*\\.internal\\.example\\.com"
port: ["443"]

- name: allow-reverse-tcp
match:
- Any
allow:
- !ReverseTunnel
protocol: [reverse_tcp]
port: ["20000..20100"]
```
Comment on lines +229 to +232

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the restrictions example, protocol: [reverse_tcp] won’t match what the server actually compares against (it uses getProtoName() which yields ReverseTcp / ReverseUnix / ReverseHttp, etc., and only does case-insensitive matching). Update the docs example to use ReverseTcp/reverseTcp (no underscore) or adjust the implementation to accept reverse_tcp aliases.

Copilot uses AI. Check for mistakes.

`match` entries accept either `Any` or regex-based fields such as `PathPrefix`, `Authorization`, and `ClientCommonName`.

`allow` entries must use tagged YAML objects:

- `!Tunnel` for regular forward tunnels (`tcp`, `udp`, `socks5`, `http_proxy`, `unix`)

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This list says http_proxy is a valid !Tunnel protocol, but restriction matching uses getProtoName() which returns Http for HTTP proxy tunnels, so http_proxy will never match (even with EqualFold). The doc should list http (or Http) here to reflect actual accepted values.

Suggested change
- `!Tunnel` for regular forward tunnels (`tcp`, `udp`, `socks5`, `http_proxy`, `unix`)
- `!Tunnel` for regular forward tunnels (`tcp`, `udp`, `socks5`, `http`, `unix`)

Copilot uses AI. Check for mistakes.
- `!ReverseTunnel` for reverse listeners (`reverse_tcp`, `reverse_unix`, and related reverse tunnel protocol names)

Use `restrict_config` when simple `restrict_to` lists are not expressive enough.
Loading