From aa207764b2a68191c5e6c7f13da69bd7cff40c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Guerraz?= Date: Wed, 14 Jun 2023 02:17:32 +0000 Subject: [PATCH] Implement STDIOTunnel (#70) --- README.md | 10 ++++++++++ cmd/wireproxy/main.go | 5 +++++ config.go | 20 ++++++++++++++++++++ routine.go | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/README.md b/README.md index 52467ec..2d14500 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,16 @@ Target = play.cubecraft.net:25565 ListenPort = 3422 Target = localhost:25545 +# STDIOTunnel is a tunnel connecting the standard input and output of the wireproxy +# process to the specified TCP target via wireguard. +# This is especially useful to use wireproxy as a ProxyCommand parameter in openssh +# For example: +# ssh -o ProxyCommand='wireproxy -c myconfig.conf' ssh.myserver.net +# Flow: +# Piped command -->(wireguard)--> ssh.myserver.net:22 +[STDIOTunnel] +Target = ssh.myserver.net:22 + # Socks5 creates a socks5 proxy on your LAN, and all traffic would be routed via wireguard. [Socks5] BindAddress = 127.0.0.1:25344 diff --git a/cmd/wireproxy/main.go b/cmd/wireproxy/main.go index d0950a8..9cc4874 100644 --- a/cmd/wireproxy/main.go +++ b/cmd/wireproxy/main.go @@ -5,6 +5,7 @@ import ( "log" "os" "os/exec" + "syscall" "github.com/akamensky/argparse" "github.com/octeep/wireproxy" @@ -116,6 +117,10 @@ func main() { return } + // Wireguard doesn't allow configuring which FD to use for logging + // https://github.com/WireGuard/wireguard-go/blob/master/device/logger.go#L39 + // so redirect STDOUT to STDERR, we don't want to print anything to STDOUT anyways + os.Stdout = os.NewFile(uintptr(syscall.Stderr), "/dev/stderr") logLevel := device.LogLevelVerbose if *silent { logLevel = device.LogLevelSilent diff --git a/config.go b/config.go index 81f00d6..efcf0e1 100644 --- a/config.go +++ b/config.go @@ -34,6 +34,10 @@ type TCPClientTunnelConfig struct { Target string } +type STDIOTunnelConfig struct { + Target string +} + type TCPServerTunnelConfig struct { ListenPort int Target string @@ -300,6 +304,17 @@ func parseTCPClientTunnelConfig(section *ini.Section) (RoutineSpawner, error) { return config, nil } +func parseSTDIOTunnelConfig(section *ini.Section) (RoutineSpawner, error) { + config := &STDIOTunnelConfig{} + targetSection, err := parseString(section, "Target") + if err != nil { + return nil, err + } + config.Target = targetSection + + return config, nil +} + func parseTCPServerTunnelConfig(section *ini.Section) (RoutineSpawner, error) { config := &TCPServerTunnelConfig{} @@ -418,6 +433,11 @@ func ParseConfig(path string) (*Configuration, error) { return nil, err } + err = parseRoutinesConfig(&routinesSpawners, cfg, "STDIOTunnel", parseSTDIOTunnelConfig) + if err != nil { + return nil, err + } + err = parseRoutinesConfig(&routinesSpawners, cfg, "TCPServerTunnel", parseTCPServerTunnelConfig) if err != nil { return nil, err diff --git a/routine.go b/routine.go index 413b76f..b52d4d0 100644 --- a/routine.go +++ b/routine.go @@ -192,6 +192,32 @@ func tcpClientForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) { go connForward(1024, conn, sconn) } +// STDIOTcpForward starts a new connection via wireguard and forward traffic from `conn` +func STDIOTcpForward(vt *VirtualTun, raddr *addressPort) { + target, err := vt.resolveToAddrPort(raddr) + if err != nil { + errorLogger.Printf("Name resolution error for %s: %s\n", raddr.address, err.Error()) + return + } + + // os.Stdout has previously been remapped to stderr, se we can't use it + stdout, err := os.OpenFile("/dev/stdout", os.O_WRONLY, 0) + if err != nil { + errorLogger.Printf("Failed to open /dev/stdout: %s\n", err.Error()) + return + } + + tcpAddr := TCPAddrFromAddrPort(*target) + sconn, err := vt.Tnet.DialTCP(tcpAddr) + if err != nil { + errorLogger.Printf("TCP Client Tunnel to %s (%s): %s\n", target, tcpAddr, err.Error()) + return + } + + go connForward(1024, os.Stdin, sconn) + go connForward(1024, sconn, stdout) +} + // SpawnRoutine spawns a local TCP server which acts as a proxy to the specified target func (conf *TCPClientTunnelConfig) SpawnRoutine(vt *VirtualTun) { raddr, err := parseAddressPort(conf.Target) @@ -213,6 +239,16 @@ func (conf *TCPClientTunnelConfig) SpawnRoutine(vt *VirtualTun) { } } +// SpawnRoutine connects to the specified target and plumbs it to STDIN / STDOUT +func (conf *STDIOTunnelConfig) SpawnRoutine(vt *VirtualTun) { + raddr, err := parseAddressPort(conf.Target) + if err != nil { + log.Fatal(err) + } + + go STDIOTcpForward(vt, raddr) +} + // tcpServerForward starts a new connection locally and forward traffic from `conn` func tcpServerForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) { target, err := vt.resolveToAddrPort(raddr)