Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add client to any server #25

Merged
merged 5 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,3 +617,31 @@ Finally, run Wiretap with the forwarded local port as your endpoint on the serve
```bash
WIRETAP_RELAY_INTERFACE_PRIVATEKEY=<key> WIRETAP_RELAY_PEER_PUBLICKEY=<key> WIRETAP_E2EE_INTERFACE_PRIVATEKEY=<key> WIRETAP_E2EE_PEER_PUBLICKEY=<key> WIRETAP_E2EE_PEER_ENDPOINT=172.16.0.1:51821 ./wiretap serve --endpoint localhost:51821
```

### Add Clients To Any Server

> **Note**
> Clients added to arbitrary servers do not currently have the same capabilities as clients added to first-hop servers (the default)

Clients can be attached to any server in the network by using the `--server-address <api-address>` argument when running `wiretap add client`. This allows a client on a different network than the first client to still gain access to all of the Wiretap network's routes. But this has some limitations.

In this example, a new client is added to the second server in the right branch of a Wiretap network. This client will only be able to access routes via the right branch of the network and not the left branch because the branches are only joined through an existing client, which does not route traffic from other clients:

```
┌─────┐
│ C │
└┬───┬┘
│ │
┌────┴┐ ┌┴────┐
│ S │ │ S │
└──┬──┘ └──┬──┘
│ │
┌──┴──┐ ┌──┴──┐
│ S │ │ S ◄───────┐
└─────┘ └─────┘ │
┌──┴─┐
│ C │
└────┘
```

You may also need to manually edit the resulting `wiretap.conf` for the new client to remove any `AllowedIPs` entries that already exist in the new client's host routing table. If the server that the client is attaching to has a route for 10.2.0.0/16, but the Client already has that route (because that's where it lives), then remove the `10.2.0.0/16` entry from the `wiretap.conf` file before importing into WireGuard. Leave the API address and any other routes you wish to access.
62 changes: 56 additions & 6 deletions src/cmd/add_client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"errors"
"fmt"
"log"
"net"
Expand All @@ -21,6 +22,7 @@ type addClientCmdConfig struct {
inputConfigFileE2EE string
outputConfigFileRelay string
outputConfigFileE2EE string
serverAddress string
mtu int
}

Expand All @@ -29,6 +31,7 @@ var addClientCmdArgs = addClientCmdConfig{
inputConfigFileE2EE: ConfigE2EE,
outputConfigFileRelay: ConfigRelay,
outputConfigFileE2EE: ConfigE2EE,
serverAddress: "",
mtu: MTU,
}

Expand All @@ -49,6 +52,7 @@ func init() {
addClientCmd.Flags().StringVarP(&addClientCmdArgs.outputConfigFileE2EE, "e2ee-output", "", addClientCmdArgs.outputConfigFileE2EE, "filename of output E2EE config file")
addClientCmd.Flags().StringVarP(&addClientCmdArgs.inputConfigFileRelay, "relay-input", "", addClientCmdArgs.inputConfigFileRelay, "filename of input relay config file")
addClientCmd.Flags().StringVarP(&addClientCmdArgs.inputConfigFileE2EE, "e2ee-input", "", addClientCmdArgs.inputConfigFileE2EE, "filename of input E2EE config file")
addClientCmd.Flags().StringVarP(&addClientCmdArgs.serverAddress, "server-address", "s", addClientCmdArgs.serverAddress, "API address of server that new client will connect to. By default new clients connect to existing relay servers")
addClientCmd.Flags().IntVarP(&addClientCmdArgs.mtu, "mtu", "m", addClientCmdArgs.mtu, "tunnel MTU")

addClientCmd.Flags().SortFlags = false
Expand All @@ -71,7 +75,7 @@ func (c addClientCmdConfig) Run() {
check("failed to retrieve address allocation from server", err)

disableV6 := false
if len(baseConfigE2EE.GetPeers()[0].GetAllowedIPs()) < 3 {
if len(baseConfigE2EE.GetAddresses()) == 1 {
disableV6 = true
}

Expand All @@ -98,15 +102,45 @@ func (c addClientCmdConfig) Run() {
check("failed to generate relay e2ee config", err)

// Copy peers.
for _, p := range baseConfigRelay.GetPeers() {
clientConfigRelay.AddPeer(p)
leafAddr := baseConfigRelay.GetAddresses()[0].IP
if c.serverAddress == "" {
for _, p := range baseConfigRelay.GetPeers() {
clientConfigRelay.AddPeer(p)
}
} else {
// Get leaf server info
leafApiAddr, err := netip.ParseAddr(c.serverAddress)
check("invalid server address", err)
leafApiAddrPort := netip.AddrPortFrom(leafApiAddr, uint16(ApiPort))
leafServerConfigRelay, _, err := api.ServerInfo(leafApiAddrPort)
check("failed to get leaf server info", err)
leafServerPeerConfigRelay, err := leafServerConfigRelay.AsPeer()
check("failed to parse client server config as peer", err)

// Search base relay config for this server's relay peer and copy routes.
out:
for _, p := range baseConfigRelay.GetPeers() {
for _, a := range p.GetAllowedIPs() {
if a.Contains(leafServerConfigRelay.GetAddresses()[0].IP) {
for _, aip := range p.GetAllowedIPs() {
err = leafServerPeerConfigRelay.AddAllowedIPs(aip.String())
check("failed to copy routes from leaf server", err)
}
break out
}
}
}

clientConfigRelay.AddPeer(leafServerPeerConfigRelay)

leafAddr = leafServerConfigRelay.GetAddresses()[0].IP
}
for _, p := range baseConfigE2EE.GetPeers() {
clientConfigE2EE.AddPeer(p)
}

// Push new client peer to all servers.
// Relay nodes need a new relay peeer on top of the e2ee peer.
// Relay nodes need a new relay peer on top of the e2ee peer.
// Relay nodes have a relay peer that matches our baseConfig public key.
clientPubKey, err := wgtypes.ParseKey(baseConfigRelay.GetPublicKey())
check("failed to get client public key", err)
Expand Down Expand Up @@ -151,7 +185,7 @@ func (c addClientCmdConfig) Run() {
})
check("failed to parse client as peer", err)

for _, p := range baseConfigE2EE.GetPeers() {
for _, p := range clientConfigE2EE.GetPeers() {
apiAddrPort := netip.AddrPortFrom(p.GetApiAddr(), uint16(ApiPort))
relay, _, err := api.ServerInfo(apiAddrPort)
if err != nil {
Expand All @@ -164,9 +198,25 @@ func (c addClientCmdConfig) Run() {
check("failed to add peer", err)

// This is a relay node.
if relay.GetPeer(clientPubKey) != nil {
if (relay.GetPeer(clientPubKey) != nil && c.serverAddress == "") || (c.serverAddress == p.GetApiAddr().String()) {
err = api.AddRelayPeer(apiAddrPort, clientPeerConfigRelay)
check("failed to add peer", err)
} else {
// This is an e2ee node. Add client IP to client/leaf-facing relay peer.
// Find client-facing relay peer.
outer:
for i, rp := range relay.GetPeers() {
for _, ap := range rp.GetAllowedIPs() {
if ap.Contains(leafAddr) {
err = api.AddAllowedIPs(apiAddrPort, rp.GetPublicKey(), clientPeerConfigRelay.GetAllowedIPs())
check("failed to add new client IP to peer", err)
break outer
}
}
if i == len(relay.GetPeers())-1 {
check("failed to find client-facing peer", errors.New("peer's relay interface has no client-facing route"))
}
}
}
}

Expand Down
59 changes: 36 additions & 23 deletions src/cmd/add_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"errors"
"fmt"
"log"
"net"
"net/netip"
"os"
Expand All @@ -26,7 +27,7 @@ type addServerCmdConfig struct {
}

var addServerCmdArgs = addServerCmdConfig{
allowedIPs: []string{ClientRelaySubnet4.String(), ClientRelaySubnet6.String()},
allowedIPs: []string{},
serverAddress: "",
configFileRelay: ConfigRelay,
configFileE2EE: ConfigE2EE,
Expand Down Expand Up @@ -210,12 +211,20 @@ func (c addServerCmdConfig) Run() {
check("failed to set endpoint", err)
}
}
relayAddrs := []string{ClientRelaySubnet4.String()}
if !disableV6 {
relayAddrs = append(relayAddrs, ClientRelaySubnet6.String())

// Make allowed IPs all of current peer's allowed IPs:
relayAddrs := []string{}
for _, p := range leafServerConfigRelay.GetPeers() {
for _, aip := range p.GetAllowedIPs() {
relayAddrs = append(relayAddrs, aip.String())
}
}
for _, a := range leafServerConfigRelay.GetAddresses() {
relayAddrs = append(relayAddrs, a.String())
}
err = leafServerPeerConfigRelay.SetAllowedIPs(relayAddrs)
check("failed to set allowedIPs", err)

serverConfigRelay.AddPeer(leafServerPeerConfigRelay)
serverConfigE2EE.AddPeer(clientPeerConfigE2EE)

Expand Down Expand Up @@ -263,30 +272,34 @@ func (c addServerCmdConfig) Run() {
err = serverConfigE2EE.SetAddresses([]string{fmt.Sprintf("%s/%d", addresses.ApiAddr.String(), addresses.ApiAddr.BitLen())})
check("failed to set addresses", err)

// Update routes for every node in path to new server (after getting addresses)
serverApi := apiAddrPort
outer:
for serverApi != leafApiAddrPort {
relay, _, err := api.ServerInfo(serverApi)
check("failed to get server info from intermediate node", err)
// Push new route to every server.
for _, p := range clientConfigE2EE.GetPeers() {
apiAddrPort := netip.AddrPortFrom(p.GetApiAddr(), uint16(ApiPort))
// Skip leaf and new peer.
if apiAddrPort == leafApiAddrPort || p.GetApiAddr() == serverPeerConfigE2EE.GetApiAddr() {
continue
}

for _, p := range relay.GetPeers() {
for _, ap := range p.GetAllowedIPs() {
relay, _, err := api.ServerInfo(apiAddrPort)
if err != nil {
log.Println("failed to query server info:", err)
continue
}

// Find leaf-facing relay peer and push route.
outer:
for i, rp := range relay.GetPeers() {
for _, ap := range rp.GetAllowedIPs() {
if ap.Contains(leafServerConfigRelay.GetAddresses()[0].IP) {
err = api.AddAllowedIPs(serverApi, p.GetPublicKey(), serverConfigRelay.GetAddresses())
check("failed to add allowedips", err)
// Find which of our E2EE peers has an endpoint that matches the first Allowed IP of this peer:
for _, e2ee_p := range clientConfigE2EE.GetPeers() {
if p.GetAllowedIPs()[0].Contains(e2ee_p.GetEndpoint().IP) {
aa := e2ee_p.GetApiAddr()
serverApi = netip.MustParseAddrPort(net.JoinHostPort(aa.String(), fmt.Sprint(ApiPort)))
continue outer
}
}
err = api.AddAllowedIPs(apiAddrPort, rp.GetPublicKey(), serverPeerConfigRelay.GetAllowedIPs())
check("failed to add new client IP to peer", err)
break outer
}
}
if i == len(relay.GetPeers())-1 {
check("failed to find leaf-facing peer", errors.New("peer's relay interface has no leaf-facing route"))
}
}
check("", errors.New("could not update routes along path, peer not found"))
}

// Leaf server is the relay peer for the new server.
Expand Down
6 changes: 3 additions & 3 deletions src/cmd/status.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cmd

import (
"errors"
"fmt"
"net/netip"
"strings"
Expand Down Expand Up @@ -93,14 +92,15 @@ func (c statusCmdConfig) Run() {
for _, rp := range current.relayConfig.GetPeers() {
// Skip client-facing peers.
for _, ip := range rp.GetAllowedIPs() {
if ClientRelaySubnet4.Contains(netip.MustParseAddr(ip.IP.String())) || ClientRelaySubnet6.Contains(netip.MustParseAddr(ip.IP.String())) {
if clientConfigRelay.GetAddresses()[0].Contains(ip.IP) {
continue outer
}
}

next, ok := nodes[rp.GetPublicKey().String()]
// Not a peer we know about. Could be another client or an error.
if !ok {
check("failed to find relay peer", errors.New("public key not returned by any node"))
continue
}
current.children = append(current.children, &next)
findChildren(&next)
Expand Down