diff --git a/.github/workflows/gofmt.yml b/.github/workflows/gofmt.yml index acb324678..1552cc6b0 100644 --- a/.github/workflows/gofmt.yml +++ b/.github/workflows/gofmt.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 809670c83..ef4e507df 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: name: Build Linux/BSD All runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: @@ -33,7 +33,7 @@ jobs: name: Build Windows runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: @@ -66,7 +66,7 @@ jobs: HAS_SIGNING_CREDS: ${{ secrets.AC_USERNAME != '' }} runs-on: macos-11 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: @@ -114,7 +114,7 @@ jobs: needs: [build-linux, build-darwin, build-windows] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download artifacts uses: actions/download-artifact@v3 diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 422259b5a..f4ee02c1a 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: diff --git a/.github/workflows/smoke/build-relay.sh b/.github/workflows/smoke/build-relay.sh index 1ec23c775..70b07f4e3 100755 --- a/.github/workflows/smoke/build-relay.sh +++ b/.github/workflows/smoke/build-relay.sh @@ -41,4 +41,4 @@ EOF ../../../../nebula-cert sign -name "host4" -groups "host,host4" -ip "192.168.100.4/24" ) -sudo docker build -t nebula:smoke-relay . +docker build -t nebula:smoke-relay . diff --git a/.github/workflows/smoke/build.sh b/.github/workflows/smoke/build.sh index 00b2346e9..9cbb20058 100755 --- a/.github/workflows/smoke/build.sh +++ b/.github/workflows/smoke/build.sh @@ -36,4 +36,4 @@ mkdir ./build ../../../../nebula-cert sign -name "host4" -groups "host,host4" -ip "192.168.100.4/24" ) -sudo docker build -t "nebula:${NAME:-smoke}" . +docker build -t "nebula:${NAME:-smoke}" . diff --git a/.github/workflows/smoke/smoke-relay.sh b/.github/workflows/smoke/smoke-relay.sh index 91954d627..8926091ee 100755 --- a/.github/workflows/smoke/smoke-relay.sh +++ b/.github/workflows/smoke/smoke-relay.sh @@ -14,24 +14,24 @@ cleanup() { set +e if [ "$(jobs -r)" ] then - sudo docker kill lighthouse1 host2 host3 host4 + docker kill lighthouse1 host2 host3 host4 fi } trap cleanup EXIT -sudo docker run --name lighthouse1 --rm nebula:smoke-relay -config lighthouse1.yml -test -sudo docker run --name host2 --rm nebula:smoke-relay -config host2.yml -test -sudo docker run --name host3 --rm nebula:smoke-relay -config host3.yml -test -sudo docker run --name host4 --rm nebula:smoke-relay -config host4.yml -test +docker run --name lighthouse1 --rm nebula:smoke-relay -config lighthouse1.yml -test +docker run --name host2 --rm nebula:smoke-relay -config host2.yml -test +docker run --name host3 --rm nebula:smoke-relay -config host3.yml -test +docker run --name host4 --rm nebula:smoke-relay -config host4.yml -test -sudo docker run --name lighthouse1 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm nebula:smoke-relay -config lighthouse1.yml 2>&1 | tee logs/lighthouse1 | sed -u 's/^/ [lighthouse1] /' & +docker run --name lighthouse1 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm nebula:smoke-relay -config lighthouse1.yml 2>&1 | tee logs/lighthouse1 | sed -u 's/^/ [lighthouse1] /' & sleep 1 -sudo docker run --name host2 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm nebula:smoke-relay -config host2.yml 2>&1 | tee logs/host2 | sed -u 's/^/ [host2] /' & +docker run --name host2 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm nebula:smoke-relay -config host2.yml 2>&1 | tee logs/host2 | sed -u 's/^/ [host2] /' & sleep 1 -sudo docker run --name host3 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm nebula:smoke-relay -config host3.yml 2>&1 | tee logs/host3 | sed -u 's/^/ [host3] /' & +docker run --name host3 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm nebula:smoke-relay -config host3.yml 2>&1 | tee logs/host3 | sed -u 's/^/ [host3] /' & sleep 1 -sudo docker run --name host4 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm nebula:smoke-relay -config host4.yml 2>&1 | tee logs/host4 | sed -u 's/^/ [host4] /' & +docker run --name host4 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm nebula:smoke-relay -config host4.yml 2>&1 | tee logs/host4 | sed -u 's/^/ [host4] /' & sleep 1 set +x @@ -39,43 +39,43 @@ echo echo " *** Testing ping from lighthouse1" echo set -x -sudo docker exec lighthouse1 ping -c1 192.168.100.2 -sudo docker exec lighthouse1 ping -c1 192.168.100.3 -sudo docker exec lighthouse1 ping -c1 192.168.100.4 +docker exec lighthouse1 ping -c1 192.168.100.2 +docker exec lighthouse1 ping -c1 192.168.100.3 +docker exec lighthouse1 ping -c1 192.168.100.4 set +x echo echo " *** Testing ping from host2" echo set -x -sudo docker exec host2 ping -c1 192.168.100.1 +docker exec host2 ping -c1 192.168.100.1 # Should fail because no relay configured in this direction -! sudo docker exec host2 ping -c1 192.168.100.3 -w5 || exit 1 -! sudo docker exec host2 ping -c1 192.168.100.4 -w5 || exit 1 +! docker exec host2 ping -c1 192.168.100.3 -w5 || exit 1 +! docker exec host2 ping -c1 192.168.100.4 -w5 || exit 1 set +x echo echo " *** Testing ping from host3" echo set -x -sudo docker exec host3 ping -c1 192.168.100.1 -sudo docker exec host3 ping -c1 192.168.100.2 -sudo docker exec host3 ping -c1 192.168.100.4 +docker exec host3 ping -c1 192.168.100.1 +docker exec host3 ping -c1 192.168.100.2 +docker exec host3 ping -c1 192.168.100.4 set +x echo echo " *** Testing ping from host4" echo set -x -sudo docker exec host4 ping -c1 192.168.100.1 +docker exec host4 ping -c1 192.168.100.1 # Should fail because relays not allowed -! sudo docker exec host4 ping -c1 192.168.100.2 -w5 || exit 1 -sudo docker exec host4 ping -c1 192.168.100.3 +! docker exec host4 ping -c1 192.168.100.2 -w5 || exit 1 +docker exec host4 ping -c1 192.168.100.3 -sudo docker exec host4 sh -c 'kill 1' -sudo docker exec host3 sh -c 'kill 1' -sudo docker exec host2 sh -c 'kill 1' -sudo docker exec lighthouse1 sh -c 'kill 1' +docker exec host4 sh -c 'kill 1' +docker exec host3 sh -c 'kill 1' +docker exec host2 sh -c 'kill 1' +docker exec lighthouse1 sh -c 'kill 1' sleep 1 if [ "$(jobs -r)" ] diff --git a/.github/workflows/smoke/smoke.sh b/.github/workflows/smoke/smoke.sh index 4aa802927..3177255bd 100755 --- a/.github/workflows/smoke/smoke.sh +++ b/.github/workflows/smoke/smoke.sh @@ -14,7 +14,7 @@ cleanup() { set +e if [ "$(jobs -r)" ] then - sudo docker kill lighthouse1 host2 host3 host4 + docker kill lighthouse1 host2 host3 host4 fi } @@ -22,51 +22,51 @@ trap cleanup EXIT CONTAINER="nebula:${NAME:-smoke}" -sudo docker run --name lighthouse1 --rm "$CONTAINER" -config lighthouse1.yml -test -sudo docker run --name host2 --rm "$CONTAINER" -config host2.yml -test -sudo docker run --name host3 --rm "$CONTAINER" -config host3.yml -test -sudo docker run --name host4 --rm "$CONTAINER" -config host4.yml -test +docker run --name lighthouse1 --rm "$CONTAINER" -config lighthouse1.yml -test +docker run --name host2 --rm "$CONTAINER" -config host2.yml -test +docker run --name host3 --rm "$CONTAINER" -config host3.yml -test +docker run --name host4 --rm "$CONTAINER" -config host4.yml -test -sudo docker run --name lighthouse1 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm "$CONTAINER" -config lighthouse1.yml 2>&1 | tee logs/lighthouse1 | sed -u 's/^/ [lighthouse1] /' & +docker run --name lighthouse1 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm "$CONTAINER" -config lighthouse1.yml 2>&1 | tee logs/lighthouse1 | sed -u 's/^/ [lighthouse1] /' & sleep 1 -sudo docker run --name host2 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm "$CONTAINER" -config host2.yml 2>&1 | tee logs/host2 | sed -u 's/^/ [host2] /' & +docker run --name host2 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm "$CONTAINER" -config host2.yml 2>&1 | tee logs/host2 | sed -u 's/^/ [host2] /' & sleep 1 -sudo docker run --name host3 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm "$CONTAINER" -config host3.yml 2>&1 | tee logs/host3 | sed -u 's/^/ [host3] /' & +docker run --name host3 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm "$CONTAINER" -config host3.yml 2>&1 | tee logs/host3 | sed -u 's/^/ [host3] /' & sleep 1 -sudo docker run --name host4 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm "$CONTAINER" -config host4.yml 2>&1 | tee logs/host4 | sed -u 's/^/ [host4] /' & +docker run --name host4 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm "$CONTAINER" -config host4.yml 2>&1 | tee logs/host4 | sed -u 's/^/ [host4] /' & sleep 1 # grab tcpdump pcaps for debugging -sudo docker exec lighthouse1 tcpdump -i nebula1 -q -w - -U 2>logs/lighthouse1.inside.log >logs/lighthouse1.inside.pcap & -sudo docker exec lighthouse1 tcpdump -i eth0 -q -w - -U 2>logs/lighthouse1.outside.log >logs/lighthouse1.outside.pcap & -sudo docker exec host2 tcpdump -i nebula1 -q -w - -U 2>logs/host2.inside.log >logs/host2.inside.pcap & -sudo docker exec host2 tcpdump -i eth0 -q -w - -U 2>logs/host2.outside.log >logs/host2.outside.pcap & -sudo docker exec host3 tcpdump -i nebula1 -q -w - -U 2>logs/host3.inside.log >logs/host3.inside.pcap & -sudo docker exec host3 tcpdump -i eth0 -q -w - -U 2>logs/host3.outside.log >logs/host3.outside.pcap & -sudo docker exec host4 tcpdump -i nebula1 -q -w - -U 2>logs/host4.inside.log >logs/host4.inside.pcap & -sudo docker exec host4 tcpdump -i eth0 -q -w - -U 2>logs/host4.outside.log >logs/host4.outside.pcap & - -sudo docker exec host2 ncat -nklv 0.0.0.0 2000 & -sudo docker exec host3 ncat -nklv 0.0.0.0 2000 & -sudo docker exec host2 ncat -e '/usr/bin/echo host2' -nkluv 0.0.0.0 3000 & -sudo docker exec host3 ncat -e '/usr/bin/echo host3' -nkluv 0.0.0.0 3000 & +docker exec lighthouse1 tcpdump -i nebula1 -q -w - -U 2>logs/lighthouse1.inside.log >logs/lighthouse1.inside.pcap & +docker exec lighthouse1 tcpdump -i eth0 -q -w - -U 2>logs/lighthouse1.outside.log >logs/lighthouse1.outside.pcap & +docker exec host2 tcpdump -i nebula1 -q -w - -U 2>logs/host2.inside.log >logs/host2.inside.pcap & +docker exec host2 tcpdump -i eth0 -q -w - -U 2>logs/host2.outside.log >logs/host2.outside.pcap & +docker exec host3 tcpdump -i nebula1 -q -w - -U 2>logs/host3.inside.log >logs/host3.inside.pcap & +docker exec host3 tcpdump -i eth0 -q -w - -U 2>logs/host3.outside.log >logs/host3.outside.pcap & +docker exec host4 tcpdump -i nebula1 -q -w - -U 2>logs/host4.inside.log >logs/host4.inside.pcap & +docker exec host4 tcpdump -i eth0 -q -w - -U 2>logs/host4.outside.log >logs/host4.outside.pcap & + +docker exec host2 ncat -nklv 0.0.0.0 2000 & +docker exec host3 ncat -nklv 0.0.0.0 2000 & +docker exec host2 ncat -e '/usr/bin/echo host2' -nkluv 0.0.0.0 3000 & +docker exec host3 ncat -e '/usr/bin/echo host3' -nkluv 0.0.0.0 3000 & set +x echo echo " *** Testing ping from lighthouse1" echo set -x -sudo docker exec lighthouse1 ping -c1 192.168.100.2 -sudo docker exec lighthouse1 ping -c1 192.168.100.3 +docker exec lighthouse1 ping -c1 192.168.100.2 +docker exec lighthouse1 ping -c1 192.168.100.3 set +x echo echo " *** Testing ping from host2" echo set -x -sudo docker exec host2 ping -c1 192.168.100.1 +docker exec host2 ping -c1 192.168.100.1 # Should fail because not allowed by host3 inbound firewall -! sudo docker exec host2 ping -c1 192.168.100.3 -w5 || exit 1 +! docker exec host2 ping -c1 192.168.100.3 -w5 || exit 1 set +x echo @@ -74,34 +74,34 @@ echo " *** Testing ncat from host2" echo set -x # Should fail because not allowed by host3 inbound firewall -! sudo docker exec host2 ncat -nzv -w5 192.168.100.3 2000 || exit 1 -! sudo docker exec host2 ncat -nzuv -w5 192.168.100.3 3000 | grep -q host3 || exit 1 +! docker exec host2 ncat -nzv -w5 192.168.100.3 2000 || exit 1 +! docker exec host2 ncat -nzuv -w5 192.168.100.3 3000 | grep -q host3 || exit 1 set +x echo echo " *** Testing ping from host3" echo set -x -sudo docker exec host3 ping -c1 192.168.100.1 -sudo docker exec host3 ping -c1 192.168.100.2 +docker exec host3 ping -c1 192.168.100.1 +docker exec host3 ping -c1 192.168.100.2 set +x echo echo " *** Testing ncat from host3" echo set -x -sudo docker exec host3 ncat -nzv -w5 192.168.100.2 2000 -sudo docker exec host3 ncat -nzuv -w5 192.168.100.2 3000 | grep -q host2 +docker exec host3 ncat -nzv -w5 192.168.100.2 2000 +docker exec host3 ncat -nzuv -w5 192.168.100.2 3000 | grep -q host2 set +x echo echo " *** Testing ping from host4" echo set -x -sudo docker exec host4 ping -c1 192.168.100.1 +docker exec host4 ping -c1 192.168.100.1 # Should fail because not allowed by host4 outbound firewall -! sudo docker exec host4 ping -c1 192.168.100.2 -w5 || exit 1 -! sudo docker exec host4 ping -c1 192.168.100.3 -w5 || exit 1 +! docker exec host4 ping -c1 192.168.100.2 -w5 || exit 1 +! docker exec host4 ping -c1 192.168.100.3 -w5 || exit 1 set +x echo @@ -109,10 +109,10 @@ echo " *** Testing ncat from host4" echo set -x # Should fail because not allowed by host4 outbound firewall -! sudo docker exec host4 ncat -nzv -w5 192.168.100.2 2000 || exit 1 -! sudo docker exec host4 ncat -nzv -w5 192.168.100.3 2000 || exit 1 -! sudo docker exec host4 ncat -nzuv -w5 192.168.100.2 3000 | grep -q host2 || exit 1 -! sudo docker exec host4 ncat -nzuv -w5 192.168.100.3 3000 | grep -q host3 || exit 1 +! docker exec host4 ncat -nzv -w5 192.168.100.2 2000 || exit 1 +! docker exec host4 ncat -nzv -w5 192.168.100.3 2000 || exit 1 +! docker exec host4 ncat -nzuv -w5 192.168.100.2 3000 | grep -q host2 || exit 1 +! docker exec host4 ncat -nzuv -w5 192.168.100.3 3000 | grep -q host3 || exit 1 set +x echo @@ -120,15 +120,15 @@ echo " *** Testing conntrack" echo set -x # host2 can ping host3 now that host3 pinged it first -sudo docker exec host2 ping -c1 192.168.100.3 +docker exec host2 ping -c1 192.168.100.3 # host4 can ping host2 once conntrack established -sudo docker exec host2 ping -c1 192.168.100.4 -sudo docker exec host4 ping -c1 192.168.100.2 +docker exec host2 ping -c1 192.168.100.4 +docker exec host4 ping -c1 192.168.100.2 -sudo docker exec host4 sh -c 'kill 1' -sudo docker exec host3 sh -c 'kill 1' -sudo docker exec host2 sh -c 'kill 1' -sudo docker exec lighthouse1 sh -c 'kill 1' +docker exec host4 sh -c 'kill 1' +docker exec host3 sh -c 'kill 1' +docker exec host2 sh -c 'kill 1' +docker exec lighthouse1 sh -c 'kill 1' sleep 1 if [ "$(jobs -r)" ] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1be159043..cc3725fa6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: @@ -72,7 +72,7 @@ jobs: os: [windows-latest, macos-11] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: diff --git a/cert/cert.go b/cert/cert.go index 24a75e3d4..4f1b776c0 100644 --- a/cert/cert.go +++ b/cert/cert.go @@ -272,6 +272,9 @@ func EncryptAndMarshalSigningPrivateKey(curve Curve, b []byte, passphrase []byte }, Ciphertext: ciphertext, }) + if err != nil { + return nil, err + } switch curve { case Curve_CURVE25519: diff --git a/cert/crypto.go b/cert/crypto.go index 94f4c48f7..3558e1a54 100644 --- a/cert/crypto.go +++ b/cert/crypto.go @@ -77,6 +77,9 @@ func aes256Decrypt(passphrase []byte, kdfParams *Argon2Parameters, data []byte) } gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } nonce, ciphertext, err := splitNonceCiphertext(data, gcm.NonceSize()) if err != nil { diff --git a/connection_manager.go b/connection_manager.go index 900db07cb..ce11f1966 100644 --- a/connection_manager.go +++ b/connection_manager.go @@ -231,7 +231,7 @@ func (n *connectionManager) migrateRelayUsed(oldhostinfo, newhostinfo *HostInfo) index = existing.LocalIndex switch r.Type { case TerminalType: - relayFrom = newhostinfo.vpnIp + relayFrom = n.intf.myVpnIp relayTo = existing.PeerIp case ForwardingType: relayFrom = existing.PeerIp @@ -256,7 +256,7 @@ func (n *connectionManager) migrateRelayUsed(oldhostinfo, newhostinfo *HostInfo) } switch r.Type { case TerminalType: - relayFrom = newhostinfo.vpnIp + relayFrom = n.intf.myVpnIp relayTo = r.PeerIp case ForwardingType: relayFrom = r.PeerIp diff --git a/e2e/handshakes_test.go b/e2e/handshakes_test.go index d02a1a733..022b5a3f1 100644 --- a/e2e/handshakes_test.go +++ b/e2e/handshakes_test.go @@ -610,6 +610,110 @@ func TestRehandshakingRelays(t *testing.T) { t.Logf("relayControl hostinfos got cleaned up!") } +func TestRehandshakingRelaysPrimary(t *testing.T) { + // This test is the same as TestRehandshakingRelays but one of the terminal types is a primary swap winner + ca, _, caKey, _ := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []*net.IPNet{}, []*net.IPNet{}, []string{}) + myControl, myVpnIpNet, _, _ := newSimpleServer(ca, caKey, "me ", net.IP{10, 0, 0, 128}, m{"relay": m{"use_relays": true}}) + relayControl, relayVpnIpNet, relayUdpAddr, relayConfig := newSimpleServer(ca, caKey, "relay ", net.IP{10, 0, 0, 1}, m{"relay": m{"am_relay": true}}) + theirControl, theirVpnIpNet, theirUdpAddr, _ := newSimpleServer(ca, caKey, "them ", net.IP{10, 0, 0, 2}, m{"relay": m{"use_relays": true}}) + + // Teach my how to get to the relay and that their can be reached via the relay + myControl.InjectLightHouseAddr(relayVpnIpNet.IP, relayUdpAddr) + myControl.InjectRelays(theirVpnIpNet.IP, []net.IP{relayVpnIpNet.IP}) + relayControl.InjectLightHouseAddr(theirVpnIpNet.IP, theirUdpAddr) + + // Build a router so we don't have to reason who gets which packet + r := router.NewR(t, myControl, relayControl, theirControl) + defer r.RenderFlow() + + // Start the servers + myControl.Start() + relayControl.Start() + theirControl.Start() + + t.Log("Trigger a handshake from me to them via the relay") + myControl.InjectTunUDPPacket(theirVpnIpNet.IP, 80, 80, []byte("Hi from me")) + + p := r.RouteForAllUntilTxTun(theirControl) + r.Log("Assert the tunnel works") + assertUdpPacket(t, []byte("Hi from me"), p, myVpnIpNet.IP, theirVpnIpNet.IP, 80, 80) + r.RenderHostmaps("working hostmaps", myControl, relayControl, theirControl) + + // When I update the certificate for the relay, both me and them will have 2 host infos for the relay, + // and the main host infos will not have any relay state to handle the me<->relay<->them tunnel. + r.Log("Renew relay certificate and spin until me and them sees it") + _, _, myNextPrivKey, myNextPEM := newTestCert(ca, caKey, "relay", time.Now(), time.Now().Add(5*time.Minute), relayVpnIpNet, nil, []string{"new group"}) + + caB, err := ca.MarshalToPEM() + if err != nil { + panic(err) + } + + relayConfig.Settings["pki"] = m{ + "ca": string(caB), + "cert": string(myNextPEM), + "key": string(myNextPrivKey), + } + rc, err := yaml.Marshal(relayConfig.Settings) + assert.NoError(t, err) + relayConfig.ReloadConfigString(string(rc)) + + for { + r.Log("Assert the tunnel works between myVpnIpNet and relayVpnIpNet") + assertTunnel(t, myVpnIpNet.IP, relayVpnIpNet.IP, myControl, relayControl, r) + c := myControl.GetHostInfoByVpnIp(iputil.Ip2VpnIp(relayVpnIpNet.IP), false) + if len(c.Cert.Details.Groups) != 0 { + // We have a new certificate now + r.Log("Certificate between my and relay is updated!") + break + } + + time.Sleep(time.Second) + } + + for { + r.Log("Assert the tunnel works between theirVpnIpNet and relayVpnIpNet") + assertTunnel(t, theirVpnIpNet.IP, relayVpnIpNet.IP, theirControl, relayControl, r) + c := theirControl.GetHostInfoByVpnIp(iputil.Ip2VpnIp(relayVpnIpNet.IP), false) + if len(c.Cert.Details.Groups) != 0 { + // We have a new certificate now + r.Log("Certificate between their and relay is updated!") + break + } + + time.Sleep(time.Second) + } + + r.Log("Assert the relay tunnel still works") + assertTunnel(t, theirVpnIpNet.IP, myVpnIpNet.IP, theirControl, myControl, r) + r.RenderHostmaps("working hostmaps", myControl, relayControl, theirControl) + // We should have two hostinfos on all sides + for len(myControl.GetHostmap().Indexes) != 2 { + t.Logf("Waiting for myControl hostinfos (%v != 2) to get cleaned up from lack of use...", len(myControl.GetHostmap().Indexes)) + r.Log("Assert the relay tunnel still works") + assertTunnel(t, theirVpnIpNet.IP, myVpnIpNet.IP, theirControl, myControl, r) + r.Log("yupitdoes") + time.Sleep(time.Second) + } + t.Logf("myControl hostinfos got cleaned up!") + for len(theirControl.GetHostmap().Indexes) != 2 { + t.Logf("Waiting for theirControl hostinfos (%v != 2) to get cleaned up from lack of use...", len(theirControl.GetHostmap().Indexes)) + r.Log("Assert the relay tunnel still works") + assertTunnel(t, theirVpnIpNet.IP, myVpnIpNet.IP, theirControl, myControl, r) + r.Log("yupitdoes") + time.Sleep(time.Second) + } + t.Logf("theirControl hostinfos got cleaned up!") + for len(relayControl.GetHostmap().Indexes) != 2 { + t.Logf("Waiting for relayControl hostinfos (%v != 2) to get cleaned up from lack of use...", len(relayControl.GetHostmap().Indexes)) + r.Log("Assert the relay tunnel still works") + assertTunnel(t, theirVpnIpNet.IP, myVpnIpNet.IP, theirControl, myControl, r) + r.Log("yupitdoes") + time.Sleep(time.Second) + } + t.Logf("relayControl hostinfos got cleaned up!") +} + func TestRehandshaking(t *testing.T) { ca, _, caKey, _ := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []*net.IPNet{}, []*net.IPNet{}, []string{}) myControl, myVpnIpNet, myUdpAddr, myConfig := newSimpleServer(ca, caKey, "me ", net.IP{10, 0, 0, 2}, nil) diff --git a/examples/config.yml b/examples/config.yml index 5c28fea7c..1cc94492f 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -171,7 +171,8 @@ punchy: # and has been deprecated for "preferred_ranges" #preferred_ranges: ["172.16.0.0/24"] -# sshd can expose informational and administrative functions via ssh this is a +# sshd can expose informational and administrative functions via ssh. This can expose informational and administrative +# functions, and allows manual tweaking of various network settings when debugging or testing. #sshd: # Toggles the feature #enabled: true diff --git a/go.mod b/go.mod index 5dcdcade4..ba57aa12f 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/google/gopacket v1.1.19 github.com/kardianos/service v1.2.2 - github.com/miekg/dns v1.1.55 + github.com/miekg/dns v1.1.56 github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f github.com/prometheus/client_golang v1.16.0 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 @@ -20,11 +20,11 @@ require ( github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/stretchr/testify v1.8.4 github.com/vishvananda/netlink v1.1.0 - golang.org/x/crypto v0.12.0 + golang.org/x/crypto v0.14.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 - golang.org/x/net v0.14.0 - golang.org/x/sys v0.11.0 - golang.org/x/term v0.11.0 + golang.org/x/net v0.17.0 + golang.org/x/sys v0.13.0 + golang.org/x/term v0.13.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b golang.zx2c4.com/wireguard/windows v0.5.3 @@ -44,7 +44,7 @@ require ( github.com/prometheus/procfs v0.10.1 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/vishvananda/netns v0.0.4 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/tools v0.8.0 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/tools v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f0fd4b851..445f18a65 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -148,16 +148,16 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -168,8 +168,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -177,7 +177,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -194,11 +194,11 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -207,8 +207,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/handshake_manager.go b/handshake_manager.go index 153d50b85..00321d67a 100644 --- a/handshake_manager.go +++ b/handshake_manager.go @@ -374,13 +374,13 @@ func (hm *HandshakeManager) GetOrHandshake(vpnIp iputil.VpnIp, cacheCb func(*Han // StartHandshake will ensure a handshake is currently being attempted for the provided vpn ip func (hm *HandshakeManager) StartHandshake(vpnIp iputil.VpnIp, cacheCb func(*HandshakeHostInfo)) *HostInfo { hm.Lock() - defer hm.Unlock() if hh, ok := hm.vpnIps[vpnIp]; ok { // We are already trying to handshake with this vpn ip if cacheCb != nil { cacheCb(hh) } + hm.Unlock() return hh.hostinfo } @@ -421,6 +421,7 @@ func (hm *HandshakeManager) StartHandshake(vpnIp iputil.VpnIp, cacheCb func(*Han } } + hm.Unlock() hm.lightHouse.QueryServer(vpnIp, hm.f) return hostinfo } diff --git a/overlay/tun_wintun_windows.go b/overlay/tun_wintun_windows.go index 9146c8817..a4061237c 100644 --- a/overlay/tun_wintun_windows.go +++ b/overlay/tun_wintun_windows.go @@ -54,9 +54,16 @@ func newWinTun(l *logrus.Logger, deviceName string, cidr *net.IPNet, defaultMTU return nil, fmt.Errorf("generate GUID failed: %w", err) } - tunDevice, err := wintun.CreateTUNWithRequestedGUID(deviceName, guid, defaultMTU) + var tunDevice wintun.Device + tunDevice, err = wintun.CreateTUNWithRequestedGUID(deviceName, guid, defaultMTU) if err != nil { - return nil, fmt.Errorf("create TUN device failed: %w", err) + // Windows 10 has an issue with unclean shutdowns not fully cleaning up the wintun device. + // Trying a second time resolves the issue. + l.WithError(err).Debug("Failed to create wintun device, retrying") + tunDevice, err = wintun.CreateTUNWithRequestedGUID(deviceName, guid, defaultMTU) + if err != nil { + return nil, fmt.Errorf("create TUN device failed: %w", err) + } } routeTree, err := makeRouteTree(l, routes, false) diff --git a/relay_manager.go b/relay_manager.go index 224135362..7aa06ccb4 100644 --- a/relay_manager.go +++ b/relay_manager.go @@ -179,6 +179,12 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N "vpnIp": h.vpnIp}) logMsg.Info("handleCreateRelayRequest") + // Is the source of the relay me? This should never happen, but did happen due to + // an issue migrating relays over to newly re-handshaked host info objects. + if from == f.myVpnIp { + logMsg.WithField("myIP", f.myVpnIp).Error("Discarding relay request from myself") + return + } // Is the target of the relay me? if target == f.myVpnIp { existingRelay, ok := h.relayState.QueryRelayForByIp(from) diff --git a/udp/udp_darwin.go b/udp/udp_darwin.go index afbf240d8..08e1b6a80 100644 --- a/udp/udp_darwin.go +++ b/udp/udp_darwin.go @@ -43,10 +43,15 @@ func NewListenConfig(multi bool) net.ListenConfig { } func (u *GenericConn) Rebind() error { - file, err := u.File() + rc, err := u.UDPConn.SyscallConn() if err != nil { return err } - return syscall.SetsockoptInt(int(file.Fd()), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, 0) + return rc.Control(func(fd uintptr) { + err := syscall.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, 0) + if err != nil { + u.l.WithError(err).Error("Failed to rebind udp socket") + } + }) }