How to forward ports with tunneling¶
Port-forwarding in Workshop is done with the tunnel interface. Tunnels pair a plug (the listening side) with a slot (the service side), forwarding every connection that reaches the plug address to the slot address. Three common scenarios cover most day-to-day port-forwarding needs.
Expose workshop services¶
This scenario was covered earlier in the
tutorial section on interfaces.
In short, you add a tunnel slot to the SDK that runs the service
and a matching plug to the system SDK:
sdks:
- name: go
slots:
caddy:
interface: tunnel
endpoint: localhost:8080 # service in the workshop
- name: system
plugs:
caddy:
interface: tunnel
endpoint: localhost:8080 # port on the host
Refresh the workshop and start the service,
so the host can reach it at localhost:8080:
$ workshop refresh
Note that port numbers can be different from each other, subject to the regular low-port limitations. Ensure the plug port is free before refreshing, or the tunnel will fail to activate.
Note
Workshop doesn’t resolve hostnames, but supports the aliases
localhost, ip6-localhost, and ip6-loopback.
Cross-protocol forwarding¶
Tunnels are not limited to identical protocols on both ends. Unix domain sockets are often used for local-only daemons. The tunnel interface lets you bridge them to TCP ports and vice versa.
Why do this?
Avoid port clashes: Listen on a unique Unix path and publish it on an arbitrary TCP port.
Expose a local service: Make a Unix-only daemon visible to tools that only speak TCP.
Note
Only TCP and Unix domain sockets can be bridged across a tunnel. UDP is not compatible with Unix domain sockets.
Workshop Unix domain socket to host TCP port¶
Suppose a gRPC service inside the workshop
listens on /run/grpc-service.sock (Unix).
You want to reach it on the host at localhost:18080:
#...
sdks:
- name: grpc-service
slots:
api:
interface: tunnel
endpoint: /run/grpc-service.sock # Unix domain socket in the workshop
- name: system
plugs:
api:
interface: tunnel
endpoint: localhost:18080 # chosen TCP port on the host
After a refresh,
the service will be reachable from the host at grpc://localhost:18080:
$ workshop refresh
$ workshop info
...
sdks:
system:
tunnels:
api:
from: 127.0.0.1:18080/tcp
to: /run/grpc-service.sock
...
Note
The tunnel interface expands $HOME and $XDG_RUNTIME_DIR
in socket file paths automatically, but refuses other variables.
Only user-writable locations are accepted for security reasons.
Host Unix domain socket to workshop TCP port¶
Now let’s invert the flow.
Share a host abstract socket (which exists only in the kernel, not on disk)
with code inside the workshop on TCP port 9000.
#...
sdks:
- name: system
slots:
bus:
interface: tunnel
endpoint: '@bus' # abstract socket on the host
- name: client
plugs:
bus:
interface: tunnel
endpoint: localhost:9000 # TCP port inside workshop
After workshop refresh and workshop connect,
the code in the workshop can connect to localhost:9000,
and Workshop forwards the traffic to the host’s abstract socket @bus.
Note
Abstract sockets avoid filesystem permissions and name collisions.
They are written as @name (note the leading “@”).
Troubleshooting¶
TCP to Unix bridging is supported, while UDP to Unix is not.
Ports below 1024 (privileged ports) may be rejected on the host side.
Ensure the slot socket addresses exist and can be accessed by the Workshop user; plug sockets are created by Workshop so they shouldn’t be already occupied.
The tunnel won’t activate if either side’s endpoint is invalid; see error messages and workshop tasks for hints.
See also¶
Explanation:
Reference: