tunneller

module
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 20, 2026 License: Apache-2.0

README

tunneller

tunneller is an HTTP reverse proxy that forwards requests to remote endpoints via SSH tunnels. Tunnels are established on demand and torn down automatically after a period of inactivity.

The target endpoint, SSH jump host, and related parameters can be supplied via the request URL, with defaults and restrictions configured at startup via command line flags.

Usage

tunneller [flags]
Flags
Flag Default Description
--addr :8080 Listen address
--endpoint.allow .* Allowed remote endpoints (regexp)
--endpoint.ca CA bundle to verify HTTPS connections to endpoints
--endpoint.headers.allow Accept, Accept-Encoding, Accept-Language, Authorization, Cache-Control, Connection, Content-Length, Content-Type, Cookie, Origin, Referer, Upgrade-Insecure-Requests, User-Agent Allowed HTTP headers to pass to endpoint (canonical form). Host header is always allowed.
--endpoint.html.rewrite Rewrites to apply to text/html content (see below for details)
--endpoint.port.allow ^(80|443)$ Allowed endpoint ports (regexp)
--endpoint.scheme.allow ^http(s)?$ Allowed endpoint schemes (regexp)
--prefix Prefix for HTTP proxy endpoint
--ssh SSH jump host address
--ssh.key SSH private key file(s) to load for jump host authentication (repeatable)
--ssh.knownhosts SSH known_hosts file to verify jump host identity
--ssh.user jump SSH jump host user
--ssh.port 22 SSH jump host port
--ssh.timeout 5m Idle timeout for SSH jump host connections
Setting Flags from the Environment

All flags can be set using the following form:

TUNNELLER_<FLAG NAME>

For example:

TUNNELLER_SSH_TIMEOUT="15m" TUNNELLER_SSH_KEY="/etc/tunneller/id_ed25519" tunneller

Command line and environment variables may be combined with environment variables taking precedence over command line flags.

Authentication

SSH authentication uses private keys loaded at startup via one or more --key flags. Keys are held in an in-process SSH agent for the lifetime of the service.

tunneller --ssh.key /etc/tunneller/id_ed25519 --ssh.key /etc/tunneller/id_rsa

If no keys are loaded then authentication will fail.

URL Routing

The request URL determines the tunnel parameters in the form of:

/{scheme}/{endpoint}/{port}/
Parameter Description Default flag
endpoint Remote host to reach via the tunnel (required)
scheme Endpoint scheme (required)
port Endpoint port (required)
Examples

Given a service started with:

tunneller \
  --ssh jump.example.com \
  --ssh.user jump \
URL Connects to
/http/192.168.1.1/80/ http://192.168.1.1:80 via jump@jump.example.com:22
/https/192.168.1.2/443/ https://192.168.1.2:443 via jump@jump.example.com:22

Restrictions

All URL parameters are validated against their corresponding --*.allow regexp flags before a tunnel is established. Requests that fail validation receive a 403 Forbidden response.

This allows the operator to constrain which jump hosts, users, ports, schemes, and endpoints are reachable through the service. For example, to restrict the service to a single jump host and only allow HTTPS to RFC 1918 addresses on port 443:

tunneller \
  --ssh jump.example.com \
  --ssh.allow '^jump\.example\.com$' \
  --endpoint.allow '^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)' \
  --endpoint.scheme.allow '^https$' \
  --endpoint.port.allow '^443$'

Tunnel Lifecycle

A tunnel is established on the first request to a given parameter combination and reused for subsequent requests with the same parameters. Tunnels are torn down automatically after a period of inactivity (idle timeout). A new tunnel will be established if a subsequent request arrives after teardown.

SSH Host Key Verification

By default SSH host keys are not verified, which is not secure in production.

The --ssh.knownhosts option accepts the path to a SSH Known Hosts file in order to verify host keys.

Endpoint Certificate Verification

Enabling certificate verification for HTTPS is highly recommended by passing the --endpoint.ca option which accepts a path to a CA bundle in PEM format or the special value @system which loads trusted CA's from the system (if available).

Any cookies that are returned from the proxied endpoint via a Set-Cookie header have their path value changed so they are only valid for the proxied path.

In addition only cookies that have been returned from a proxied endpoint via a Set-Cookie header will be passed from the browser to the remote endpoint.

This "cookie tracking" is per tunnel but is maintained for the lifetime of the entire service, not just the lifetime of the particular tunnel.

Metrics

Prometheus metrics are provided at the /metrics endpoint by default but can be changed using the --metrics.path flag or disabled completely with the --metrics.enabled flag.

Metric Name Type Description
tunneller_tunnel_count Guage Number of active SSH tunnels
tunneller_tunnel_established_total Counter Total number of SSH tunnels established successfully
tunneller_tunnel_error_total Counter Total number of errors when establishing SSH tunnels
tunneller_tunnel_total Counter Total number of SSH tunnels attempted to be established

Content Rewrites

By default action, href and src properties that reference absolute paths will be rewritten based on the prefix used to connect to the endpoint.

In addition, custom content rewrites can be provided via the --endpoint.html.rewrite option as follows:

tunneller --endpoint.html.rewrite "s#regex#template#"

The regex is a Go RE2 regular expression that must contain a valid RE2 regular expression that will match at most one substring.

For example, althrough all of the following three regular expressions are valid however only the first two will work as expected:

foo="([^"\n\r]*)"
foo=("[^"\n\r]*")|foo=('[^'\n\r]*')
(foo|bar)="([^"\n\r]*)"

The last form will not yield the expected results and unfortuantely will not cause a parsing error.

Template is a Go template that is passed the URL prefix as {{ .Prefix }} and the captured string as {{ .Captured }}.

The overall syntax is somewhat inspired by sed with the seperator between regexp and template being # or /.

The unmatched content of the regexp is wrapped back around the templated response.

So for example to replace absolute URLs in all href properties a rewrite as follows may be appropriate:

tunneller --endpoint.html.rewrite 's#href=["'](/.*)["']#{{ .Prefix }}{{ .Captured }}#'

The above would make the following changes for a device accessed via the URL path of /https/192.168.10.1/443/ as follows:

<a href="relative/link">This is unchanged</a>
<a href="/absolute/link">This will be updated</a>

Would become:

<a href="relative/link">This is unchanged</a>
<a href="/https/192.168.10.1/443/absolute/link">This will be updated</a>

Directories

Path Synopsis
cmd
tunneller command
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL