Skip to content

Commit 67e3e20

Browse files
authored
Add client to any server (#25)
* Allow additional clients to attach to any server
1 parent 1ddf48b commit 67e3e20

File tree

4 files changed

+123
-32
lines changed

4 files changed

+123
-32
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,3 +617,31 @@ Finally, run Wiretap with the forwarded local port as your endpoint on the serve
617617
```bash
618618
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
619619
```
620+
621+
### Add Clients To Any Server
622+
623+
> **Note**
624+
> Clients added to arbitrary servers do not currently have the same capabilities as clients added to first-hop servers (the default)
625+
626+
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.
627+
628+
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:
629+
630+
```
631+
┌─────┐
632+
│ C │
633+
└┬───┬┘
634+
│ │
635+
┌────┴┐ ┌┴────┐
636+
│ S │ │ S │
637+
└──┬──┘ └──┬──┘
638+
│ │
639+
┌──┴──┐ ┌──┴──┐
640+
│ S │ │ S ◄───────┐
641+
└─────┘ └─────┘ │
642+
┌──┴─┐
643+
│ C │
644+
└────┘
645+
```
646+
647+
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.

src/cmd/add_client.go

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"errors"
45
"fmt"
56
"log"
67
"net"
@@ -21,6 +22,7 @@ type addClientCmdConfig struct {
2122
inputConfigFileE2EE string
2223
outputConfigFileRelay string
2324
outputConfigFileE2EE string
25+
serverAddress string
2426
mtu int
2527
}
2628

@@ -29,6 +31,7 @@ var addClientCmdArgs = addClientCmdConfig{
2931
inputConfigFileE2EE: ConfigE2EE,
3032
outputConfigFileRelay: ConfigRelay,
3133
outputConfigFileE2EE: ConfigE2EE,
34+
serverAddress: "",
3235
mtu: MTU,
3336
}
3437

@@ -49,6 +52,7 @@ func init() {
4952
addClientCmd.Flags().StringVarP(&addClientCmdArgs.outputConfigFileE2EE, "e2ee-output", "", addClientCmdArgs.outputConfigFileE2EE, "filename of output E2EE config file")
5053
addClientCmd.Flags().StringVarP(&addClientCmdArgs.inputConfigFileRelay, "relay-input", "", addClientCmdArgs.inputConfigFileRelay, "filename of input relay config file")
5154
addClientCmd.Flags().StringVarP(&addClientCmdArgs.inputConfigFileE2EE, "e2ee-input", "", addClientCmdArgs.inputConfigFileE2EE, "filename of input E2EE config file")
55+
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")
5256
addClientCmd.Flags().IntVarP(&addClientCmdArgs.mtu, "mtu", "m", addClientCmdArgs.mtu, "tunnel MTU")
5357

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

7377
disableV6 := false
74-
if len(baseConfigE2EE.GetPeers()[0].GetAllowedIPs()) < 3 {
78+
if len(baseConfigE2EE.GetAddresses()) == 1 {
7579
disableV6 = true
7680
}
7781

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

100104
// Copy peers.
101-
for _, p := range baseConfigRelay.GetPeers() {
102-
clientConfigRelay.AddPeer(p)
105+
leafAddr := baseConfigRelay.GetAddresses()[0].IP
106+
if c.serverAddress == "" {
107+
for _, p := range baseConfigRelay.GetPeers() {
108+
clientConfigRelay.AddPeer(p)
109+
}
110+
} else {
111+
// Get leaf server info
112+
leafApiAddr, err := netip.ParseAddr(c.serverAddress)
113+
check("invalid server address", err)
114+
leafApiAddrPort := netip.AddrPortFrom(leafApiAddr, uint16(ApiPort))
115+
leafServerConfigRelay, _, err := api.ServerInfo(leafApiAddrPort)
116+
check("failed to get leaf server info", err)
117+
leafServerPeerConfigRelay, err := leafServerConfigRelay.AsPeer()
118+
check("failed to parse client server config as peer", err)
119+
120+
// Search base relay config for this server's relay peer and copy routes.
121+
out:
122+
for _, p := range baseConfigRelay.GetPeers() {
123+
for _, a := range p.GetAllowedIPs() {
124+
if a.Contains(leafServerConfigRelay.GetAddresses()[0].IP) {
125+
for _, aip := range p.GetAllowedIPs() {
126+
err = leafServerPeerConfigRelay.AddAllowedIPs(aip.String())
127+
check("failed to copy routes from leaf server", err)
128+
}
129+
break out
130+
}
131+
}
132+
}
133+
134+
clientConfigRelay.AddPeer(leafServerPeerConfigRelay)
135+
136+
leafAddr = leafServerConfigRelay.GetAddresses()[0].IP
103137
}
104138
for _, p := range baseConfigE2EE.GetPeers() {
105139
clientConfigE2EE.AddPeer(p)
106140
}
107141

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

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

166200
// This is a relay node.
167-
if relay.GetPeer(clientPubKey) != nil {
201+
if (relay.GetPeer(clientPubKey) != nil && c.serverAddress == "") || (c.serverAddress == p.GetApiAddr().String()) {
168202
err = api.AddRelayPeer(apiAddrPort, clientPeerConfigRelay)
169203
check("failed to add peer", err)
204+
} else {
205+
// This is an e2ee node. Add client IP to client/leaf-facing relay peer.
206+
// Find client-facing relay peer.
207+
outer:
208+
for i, rp := range relay.GetPeers() {
209+
for _, ap := range rp.GetAllowedIPs() {
210+
if ap.Contains(leafAddr) {
211+
err = api.AddAllowedIPs(apiAddrPort, rp.GetPublicKey(), clientPeerConfigRelay.GetAllowedIPs())
212+
check("failed to add new client IP to peer", err)
213+
break outer
214+
}
215+
}
216+
if i == len(relay.GetPeers())-1 {
217+
check("failed to find client-facing peer", errors.New("peer's relay interface has no client-facing route"))
218+
}
219+
}
170220
}
171221
}
172222

src/cmd/add_server.go

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"errors"
55
"fmt"
6+
"log"
67
"net"
78
"net/netip"
89
"os"
@@ -26,7 +27,7 @@ type addServerCmdConfig struct {
2627
}
2728

2829
var addServerCmdArgs = addServerCmdConfig{
29-
allowedIPs: []string{ClientRelaySubnet4.String(), ClientRelaySubnet6.String()},
30+
allowedIPs: []string{},
3031
serverAddress: "",
3132
configFileRelay: ConfigRelay,
3233
configFileE2EE: ConfigE2EE,
@@ -210,12 +211,20 @@ func (c addServerCmdConfig) Run() {
210211
check("failed to set endpoint", err)
211212
}
212213
}
213-
relayAddrs := []string{ClientRelaySubnet4.String()}
214-
if !disableV6 {
215-
relayAddrs = append(relayAddrs, ClientRelaySubnet6.String())
214+
215+
// Make allowed IPs all of current peer's allowed IPs:
216+
relayAddrs := []string{}
217+
for _, p := range leafServerConfigRelay.GetPeers() {
218+
for _, aip := range p.GetAllowedIPs() {
219+
relayAddrs = append(relayAddrs, aip.String())
220+
}
221+
}
222+
for _, a := range leafServerConfigRelay.GetAddresses() {
223+
relayAddrs = append(relayAddrs, a.String())
216224
}
217225
err = leafServerPeerConfigRelay.SetAllowedIPs(relayAddrs)
218226
check("failed to set allowedIPs", err)
227+
219228
serverConfigRelay.AddPeer(leafServerPeerConfigRelay)
220229
serverConfigE2EE.AddPeer(clientPeerConfigE2EE)
221230

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

266-
// Update routes for every node in path to new server (after getting addresses)
267-
serverApi := apiAddrPort
268-
outer:
269-
for serverApi != leafApiAddrPort {
270-
relay, _, err := api.ServerInfo(serverApi)
271-
check("failed to get server info from intermediate node", err)
275+
// Push new route to every server.
276+
for _, p := range clientConfigE2EE.GetPeers() {
277+
apiAddrPort := netip.AddrPortFrom(p.GetApiAddr(), uint16(ApiPort))
278+
// Skip leaf and new peer.
279+
if apiAddrPort == leafApiAddrPort || p.GetApiAddr() == serverPeerConfigE2EE.GetApiAddr() {
280+
continue
281+
}
272282

273-
for _, p := range relay.GetPeers() {
274-
for _, ap := range p.GetAllowedIPs() {
283+
relay, _, err := api.ServerInfo(apiAddrPort)
284+
if err != nil {
285+
log.Println("failed to query server info:", err)
286+
continue
287+
}
288+
289+
// Find leaf-facing relay peer and push route.
290+
outer:
291+
for i, rp := range relay.GetPeers() {
292+
for _, ap := range rp.GetAllowedIPs() {
275293
if ap.Contains(leafServerConfigRelay.GetAddresses()[0].IP) {
276-
err = api.AddAllowedIPs(serverApi, p.GetPublicKey(), serverConfigRelay.GetAddresses())
277-
check("failed to add allowedips", err)
278-
// Find which of our E2EE peers has an endpoint that matches the first Allowed IP of this peer:
279-
for _, e2ee_p := range clientConfigE2EE.GetPeers() {
280-
if p.GetAllowedIPs()[0].Contains(e2ee_p.GetEndpoint().IP) {
281-
aa := e2ee_p.GetApiAddr()
282-
serverApi = netip.MustParseAddrPort(net.JoinHostPort(aa.String(), fmt.Sprint(ApiPort)))
283-
continue outer
284-
}
285-
}
294+
err = api.AddAllowedIPs(apiAddrPort, rp.GetPublicKey(), serverPeerConfigRelay.GetAllowedIPs())
295+
check("failed to add new client IP to peer", err)
296+
break outer
286297
}
287298
}
299+
if i == len(relay.GetPeers())-1 {
300+
check("failed to find leaf-facing peer", errors.New("peer's relay interface has no leaf-facing route"))
301+
}
288302
}
289-
check("", errors.New("could not update routes along path, peer not found"))
290303
}
291304

292305
// Leaf server is the relay peer for the new server.

src/cmd/status.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package cmd
22

33
import (
4-
"errors"
54
"fmt"
65
"net/netip"
76
"strings"
@@ -93,14 +92,15 @@ func (c statusCmdConfig) Run() {
9392
for _, rp := range current.relayConfig.GetPeers() {
9493
// Skip client-facing peers.
9594
for _, ip := range rp.GetAllowedIPs() {
96-
if ClientRelaySubnet4.Contains(netip.MustParseAddr(ip.IP.String())) || ClientRelaySubnet6.Contains(netip.MustParseAddr(ip.IP.String())) {
95+
if clientConfigRelay.GetAddresses()[0].Contains(ip.IP) {
9796
continue outer
9897
}
9998
}
10099

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

0 commit comments

Comments
 (0)