Skip to content

Commit

Permalink
Localhost server access (#68)
Browse files Browse the repository at this point in the history
Add ability to access the 127.0.0.1 IP of a Server
  • Loading branch information
Aptimex authored Nov 25, 2024
1 parent fdfe186 commit 21e6aba
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 57 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
bin/*
!bin/.gitkeep
src/dist
*.exe

# macOS
.DS_Store
Expand Down
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 <IPv4 address>` 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 <IPv4 address> -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]
Expand Down
6 changes: 5 additions & 1 deletion src/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
25 changes: 16 additions & 9 deletions src/cmd/add_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type addServerCmdConfig struct {
writeToClipboard bool
port int
nickname string
localhostIP string
}

var addServerCmdArgs = addServerCmdConfig{
Expand All @@ -37,6 +38,7 @@ var addServerCmdArgs = addServerCmdConfig{
writeToClipboard: false,
port: USE_ENDPOINT_PORT,
nickname: "",
localhostIP: "",
}

// addServerCmd represents the server command.
Expand All @@ -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")
Expand All @@ -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 {
Expand All @@ -83,7 +86,7 @@ func init() {
} else {
fmt.Printf("Failed to hide flag %v: %v\n", f, err)
}

}
}
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
46 changes: 28 additions & 18 deletions src/cmd/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type configureCmdConfig struct {
keepalive int
mtu int
disableV6 bool
localhostIP string
}

// Defaults for configure command.
Expand All @@ -61,6 +62,7 @@ var configureCmdArgs = configureCmdConfig{
keepalive: Keepalive,
mtu: MTU,
disableV6: false,
localhostIP: "",
}

// configureCmd represents the configure command.
Expand All @@ -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")
Expand All @@ -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")
Expand All @@ -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",
Expand All @@ -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
}
Expand Down Expand Up @@ -177,24 +183,24 @@ 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
} else {
clientPort = c.port
serverPort = Port
}

err = serverConfigRelay.SetPort(serverPort)
check("failed to set port", err)

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 21e6aba

Please sign in to comment.