diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 5597def..0e89c75 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -15,12 +15,12 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: "1.20" + go-version: "1.23.3" - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version # version: latest diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 69f69db..9bd8118 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.20" + go-version: "1.23.3" cache: true cache-dependency-path: src/go.sum - name: Check GoReleaser Config diff --git a/.gitignore b/.gitignore index dc1d65f..99ffce1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ bin/* !bin/.gitkeep src/dist +*.exe # macOS .DS_Store diff --git a/README.md b/README.md index 9816f42..ce4af54 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ In this diagram, the Client has generated and installed WireGuard configuration - [Features](#features) - [Demo](#demo) - [Experimental](#experimental) + - [Localhost Server Access](#localhost-server-access) - [TCP Tunneling](#tcp-tunneling) - [Add Clients To Any Server](#add-clients-to-any-server) @@ -85,7 +86,7 @@ See the [Usage section](#Usage) for more details. No installation of Wiretap is required. Just grab a binary from the [releases](https://github.com/sandialabs/wiretap/releases) page. You may need two different binaries if the OS/ARCH are different on the client and server machines. -If you want to compile it yourself or can't find the OS/ARCH you're looking for, install Go (>=1.20) from https://go.dev/dl/ and use the provided [Makefile](./src/Makefile). +If you want to compile it yourself or can't find the OS/ARCH you're looking for, install Go (>=1.23.3) from https://go.dev/dl/ and use the provided [Makefile](./src/Makefile). # How it Works @@ -539,6 +540,30 @@ Please see the [Demo page in the Wiki](https://github.com/sandialabs/wiretap/wik # Experimental +## Localhost Server Access + +Sometimes you want to access many ports on the Server itself that are listening on the localhost/loopback interface instead of a public interface. Rather than setting up many individual port forwards, you can use Wiretap's "localhost IP" redirection feature. + +When running the `configure` or `add server` commands, you can specify a `--localhost-ip ` argument. For example: +```bash +./wiretap configure --endpoint 7.3.3.1:1337 --routes 10.0.0.0/24 -i 192.168.137.137 +``` +Any packets received by this Server through the Wiretap network with this target destination address (`192.168.137.137` in this example) will be rerouted to the Server host's `127.0.0.1` loopback address instead, with replies routed back to the Client appropriately. + +> [!CAUTION] +> It is **strongly** recommended that you specify a private (non-routable) IP address to use for this option, preferably one that you know is not in use in the target network. This feature has only been lightly tested, so if the re-routing fails unexpectedly you want to ensure your traffic will go to a "safe" destination. For similar reasons you should not specify a broadcast address, or IPs that your Client already has routes for. + +Under the hood, this feature is roughly equivalent to adding this `iptables` rule to Wiretap's userspace networking stack on the Server: +``` +iptables -t nat -A PREROUTING -p tcp -d -j DNAT --to-destination 127.0.0.1 +``` + +Limitations: +- Currently this only works for TCP connections, and only for an IPv4 target address. + - Unfortunately there's [not a clean way](https://serverfault.com/a/975890) to do NAT to the IPv6 `::1` loopback address, so this feature can't be used to access services listening exclusively on that IPv6 address. +- This feature does not provide access to other IPs in the 127.0.0.0/8 space. + + ## TCP Tunneling > [!WARNING] diff --git a/src/api/api.go b/src/api/api.go index e1d85ef..fafc002 100644 --- a/src/api/api.go +++ b/src/api/api.go @@ -28,7 +28,11 @@ type request struct { // MakeRequest attempts to send an API query to the Wiretap server. func makeRequest(req request) ([]byte, error) { - client := &http.Client{Timeout: 3 * time.Second} + // Never use a proxy for API requests, they must go direct through the Wiretap network + tr := &http.Transport{ + Proxy: nil, + } + client := &http.Client{Timeout: 3 * time.Second, Transport: tr} reqBody := bytes.NewBuffer(req.Body) r, err := http.NewRequest(req.Method, req.URL, reqBody) diff --git a/src/cmd/add_server.go b/src/cmd/add_server.go index e9919f9..7364372 100644 --- a/src/cmd/add_server.go +++ b/src/cmd/add_server.go @@ -26,6 +26,7 @@ type addServerCmdConfig struct { writeToClipboard bool port int nickname string + localhostIP string } var addServerCmdArgs = addServerCmdConfig{ @@ -37,6 +38,7 @@ var addServerCmdArgs = addServerCmdConfig{ writeToClipboard: false, port: USE_ENDPOINT_PORT, nickname: "", + localhostIP: "", } // addServerCmd represents the server command. @@ -56,8 +58,9 @@ func init() { addServerCmd.Flags().StringVarP(&addServerCmdArgs.serverAddress, "server-address", "s", addServerCmdArgs.serverAddress, "API address of server that new server will connect to, connects to client by default") addServerCmd.Flags().IntVarP(&addServerCmdArgs.port, "port", "p", addServerCmdArgs.port, "listener port to start on new server for wireguard relay. If --outbound, default is the port specified in --endpoint; otherwise default is 51820") addServerCmd.Flags().StringVarP(&addServerCmdArgs.nickname, "nickname", "n", addServerCmdArgs.nickname, "Server nickname to display in 'status' command") + addServerCmd.Flags().StringVarP(&addServerCmdArgs.localhostIP, "localhost-ip", "i", addServerCmdArgs.localhostIP, "[EXPERIMENTAL] Redirect wiretap packets destined for this IPv4 address to server's localhost") addServerCmd.Flags().BoolVarP(&addServerCmdArgs.writeToClipboard, "clipboard", "c", addServerCmdArgs.writeToClipboard, "copy configuration args to clipboard") - + addServerCmd.Flags().StringVarP(&addServerCmdArgs.configFileRelay, "relay-input", "", addServerCmdArgs.configFileRelay, "filename of input relay config file") addServerCmd.Flags().StringVarP(&addServerCmdArgs.configFileE2EE, "e2ee-input", "", addServerCmdArgs.configFileE2EE, "filename of input E2EE config file") addServerCmd.Flags().StringVarP(&addServerCmdArgs.configFileServer, "server-output", "", addServerCmdArgs.configFileServer, "filename of server config output file") @@ -67,7 +70,7 @@ func init() { addServerCmd.Flags().SortFlags = false addServerCmd.PersistentFlags().SortFlags = false - + helpFunc := addCmd.HelpFunc() addCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { if !ShowHidden { @@ -83,7 +86,7 @@ func init() { } else { fmt.Printf("Failed to hide flag %v: %v\n", f, err) } - + } } } @@ -170,7 +173,7 @@ func (c addServerCmdConfig) Run() { PublicKey: serverConfigE2EE.GetPublicKey(), AllowedIPs: c.allowedIPs, Endpoint: net.JoinHostPort(newRelayPrefixes[0].Addr().Next().Next().String(), fmt.Sprint(E2EEPort)), - Nickname: c.nickname, + Nickname: c.nickname, }) check("failed to generate new e2ee peer", err) clientConfigE2EE.AddPeer(serverE2EEPeer) @@ -264,7 +267,7 @@ func (c addServerCmdConfig) Run() { PublicKey: serverConfigE2EE.GetPublicKey(), AllowedIPs: c.allowedIPs, Endpoint: net.JoinHostPort(addresses.NextServerRelayAddr4.String(), fmt.Sprint(E2EEPort)), - Nickname: c.nickname, + Nickname: c.nickname, }) check("failed to parse server as peer", err) clientConfigE2EE.AddPeer(serverPeerConfigE2EE) @@ -336,20 +339,24 @@ func (c addServerCmdConfig) Run() { // Leaf server is the relay peer for the new server. clientConfigRelay = leafServerConfigRelay } - + // Set port defaults if c.port == USE_ENDPOINT_PORT { if addArgs.outbound { //for outbound, default port is same as endpoint port c.port = portFromEndpoint(addArgs.endpoint) - + } else { //for inbound, use a reasonable default for server relay listening port - c.port = Port; + c.port = Port } } - + err = serverConfigRelay.SetPort(c.port) check("failed to set port", err) + // Setup localhost IP relay + err = serverConfigRelay.SetLocalhostIP(c.localhostIP) + check("failed to set localhost IP", err) + // Overwrite Relay file with new server peer if adding a server directly to the client. var fileStatusRelay string if len(c.serverAddress) == 0 { diff --git a/src/cmd/configure.go b/src/cmd/configure.go index 78d6b3d..c35e0a0 100644 --- a/src/cmd/configure.go +++ b/src/cmd/configure.go @@ -35,6 +35,7 @@ type configureCmdConfig struct { keepalive int mtu int disableV6 bool + localhostIP string } // Defaults for configure command. @@ -61,6 +62,7 @@ var configureCmdArgs = configureCmdConfig{ keepalive: Keepalive, mtu: MTU, disableV6: false, + localhostIP: "", } // configureCmd represents the configure command. @@ -82,7 +84,8 @@ func init() { configureCmd.Flags().BoolVar(&configureCmdArgs.outbound, "outbound", configureCmdArgs.outbound, "client will initiate handshake to server; --endpoint now specifies server's listening socket instead of client's, and --port assigns the server's listening port instead of client's") configureCmd.Flags().IntVarP(&configureCmdArgs.port, "port", "p", configureCmdArgs.port, "listener port for wireguard relay. Default is to copy the --endpoint port. If --outbound, sets port for the server; else for the client.") configureCmd.Flags().StringVarP(&configureCmdArgs.nickname, "nickname", "n", configureCmdArgs.nickname, "Server nickname to display in 'status' command") - + configureCmd.Flags().StringVarP(&configureCmdArgs.localhostIP, "localhost-ip", "i", configureCmdArgs.localhostIP, "[EXPERIMENTAL] Redirect wiretap packets destined for this IPv4 address to server's localhost") + configureCmd.Flags().StringVarP(&configureCmdArgs.configFileRelay, "relay-output", "", configureCmdArgs.configFileRelay, "wireguard relay config output filename") configureCmd.Flags().StringVarP(&configureCmdArgs.configFileE2EE, "e2ee-output", "", configureCmdArgs.configFileE2EE, "wireguard E2EE config output filename") configureCmd.Flags().StringVarP(&configureCmdArgs.configFileServer, "server-output", "s", configureCmdArgs.configFileServer, "wiretap server config output filename") @@ -93,7 +96,7 @@ func init() { configureCmd.Flags().IntVarP(&configureCmdArgs.keepalive, "keepalive", "k", configureCmdArgs.keepalive, "tunnel keepalive in seconds, only applies to outbound handshakes") configureCmd.Flags().IntVarP(&configureCmdArgs.mtu, "mtu", "m", configureCmdArgs.mtu, "tunnel MTU") configureCmd.Flags().BoolVarP(&configureCmdArgs.disableV6, "disable-ipv6", "", configureCmdArgs.disableV6, "disables IPv6") - + configureCmd.Flags().StringVarP(&configureCmdArgs.clientAddr4Relay, "ipv4-relay", "", configureCmdArgs.clientAddr4Relay, "ipv4 relay address") configureCmd.Flags().StringVarP(&configureCmdArgs.clientAddr6Relay, "ipv6-relay", "", configureCmdArgs.clientAddr6Relay, "ipv6 relay address") configureCmd.Flags().StringVarP(&configureCmdArgs.clientAddr4E2EE, "ipv4-e2ee", "", configureCmdArgs.clientAddr4E2EE, "ipv4 e2ee address") @@ -112,15 +115,15 @@ func init() { configureCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { if !ShowHidden { for _, f := range []string{ - "api", - "ipv4-relay", - "ipv6-relay", - "ipv4-e2ee", - "ipv6-e2ee", - "ipv4-relay-server", - "ipv6-relay-server", - "keepalive", - "mtu", + "api", + "ipv4-relay", + "ipv6-relay", + "ipv4-e2ee", + "ipv6-e2ee", + "ipv4-relay-server", + "ipv6-relay-server", + "keepalive", + "mtu", "disable-ipv6", "relay-output", "e2ee-output", @@ -141,6 +144,9 @@ func init() { func (c configureCmdConfig) Run() { var err error + if c.localhostIP != "" { + c.allowedIPs = append(c.allowedIPs, c.localhostIP+"/32") + } if c.disableV6 && netip.MustParsePrefix(c.apiAddr).Addr().Is6() { c.apiAddr = c.apiv4Addr } @@ -177,16 +183,16 @@ func (c configureCmdConfig) Run() { if !c.disableV6 { clientE2EEAddrs = append(clientE2EEAddrs, c.clientAddr6E2EE) } - + if c.port == USE_ENDPOINT_PORT { c.port = portFromEndpoint(c.endpoint) } - + // We only configure one of these (based on --outbound or not) // The other must be manually changed in the configs/command/envs - var clientPort int; - var serverPort int; - + var clientPort int + var serverPort int + if c.outbound { clientPort = Port serverPort = c.port @@ -194,7 +200,7 @@ func (c configureCmdConfig) Run() { clientPort = c.port serverPort = Port } - + err = serverConfigRelay.SetPort(serverPort) check("failed to set port", err) @@ -242,7 +248,7 @@ func (c configureCmdConfig) Run() { PublicKey: serverConfigE2EE.GetPublicKey(), AllowedIPs: c.allowedIPs, Endpoint: net.JoinHostPort(relaySubnet4.Addr().Next().Next().String(), fmt.Sprint(E2EEPort)), - Nickname: c.nickname, + Nickname: c.nickname, }, }, Addresses: clientE2EEAddrs, @@ -277,6 +283,10 @@ func (c configureCmdConfig) Run() { err = serverConfigRelay.SetMTU(c.mtu) check("failed to set mtu", err) } + if c.localhostIP != "" { + err = serverConfigRelay.SetLocalhostIP(c.localhostIP) + check("failed to set localhost IP", err) + } // Add number to filename if it already exists. c.configFileRelay = peer.FindAvailableFilename(c.configFileRelay) diff --git a/src/cmd/serve.go b/src/cmd/serve.go index 6e46af0..35f4c46 100644 --- a/src/cmd/serve.go +++ b/src/cmd/serve.go @@ -10,6 +10,7 @@ import ( "strings" "sync" "time" + "slices" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -17,8 +18,10 @@ import ( "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun/netstack" + "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" + "gvisor.dev/gvisor/pkg/tcpip/stack" gtcp "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" gudp "gvisor.dev/gvisor/pkg/tcpip/transport/udp" @@ -47,6 +50,7 @@ type serveCmdConfig struct { keepaliveCount uint keepaliveInterval uint disableV6 bool + localhostIP string } type wiretapDefaultConfig struct { @@ -82,6 +86,7 @@ var serveCmd = serveCmdConfig{ keepaliveCount: 3, keepaliveInterval: 60, disableV6: false, + localhostIP: "", } var wiretapDefault = wiretapDefaultConfig{ @@ -124,6 +129,7 @@ func init() { cmd.Flags().BoolVarP(&serveCmd.disableV6, "disable-ipv6", "", serveCmd.disableV6, "disable ipv6") cmd.Flags().BoolVarP(&serveCmd.logging, "log", "l", serveCmd.logging, "enable logging to file") cmd.Flags().StringVarP(&serveCmd.logFile, "log-file", "o", serveCmd.logFile, "write log to this filename") + cmd.Flags().StringVarP(&serveCmd.localhostIP, "localhost-ip", "i", serveCmd.localhostIP, "[EXPERIMENTAL] redirect Wiretap packets destined for this IPv4 address to server's localhost") cmd.Flags().StringP("api", "0", wiretapDefault.apiAddr, "address of API service") cmd.Flags().IntP("keepalive", "k", wiretapDefault.keepalive, "tunnel keepalive in seconds") cmd.Flags().IntP("mtu", "m", wiretapDefault.mtu, "tunnel MTU") @@ -145,6 +151,9 @@ func init() { err = viper.BindPFlag("disableipv6", cmd.Flags().Lookup("disable-ipv6")) check("error binding flag to viper", err) + err = viper.BindPFlag("Relay.Interface.LocalhostIP", cmd.Flags().Lookup("localhost-ip")) + check("error binding flag to viper", err) + // Quiet and debug flags must be used independently. cmd.MarkFlagsMutuallyExclusive("debug", "quiet") @@ -484,6 +493,30 @@ func (c serveCmdConfig) Run() { } s.SetTransportProtocolHandler(gudp.ProtocolNumber, udp.Handler(udpConfig)) + // Setup localhost forwarding IP using IPTables + if viper.IsSet("Relay.Interface.LocalhostIP") && viper.GetString("Relay.Interface.LocalhostIP") != "" { + localhostAddr, err := netip.ParseAddr(viper.GetString("Relay.Interface.LocalhostIP")) + check("failed to parse localhost-ip address", err) + if len(localhostAddr.AsSlice()) != 4 { + log.Fatalf("Localhost IP must be an IPv4 address") + } + + configureLocalhostForwarding(localhostAddr, s) + + if localhostAddr.IsLoopback() { + fmt.Printf("=== WARNING: %s is a loopback IP. It will probably not work for Localhost Forwarding ===\n", localhostAddr.String()) + + } else if localhostAddr.IsMulticast() { + fmt.Printf("=== WARNING: %s is a Multicast IP. Your OS might still send extra packets to other IPs when you target this IP ===\n", localhostAddr.String()) + + } else if !localhostAddr.IsPrivate() { + fmt.Printf("=== WARNING: %s is a public IP. If Localhost Forwarding fails, your traffic may actually touch that IP ===\n", localhostAddr.String()) + } + + fmt.Println("Localhost Forwarding configured for ", localhostAddr) + fmt.Println() + } + // Make new relay device. devRelay := device.NewDevice(tunRelay, conn.NewDefaultBind(), device.NewLogger(logger, "")) // Configure wireguard. @@ -535,3 +568,53 @@ func (c serveCmdConfig) Run() { wg.Wait() } + +// Setup iptables rule for localhost re-routing (DNAT) +func configureLocalhostForwarding(localhostAddr netip.Addr, s *stack.Stack) { + // https://pkg.go.dev/gvisor.dev/gvisor@v0.0.0-20231115214215-71bcc96c6e38/pkg/tcpip/stack + newFilter := stack.EmptyFilter4() + newFilter.Dst = tcpip.AddrFromSlice(localhostAddr.AsSlice()) + newFilter.DstMask = tcpip.AddrFromSlice([]byte{255, 255, 255, 255}) + + newRule := new(stack.Rule) + newRule.Filter = newFilter + + //Do address-only DNAT; port remains the same, so all ports are effectively forwarded to localhost + newRule.Target = &stack.DNATTarget{ + Addr: tcpip.AddrFromSlice([]byte{127, 0, 0, 1}), + NetworkProtocol: ipv4.ProtocolNumber, + ChangeAddress: true, + ChangePort: false, + } + + ipt := s.IPTables() + natTable := ipt.GetTable(stack.NATID, false) + newTable := prependIPtableRule(natTable, *newRule, stack.Prerouting) + + //ForceReplaceTable ensures IPtables get enabled; ReplaceTable doesn't. + ipt.ForceReplaceTable(stack.NATID, newTable, false) +} + +// Adds a rule to the start of a table chain. +func prependIPtableRule(table stack.Table, newRule stack.Rule, chain stack.Hook) (stack.Table) { + insertIndex := int(table.BuiltinChains[chain]) + fmt.Printf("Inserting rule into index %d\n", insertIndex) + table.Rules = slices.Insert(table.Rules, insertIndex, newRule) + + // Increment the later chain and underflow index pointers to account for the rule added to the Rules slice + // https://pkg.go.dev/gvisor.dev/gvisor@v0.0.0-20231115214215-71bcc96c6e38/pkg/tcpip/stack#Table + for chainHook, ruleIndex := range table.BuiltinChains { + //assumes each chain has its own unique starting rule index + if ruleIndex > insertIndex { + table.BuiltinChains[chainHook] = ruleIndex + 1 + + } + } + for chainHook, ruleIndex := range table.Underflows { + if ruleIndex >= insertIndex { + table.Underflows[chainHook] = ruleIndex + 1 + } + } + + return table +} \ No newline at end of file diff --git a/src/go.mod b/src/go.mod index f9a2424..dedb08f 100644 --- a/src/go.mod +++ b/src/go.mod @@ -1,6 +1,8 @@ module wiretap -go 1.20 +go 1.21.1 + +toolchain go1.23.3 replace golang.zx2c4.com/wireguard => github.com/luker983/wireguard-go v0.0.0-20231019223227-fc689040dc0a @@ -17,7 +19,7 @@ require ( golang.org/x/net v0.23.0 golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb - gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 + gvisor.dev/gvisor v0.0.0-20231115214215-71bcc96c6e38 ) require ( @@ -37,10 +39,10 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect golang.org/x/crypto v0.21.0 // indirect - golang.org/x/sync v0.3.0 // indirect + golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.1.0 // indirect + golang.org/x/time v0.3.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/src/go.sum b/src/go.sum index f578fb4..5f149fa 100644 --- a/src/go.sum +++ b/src/go.sum @@ -63,6 +63,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -109,6 +110,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -146,9 +148,11 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= github.com/luker983/wireguard-go v0.0.0-20231019223227-fc689040dc0a h1:gUjN6KFTeYzhwiT2qaf2kRLJhfi5+/yvqxeKYg8vHuA= @@ -175,6 +179,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= @@ -307,8 +312,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -362,8 +367,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -517,8 +522,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= -gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= +gvisor.dev/gvisor v0.0.0-20231115214215-71bcc96c6e38 h1:iKEOYAMkiugi05kLeJD5v95Fwrm5XP6mstN8RSXD7v4= +gvisor.dev/gvisor v0.0.0-20231115214215-71bcc96c6e38/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/src/peer/config.go b/src/peer/config.go index cf83d72..3f3b1a6 100644 --- a/src/peer/config.go +++ b/src/peer/config.go @@ -14,17 +14,19 @@ import ( ) type Config struct { - config wgtypes.Config - mtu int - peers []PeerConfig - addresses []net.IPNet + config wgtypes.Config + mtu int + peers []PeerConfig + addresses []net.IPNet + localhostIP string } type configJSON struct { - Config wgtypes.Config - MTU int - Peers []PeerConfig - Addresses []net.IPNet + Config wgtypes.Config + MTU int + Peers []PeerConfig + Addresses []net.IPNet + LocalhostIP string } type ConfigArgs struct { @@ -35,6 +37,7 @@ type ConfigArgs struct { ReplacePeers bool Peers []PeerConfigArgs Addresses []string + LocalhostIP string } type Shell uint @@ -152,6 +155,9 @@ func ParseConfig(filename string) (c Config, err error) { return c, e } err = c.SetMTU(mtu) + case "localhostip": + err = c.SetLocalhostIP(value) + fmt.Println("LocalhostIP value parsed") } if err != nil { return c, err @@ -163,13 +169,13 @@ func ParseConfig(filename string) (c Config, err error) { if len(line) == 0 { continue } - + if strings.HasPrefix(line, CUSTOM_PREFIX) { //special wiretap-specific values line = line[len(CUSTOM_PREFIX):] } else if line[0] == '#' { continue } - + key, value, err := parseConfigLine(line) if err != nil { return c, err @@ -220,6 +226,7 @@ func (c *Config) MarshalJSON() ([]byte, error) { c.mtu, c.peers, c.addresses, + c.localhostIP, }) } @@ -375,6 +382,15 @@ func (c *Config) GetPeerEndpoint(i int) string { return "" } +func (c *Config) GetLocalhostIP() string { + return c.localhostIP +} + +func (c *Config) SetLocalhostIP(ip string) error { + c.localhostIP = ip + return nil +} + // Convert config to peer config, only transfers keys. func (c *Config) AsPeer() (p PeerConfig, err error) { p, err = NewPeerConfig() @@ -401,6 +417,9 @@ func (c *Config) AsFile() string { if c.mtu != 0 { s.WriteString(fmt.Sprintf("MTU = %d\n", c.mtu)) } + if c.localhostIP != "" { + s.WriteString(fmt.Sprintf("LocalhostIP = %s\n", c.localhostIP)) + } for _, p := range c.peers { s.WriteString(fmt.Sprintf("\n%s", p.AsFile())) } @@ -414,7 +433,7 @@ func (c *Config) AsShareableFile() string { s.WriteString("[Peer]\n") s.WriteString(fmt.Sprintf("PublicKey = %s\n", c.config.PrivateKey.PublicKey().String())) s.WriteString("AllowedIPs = 0.0.0.0/32\n") - + return s.String() } @@ -506,6 +525,11 @@ func CreateServerCommand(relayConfig Config, e2eeConfig Config, shell Shell, sim vals = append(vals, "true") } + if len(relayConfig.GetLocalhostIP()) > 0 { + keys = append(keys, "WIRETAP_RELAY_INTERFACE_LOCALHOSTIP") + vals = append(vals, relayConfig.GetLocalhostIP()) + } + switch shell { case POSIX: for i := 0; i < len(keys); i++ { @@ -544,6 +568,10 @@ func CreateServerFile(relayConfig Config, e2eeConfig Config) string { s.WriteString(fmt.Sprintf("MTU = %d\n", relayConfig.mtu)) } + if relayConfig.localhostIP != "" { + s.WriteString(fmt.Sprintf("LocalhostIP = %s\n", relayConfig.GetLocalhostIP())) + } + // Relay Peer. s.WriteString("\n[Relay.Peer]\n") diff --git a/src/transport/tcp/tcp.go b/src/transport/tcp/tcp.go index 4225e4f..09c49f7 100644 --- a/src/transport/tcp/tcp.go +++ b/src/transport/tcp/tcp.go @@ -49,14 +49,14 @@ func Handler(c Config) func(*tcp.ForwarderRequest) { addr, _ := netip.AddrFromSlice(s.LocalAddress.AsSlice()) err := transport.GetConnCounts().AddAddress(addr, c.Tnet.Stack(), c.StackLock) if err != nil { - log.Println("failed to add address:", err) + log.Println("failed to add address: ", err) req.Complete(false) return } defer func() { err := transport.GetConnCounts().RemoveAddress(addr, c.Tnet.Stack(), c.StackLock) if err != nil { - log.Println("failed to remove address:", err) + log.Println("failed to remove address: ", err) } }()