From 6be0bad68a33cf5ece63a9213ce429057808e33d Mon Sep 17 00:00:00 2001 From: brad-defined <77982333+brad-defined@users.noreply.github.com> Date: Thu, 18 May 2023 14:13:32 -0400 Subject: [PATCH 01/52] Fix static_host_map DNS lookup Linux issue - put v4 addr into v6 slice(#877) --- remote_list.go | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/remote_list.go b/remote_list.go index 454071483..f2b0d12cb 100644 --- a/remote_list.go +++ b/remote_list.go @@ -582,20 +582,11 @@ func (r *RemoteList) unlockedCollect() { dnsAddrs := r.hr.GetIPs() for _, addr := range dnsAddrs { if r.shouldAdd == nil || r.shouldAdd(addr.Addr()) { - switch { - case addr.Addr().Is4(): - v4 := addr.Addr().As4() - addrs = append(addrs, &udp.Addr{ - IP: v4[:], - Port: addr.Port(), - }) - case addr.Addr().Is6(): - v6 := addr.Addr().As16() - addrs = append(addrs, &udp.Addr{ - IP: v6[:], - Port: addr.Port(), - }) - } + v6 := addr.Addr().As16() + addrs = append(addrs, &udp.Addr{ + IP: v6[:], + Port: addr.Port(), + }) } } From 165b671e70aabfd6f06d90f14e8ca4a3476e8ad6 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 18 May 2023 15:39:24 -0400 Subject: [PATCH 02/52] v1.7.1 (#878) Update CHANGELOG for Nebula v1.7.1 --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3febb3237..5c5e244ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.7.1] - 2023-05-18 + +### Fixed + +- Fix IPv4 addresses returned by `static_host_map` DNS lookup queries being + treated as IPv6 addresses. (#877) + ## [1.7.0] - 2023-05-17 ### Added @@ -475,7 +482,8 @@ created.) - Initial public release. -[Unreleased]: https://github.com/slackhq/nebula/compare/v1.7.0...HEAD +[Unreleased]: https://github.com/slackhq/nebula/compare/v1.7.1...HEAD +[1.7.1]: https://github.com/slackhq/nebula/releases/tag/v1.7.1 [1.7.0]: https://github.com/slackhq/nebula/releases/tag/v1.7.0 [1.6.1]: https://github.com/slackhq/nebula/releases/tag/v1.6.1 [1.6.0]: https://github.com/slackhq/nebula/releases/tag/v1.6.0 From 6d8c5f437cb718f840bad800d16fd67759db54da Mon Sep 17 00:00:00 2001 From: Wade Simmons Date: Tue, 23 May 2023 13:24:33 -0400 Subject: [PATCH 03/52] GitHub actions update setup-go (#881) This does caching for us, so we can remove our manual caching of modules --- .github/workflows/gofmt.yml | 17 +++--------- .github/workflows/release.yml | 33 +++++++++++------------ .github/workflows/smoke.yml | 17 +++--------- .github/workflows/test.yml | 51 +++++++++-------------------------- 4 files changed, 35 insertions(+), 83 deletions(-) diff --git a/.github/workflows/gofmt.yml b/.github/workflows/gofmt.yml index e1a49b608..acb324678 100644 --- a/.github/workflows/gofmt.yml +++ b/.github/workflows/gofmt.yml @@ -14,21 +14,12 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 - uses: actions/setup-go@v2 - with: - go-version: "1.20" - id: go - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/cache@v2 + - uses: actions/setup-go@v4 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-gofmt1.20-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-gofmt1.20- + go-version-file: 'go.mod' + check-latest: true - name: Install goimports run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81203adfb..05f9ee6fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,13 +10,12 @@ jobs: name: Build Linux All runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 - uses: actions/setup-go@v2 - with: - go-version: "1.20" + - uses: actions/checkout@v3 - - name: Checkout code - uses: actions/checkout@v2 + - uses: actions/setup-go@v4 + with: + go-version-file: 'go.mod' + check-latest: true - name: Build run: | @@ -34,13 +33,12 @@ jobs: name: Build Windows runs-on: windows-latest steps: - - name: Set up Go 1.20 - uses: actions/setup-go@v2 - with: - go-version: "1.20" + - uses: actions/checkout@v3 - - name: Checkout code - uses: actions/checkout@v2 + - uses: actions/setup-go@v4 + with: + go-version-file: 'go.mod' + check-latest: true - name: Build run: | @@ -68,13 +66,12 @@ jobs: HAS_SIGNING_CREDS: ${{ secrets.AC_USERNAME != '' }} runs-on: macos-11 steps: - - name: Set up Go 1.20 - uses: actions/setup-go@v2 - with: - go-version: "1.20" + - uses: actions/checkout@v3 - - name: Checkout code - uses: actions/checkout@v2 + - uses: actions/setup-go@v4 + with: + go-version-file: 'go.mod' + check-latest: true - name: Import certificates if: env.HAS_SIGNING_CREDS == 'true' diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index e1b1d59e3..422259b5a 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -18,21 +18,12 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 - uses: actions/setup-go@v2 - with: - go-version: "1.20" - id: go - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/cache@v2 + - uses: actions/setup-go@v4 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go1.20-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go1.20- + go-version-file: 'go.mod' + check-latest: true - name: build run: make bin-docker diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 05aff781d..1be159043 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,21 +18,12 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 - uses: actions/setup-go@v2 - with: - go-version: "1.20" - id: go - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/cache@v2 + - uses: actions/setup-go@v4 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go1.20-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go1.20- + go-version-file: 'go.mod' + check-latest: true - name: Build run: make all @@ -57,21 +48,12 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 - uses: actions/setup-go@v2 - with: - go-version: "1.20" - id: go - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/cache@v2 + - uses: actions/setup-go@v4 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go1.20-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go1.20- + go-version-file: 'go.mod' + check-latest: true - name: Build run: make bin-boringcrypto @@ -90,21 +72,12 @@ jobs: os: [windows-latest, macos-11] steps: - - name: Set up Go 1.20 - uses: actions/setup-go@v2 - with: - go-version: "1.20" - id: go - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/cache@v2 + - uses: actions/setup-go@v4 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go1.20-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go1.20- + go-version-file: 'go.mod' + check-latest: true - name: Build nebula run: go build ./cmd/nebula From 96f4dcaab831e562819da284bf6e3346e34919de Mon Sep 17 00:00:00 2001 From: brad-defined <77982333+brad-defined@users.noreply.github.com> Date: Wed, 31 May 2023 16:05:46 -0400 Subject: [PATCH 04/52] Fix reconfig freeze attempting to send to an unbuffered, unread channel (#886) * Fixes a reocnfig freeze where the reconfig attempts to send to an unbuffered channel with no readers. Only create stop channel when a DNS goroutine is created, and only send when the channel exists. Buffer to size 1 so that the stop message can be immediately sent even if the goroutine is busy doing DNS lookups. --- lighthouse.go | 12 ++++++++++++ lighthouse_test.go | 14 ++++++++++++-- remote_list.go | 13 ++++++------- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lighthouse.go b/lighthouse.go index 460a1cb53..da604f089 100644 --- a/lighthouse.go +++ b/lighthouse.go @@ -262,6 +262,18 @@ func (lh *LightHouse) reload(c *config.C, initial bool) error { //NOTE: many things will get much simpler when we combine static_host_map and lighthouse.hosts in config if initial || c.HasChanged("static_host_map") || c.HasChanged("static_map.cadence") || c.HasChanged("static_map.network") || c.HasChanged("static_map.lookup_timeout") { + // Clean up. Entries still in the static_host_map will be re-built. + // Entries no longer present must have their (possible) background DNS goroutines stopped. + if existingStaticList := lh.staticList.Load(); existingStaticList != nil { + lh.RLock() + for staticVpnIp := range *existingStaticList { + if am, ok := lh.addrMap[staticVpnIp]; ok && am != nil { + am.hr.Cancel() + } + } + lh.RUnlock() + } + // Build a new list based on current config. staticList := make(map[iputil.VpnIp]struct{}) err := lh.loadStaticMap(c, lh.myVpnNet, staticList) if err != nil { diff --git a/lighthouse_test.go b/lighthouse_test.go index aa4da4c0f..73632ac79 100644 --- a/lighthouse_test.go +++ b/lighthouse_test.go @@ -12,6 +12,7 @@ import ( "github.com/slackhq/nebula/test" "github.com/slackhq/nebula/udp" "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v2" ) //TODO: Add a test to ensure udpAddr is copied and not reused @@ -242,8 +243,17 @@ func TestLighthouse_reload(t *testing.T) { lh, err := NewLightHouseFromConfig(context.Background(), l, c, &net.IPNet{IP: net.IP{10, 128, 0, 1}, Mask: net.IPMask{255, 255, 255, 0}}, nil, nil) assert.NoError(t, err) - c.Settings["static_host_map"] = map[interface{}]interface{}{"10.128.0.2": []interface{}{"1.1.1.1:4242"}} - lh.reload(c, false) + nc := map[interface{}]interface{}{ + "static_host_map": map[interface{}]interface{}{ + "10.128.0.2": []interface{}{"1.1.1.1:4242"}, + }, + } + rc, err := yaml.Marshal(nc) + assert.NoError(t, err) + c.ReloadConfigString(string(rc)) + + err = lh.reload(c, false) + assert.NoError(t, err) } func newLHHostRequest(fromAddr *udp.Addr, myVpnIp, queryVpnIp iputil.VpnIp, lhh *LightHouseHandler) testLhReply { diff --git a/remote_list.go b/remote_list.go index f2b0d12cb..60a1afdaf 100644 --- a/remote_list.go +++ b/remote_list.go @@ -70,7 +70,7 @@ type hostnamesResults struct { hostnames []hostnamePort network string lookupTimeout time.Duration - stop chan struct{} + cancelFn func() l *logrus.Logger ips atomic.Pointer[map[netip.AddrPort]struct{}] } @@ -80,7 +80,6 @@ func NewHostnameResults(ctx context.Context, l *logrus.Logger, d time.Duration, hostnames: make([]hostnamePort, len(hostPorts)), network: network, lookupTimeout: timeout, - stop: make(chan (struct{})), l: l, } @@ -115,6 +114,8 @@ func NewHostnameResults(ctx context.Context, l *logrus.Logger, d time.Duration, // Time for the DNS lookup goroutine if performBackgroundLookup { + newCtx, cancel := context.WithCancel(ctx) + r.cancelFn = cancel ticker := time.NewTicker(d) go func() { defer ticker.Stop() @@ -154,9 +155,7 @@ func NewHostnameResults(ctx context.Context, l *logrus.Logger, d time.Duration, onUpdate() } select { - case <-ctx.Done(): - return - case <-r.stop: + case <-newCtx.Done(): return case <-ticker.C: continue @@ -169,8 +168,8 @@ func NewHostnameResults(ctx context.Context, l *logrus.Logger, d time.Duration, } func (hr *hostnamesResults) Cancel() { - if hr != nil { - hr.stop <- struct{}{} + if hr != nil && hr.cancelFn != nil { + hr.cancelFn() } } From 57eb80e9fbb09ac16f22b38b258a18005b7a6cb1 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Thu, 1 Jun 2023 10:05:07 -0500 Subject: [PATCH 05/52] v1.7.2 (#887) Update CHANGELOG for Nebula v1.7.2 --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c5e244ca..6951a4afa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.7.2] - 2023-06-01 + +### Fixed + +- Fix a freeze during config reload if the `static_host_map` config was changed. (#886) + ## [1.7.1] - 2023-05-18 ### Fixed @@ -482,7 +488,8 @@ created.) - Initial public release. -[Unreleased]: https://github.com/slackhq/nebula/compare/v1.7.1...HEAD +[Unreleased]: https://github.com/slackhq/nebula/compare/v1.7.2...HEAD +[1.7.2]: https://github.com/slackhq/nebula/releases/tag/v1.7.2 [1.7.1]: https://github.com/slackhq/nebula/releases/tag/v1.7.1 [1.7.0]: https://github.com/slackhq/nebula/releases/tag/v1.7.0 [1.6.1]: https://github.com/slackhq/nebula/releases/tag/v1.6.1 From 928731acfe398531d97130d54ee89cd92d037b77 Mon Sep 17 00:00:00 2001 From: Wade Simmons Date: Wed, 14 Jun 2023 11:45:01 -0400 Subject: [PATCH 06/52] fix up the release workflow (#891) actions/create-release is deprecated, just switch to using `gh` cli. This is actually much easier anyways! --- .github/workflows/release.yml | 212 +++------------------------------- 1 file changed, 17 insertions(+), 195 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 05f9ee6fd..a5590d3fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: mv build/*.tar.gz release - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: linux-latest path: release @@ -55,7 +55,7 @@ jobs: mv dist\windows\wintun build\dist\windows\ - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: windows-latest path: build @@ -104,7 +104,7 @@ jobs: fi - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: darwin-latest path: ./release/* @@ -114,12 +114,16 @@ jobs: needs: [build-linux, build-darwin, build-windows] runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3 + - name: Download artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 + with: + path: artifacts - name: Zip Windows run: | - cd windows-latest + cd artifacts/windows-latest cp windows-amd64/* . zip -r nebula-windows-amd64.zip nebula.exe nebula-cert.exe dist cp windows-arm64/* . @@ -127,6 +131,7 @@ jobs: - name: Create sha256sum run: | + cd artifacts for dir in linux-latest darwin-latest windows-latest do ( @@ -156,195 +161,12 @@ jobs: - name: Create Release id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false - - ## - ## Upload assets (I wish we could just upload the whole folder at once... - ## - - - name: Upload SHASUM256.txt - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./SHASUM256.txt - asset_name: SHASUM256.txt - asset_content_type: text/plain - - - name: Upload darwin zip - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./darwin-latest/nebula-darwin.zip - asset_name: nebula-darwin.zip - asset_content_type: application/zip - - - name: Upload windows-amd64 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./windows-latest/nebula-windows-amd64.zip - asset_name: nebula-windows-amd64.zip - asset_content_type: application/zip - - - name: Upload windows-arm64 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./windows-latest/nebula-windows-arm64.zip - asset_name: nebula-windows-arm64.zip - asset_content_type: application/zip - - - name: Upload linux-amd64 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-amd64.tar.gz - asset_name: nebula-linux-amd64.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-386 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-386.tar.gz - asset_name: nebula-linux-386.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-ppc64le - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-ppc64le.tar.gz - asset_name: nebula-linux-ppc64le.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-arm-5 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-arm-5.tar.gz - asset_name: nebula-linux-arm-5.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-arm-6 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-arm-6.tar.gz - asset_name: nebula-linux-arm-6.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-arm-7 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-arm-7.tar.gz - asset_name: nebula-linux-arm-7.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-arm64 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-arm64.tar.gz - asset_name: nebula-linux-arm64.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-mips - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-mips.tar.gz - asset_name: nebula-linux-mips.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-mipsle - uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-mipsle.tar.gz - asset_name: nebula-linux-mipsle.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-mips64 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-mips64.tar.gz - asset_name: nebula-linux-mips64.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-mips64le - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-mips64le.tar.gz - asset_name: nebula-linux-mips64le.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-mips-softfloat - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-mips-softfloat.tar.gz - asset_name: nebula-linux-mips-softfloat.tar.gz - asset_content_type: application/gzip - - - name: Upload linux-riscv64 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-linux-riscv64.tar.gz - asset_name: nebula-linux-riscv64.tar.gz - asset_content_type: application/gzip - - - name: Upload freebsd-amd64 - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./linux-latest/nebula-freebsd-amd64.tar.gz - asset_name: nebula-freebsd-amd64.tar.gz - asset_content_type: application/gzip + run: | + cd artifacts + gh release create \ + --verify-tag \ + --title "Release ${{ github.ref_name }}" \ + "${{ github.ref_name }}" \ + SHASUM256.txt *-latest/*.zip *-latest/*.tar.gz From 3bbf5f4e6746bb19e433b7e7978a43b0b94e6433 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Wed, 14 Jun 2023 10:48:52 -0500 Subject: [PATCH 07/52] Use an interface for udp conns (#901) --- connection_manager_test.go | 12 ++++++------ control_tester.go | 12 ++++++------ handshake_manager.go | 4 ++-- handshake_manager_test.go | 2 +- interface.go | 10 +++++----- lighthouse.go | 4 ++-- main.go | 2 +- udp/conn.go | 27 +++++++++++++++++++++++++++ udp/udp_android.go | 2 +- udp/udp_darwin.go | 2 +- udp/udp_freebsd.go | 2 +- udp/udp_generic.go | 20 ++++++++++---------- udp/udp_linux.go | 36 ++++++++++++++++++------------------ udp/udp_linux_32.go | 2 +- udp/udp_linux_64.go | 2 +- udp/udp_tester.go | 22 +++++++++++----------- udp/udp_windows.go | 2 +- 17 files changed, 95 insertions(+), 68 deletions(-) diff --git a/connection_manager_test.go b/connection_manager_test.go index 3a2561109..bfe57c8f0 100644 --- a/connection_manager_test.go +++ b/connection_manager_test.go @@ -54,10 +54,10 @@ func Test_NewConnectionManagerTest(t *testing.T) { ifce := &Interface{ hostMap: hostMap, inside: &test.NoopTun{}, - outside: &udp.Conn{}, + outside: &udp.NoopConn{}, firewall: &Firewall{}, lightHouse: lh, - handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.Conn{}, defaultHandshakeConfig), + handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), l: l, } ifce.certState.Store(cs) @@ -133,10 +133,10 @@ func Test_NewConnectionManagerTest2(t *testing.T) { ifce := &Interface{ hostMap: hostMap, inside: &test.NoopTun{}, - outside: &udp.Conn{}, + outside: &udp.NoopConn{}, firewall: &Firewall{}, lightHouse: lh, - handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.Conn{}, defaultHandshakeConfig), + handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), l: l, } ifce.certState.Store(cs) @@ -252,10 +252,10 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { ifce := &Interface{ hostMap: hostMap, inside: &test.NoopTun{}, - outside: &udp.Conn{}, + outside: &udp.NoopConn{}, firewall: &Firewall{}, lightHouse: lh, - handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.Conn{}, defaultHandshakeConfig), + handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), l: l, disconnectInvalid: true, caPool: ncp, diff --git a/control_tester.go b/control_tester.go index 48deb1379..340ba1c5e 100644 --- a/control_tester.go +++ b/control_tester.go @@ -21,7 +21,7 @@ import ( func (c *Control) WaitForType(msgType header.MessageType, subType header.MessageSubType, pipeTo *Control) { h := &header.H{} for { - p := c.f.outside.Get(true) + p := c.f.outside.(*udp.TesterConn).Get(true) if err := h.Parse(p.Data); err != nil { panic(err) } @@ -37,7 +37,7 @@ func (c *Control) WaitForType(msgType header.MessageType, subType header.Message func (c *Control) WaitForTypeByIndex(toIndex uint32, msgType header.MessageType, subType header.MessageSubType, pipeTo *Control) { h := &header.H{} for { - p := c.f.outside.Get(true) + p := c.f.outside.(*udp.TesterConn).Get(true) if err := h.Parse(p.Data); err != nil { panic(err) } @@ -90,11 +90,11 @@ func (c *Control) GetFromTun(block bool) []byte { // GetFromUDP will pull a udp packet off the udp side of nebula func (c *Control) GetFromUDP(block bool) *udp.Packet { - return c.f.outside.Get(block) + return c.f.outside.(*udp.TesterConn).Get(block) } func (c *Control) GetUDPTxChan() <-chan *udp.Packet { - return c.f.outside.TxPackets + return c.f.outside.(*udp.TesterConn).TxPackets } func (c *Control) GetTunTxChan() <-chan []byte { @@ -103,7 +103,7 @@ func (c *Control) GetTunTxChan() <-chan []byte { // InjectUDPPacket will inject a packet into the udp side of nebula func (c *Control) InjectUDPPacket(p *udp.Packet) { - c.f.outside.Send(p) + c.f.outside.(*udp.TesterConn).Send(p) } // InjectTunUDPPacket puts a udp packet on the tun interface. Using UDP here because it's a simpler protocol @@ -143,7 +143,7 @@ func (c *Control) GetVpnIp() iputil.VpnIp { } func (c *Control) GetUDPAddr() string { - return c.f.outside.Addr.String() + return c.f.outside.(*udp.TesterConn).Addr.String() } func (c *Control) KillPendingTunnel(vpnIp net.IP) bool { diff --git a/handshake_manager.go b/handshake_manager.go index b02fb28af..02b27bbda 100644 --- a/handshake_manager.go +++ b/handshake_manager.go @@ -45,7 +45,7 @@ type HandshakeManager struct { pendingHostMap *HostMap mainHostMap *HostMap lightHouse *LightHouse - outside *udp.Conn + outside udp.Conn config HandshakeConfig OutboundHandshakeTimer *LockingTimerWheel[iputil.VpnIp] messageMetrics *MessageMetrics @@ -57,7 +57,7 @@ type HandshakeManager struct { trigger chan iputil.VpnIp } -func NewHandshakeManager(l *logrus.Logger, tunCidr *net.IPNet, preferredRanges []*net.IPNet, mainHostMap *HostMap, lightHouse *LightHouse, outside *udp.Conn, config HandshakeConfig) *HandshakeManager { +func NewHandshakeManager(l *logrus.Logger, tunCidr *net.IPNet, preferredRanges []*net.IPNet, mainHostMap *HostMap, lightHouse *LightHouse, outside udp.Conn, config HandshakeConfig) *HandshakeManager { return &HandshakeManager{ pendingHostMap: NewHostMap(l, "pending", tunCidr, preferredRanges), mainHostMap: mainHostMap, diff --git a/handshake_manager_test.go b/handshake_manager_test.go index 3e39e48f4..612ea4470 100644 --- a/handshake_manager_test.go +++ b/handshake_manager_test.go @@ -23,7 +23,7 @@ func Test_NewHandshakeManagerVpnIp(t *testing.T) { mainHM := NewHostMap(l, "test", vpncidr, preferredRanges) lh := newTestLighthouse() - blah := NewHandshakeManager(l, tuncidr, preferredRanges, mainHM, lh, &udp.Conn{}, defaultHandshakeConfig) + blah := NewHandshakeManager(l, tuncidr, preferredRanges, mainHM, lh, &udp.NoopConn{}, defaultHandshakeConfig) now := time.Now() blah.NextOutboundHandshakeTimerTick(now, mw) diff --git a/interface.go b/interface.go index 220cb252b..82ab0f04c 100644 --- a/interface.go +++ b/interface.go @@ -26,7 +26,7 @@ const mtu = 9001 type InterfaceConfig struct { HostMap *HostMap - Outside *udp.Conn + Outside udp.Conn Inside overlay.Device certState *CertState Cipher string @@ -52,7 +52,7 @@ type InterfaceConfig struct { type Interface struct { hostMap *HostMap - outside *udp.Conn + outside udp.Conn inside overlay.Device certState atomic.Pointer[CertState] cipher string @@ -80,7 +80,7 @@ type Interface struct { conntrackCacheTimeout time.Duration - writers []*udp.Conn + writers []udp.Conn readers []io.ReadWriteCloser metricHandshakes metrics.Histogram @@ -167,7 +167,7 @@ func NewInterface(ctx context.Context, c *InterfaceConfig) (*Interface, error) { dropMulticast: c.DropMulticast, routines: c.routines, version: c.version, - writers: make([]*udp.Conn, c.routines), + writers: make([]udp.Conn, c.routines), readers: make([]io.ReadWriteCloser, c.routines), caPool: c.caPool, disconnectInvalid: c.disconnectInvalid, @@ -243,7 +243,7 @@ func (f *Interface) run() { func (f *Interface) listenOut(i int) { runtime.LockOSThread() - var li *udp.Conn + var li udp.Conn // TODO clean this up with a coherent interface for each outside connection if i > 0 { li = f.writers[i] diff --git a/lighthouse.go b/lighthouse.go index da604f089..f281f9b70 100644 --- a/lighthouse.go +++ b/lighthouse.go @@ -39,7 +39,7 @@ type LightHouse struct { myVpnIp iputil.VpnIp myVpnZeros iputil.VpnIp myVpnNet *net.IPNet - punchConn *udp.Conn + punchConn udp.Conn punchy *Punchy // Local cache of answers from light houses @@ -84,7 +84,7 @@ type LightHouse struct { // NewLightHouseFromConfig will build a Lighthouse struct from the values provided in the config object // addrMap should be nil unless this is during a config reload -func NewLightHouseFromConfig(ctx context.Context, l *logrus.Logger, c *config.C, myVpnNet *net.IPNet, pc *udp.Conn, p *Punchy) (*LightHouse, error) { +func NewLightHouseFromConfig(ctx context.Context, l *logrus.Logger, c *config.C, myVpnNet *net.IPNet, pc udp.Conn, p *Punchy) (*LightHouse, error) { amLighthouse := c.GetBool("lighthouse.am_lighthouse", false) nebulaPort := uint32(c.GetInt("listen.port", 0)) if amLighthouse && nebulaPort == 0 { diff --git a/main.go b/main.go index 4d604f5af..e4c262413 100644 --- a/main.go +++ b/main.go @@ -147,7 +147,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg } // set up our UDP listener - udpConns := make([]*udp.Conn, routines) + udpConns := make([]udp.Conn, routines) port := c.GetInt("listen.port", 0) if !configTest { diff --git a/udp/conn.go b/udp/conn.go index f967a9ad5..33520db37 100644 --- a/udp/conn.go +++ b/udp/conn.go @@ -1,6 +1,7 @@ package udp import ( + "github.com/slackhq/nebula/config" "github.com/slackhq/nebula/firewall" "github.com/slackhq/nebula/header" ) @@ -18,3 +19,29 @@ type EncReader func( q int, localCache firewall.ConntrackCache, ) + +type Conn interface { + Rebind() error + LocalAddr() (*Addr, error) + ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) + WriteTo(b []byte, addr *Addr) error + ReloadConfig(c *config.C) +} + +type NoopConn struct{} + +func (NoopConn) Rebind() error { + return nil +} +func (NoopConn) LocalAddr() (*Addr, error) { + return nil, nil +} +func (NoopConn) ListenOut(_ EncReader, _ LightHouseHandlerFunc, _ *firewall.ConntrackCacheTicker, _ int) { + return +} +func (NoopConn) WriteTo(_ []byte, _ *Addr) error { + return nil +} +func (NoopConn) ReloadConfig(_ *config.C) { + return +} diff --git a/udp/udp_android.go b/udp/udp_android.go index d2812a8af..08cde96c9 100644 --- a/udp/udp_android.go +++ b/udp/udp_android.go @@ -34,6 +34,6 @@ func NewListenConfig(multi bool) net.ListenConfig { } } -func (u *Conn) Rebind() error { +func (u *GenericConn) Rebind() error { return nil } diff --git a/udp/udp_darwin.go b/udp/udp_darwin.go index 69d0c58e7..260ce4435 100644 --- a/udp/udp_darwin.go +++ b/udp/udp_darwin.go @@ -37,7 +37,7 @@ func NewListenConfig(multi bool) net.ListenConfig { } } -func (u *Conn) Rebind() error { +func (u *GenericConn) Rebind() error { file, err := u.File() if err != nil { return err diff --git a/udp/udp_freebsd.go b/udp/udp_freebsd.go index 10ff94b40..920f91b6d 100644 --- a/udp/udp_freebsd.go +++ b/udp/udp_freebsd.go @@ -36,6 +36,6 @@ func NewListenConfig(multi bool) net.ListenConfig { } } -func (u *Conn) Rebind() error { +func (u *GenericConn) Rebind() error { return nil } diff --git a/udp/udp_generic.go b/udp/udp_generic.go index ff254eb3c..490cc61b8 100644 --- a/udp/udp_generic.go +++ b/udp/udp_generic.go @@ -18,30 +18,30 @@ import ( "github.com/slackhq/nebula/header" ) -type Conn struct { +type GenericConn struct { *net.UDPConn l *logrus.Logger } -func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (*Conn, error) { +func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) { lc := NewListenConfig(multi) pc, err := lc.ListenPacket(context.TODO(), "udp", net.JoinHostPort(ip.String(), fmt.Sprintf("%v", port))) if err != nil { return nil, err } if uc, ok := pc.(*net.UDPConn); ok { - return &Conn{UDPConn: uc, l: l}, nil + return &GenericConn{UDPConn: uc, l: l}, nil } return nil, fmt.Errorf("Unexpected PacketConn: %T %#v", pc, pc) } -func (uc *Conn) WriteTo(b []byte, addr *Addr) error { - _, err := uc.UDPConn.WriteToUDP(b, &net.UDPAddr{IP: addr.IP, Port: int(addr.Port)}) +func (u *GenericConn) WriteTo(b []byte, addr *Addr) error { + _, err := u.UDPConn.WriteToUDP(b, &net.UDPAddr{IP: addr.IP, Port: int(addr.Port)}) return err } -func (uc *Conn) LocalAddr() (*Addr, error) { - a := uc.UDPConn.LocalAddr() +func (u *GenericConn) LocalAddr() (*Addr, error) { + a := u.UDPConn.LocalAddr() switch v := a.(type) { case *net.UDPAddr: @@ -55,11 +55,11 @@ func (uc *Conn) LocalAddr() (*Addr, error) { } } -func (u *Conn) ReloadConfig(c *config.C) { +func (u *GenericConn) ReloadConfig(c *config.C) { // TODO } -func NewUDPStatsEmitter(udpConns []*Conn) func() { +func NewUDPStatsEmitter(udpConns []Conn) func() { // No UDP stats for non-linux return func() {} } @@ -68,7 +68,7 @@ type rawMessage struct { Len uint32 } -func (u *Conn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) { +func (u *GenericConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) { plaintext := make([]byte, MTU) buffer := make([]byte, MTU) h := &header.H{} diff --git a/udp/udp_linux.go b/udp/udp_linux.go index 26bbe3647..60defaa11 100644 --- a/udp/udp_linux.go +++ b/udp/udp_linux.go @@ -20,7 +20,7 @@ import ( //TODO: make it support reload as best you can! -type Conn struct { +type StdConn struct { sysFd int l *logrus.Logger batch int @@ -45,7 +45,7 @@ const ( type _SK_MEMINFO [_SK_MEMINFO_VARS]uint32 -func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (*Conn, error) { +func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) { syscall.ForkLock.RLock() fd, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP) if err == nil { @@ -77,30 +77,30 @@ func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) ( //v, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_INCOMING_CPU) //l.Println(v, err) - return &Conn{sysFd: fd, l: l, batch: batch}, err + return &StdConn{sysFd: fd, l: l, batch: batch}, err } -func (u *Conn) Rebind() error { +func (u *StdConn) Rebind() error { return nil } -func (u *Conn) SetRecvBuffer(n int) error { +func (u *StdConn) SetRecvBuffer(n int) error { return unix.SetsockoptInt(u.sysFd, unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, n) } -func (u *Conn) SetSendBuffer(n int) error { +func (u *StdConn) SetSendBuffer(n int) error { return unix.SetsockoptInt(u.sysFd, unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, n) } -func (u *Conn) GetRecvBuffer() (int, error) { +func (u *StdConn) GetRecvBuffer() (int, error) { return unix.GetsockoptInt(int(u.sysFd), unix.SOL_SOCKET, unix.SO_RCVBUF) } -func (u *Conn) GetSendBuffer() (int, error) { +func (u *StdConn) GetSendBuffer() (int, error) { return unix.GetsockoptInt(int(u.sysFd), unix.SOL_SOCKET, unix.SO_SNDBUF) } -func (u *Conn) LocalAddr() (*Addr, error) { +func (u *StdConn) LocalAddr() (*Addr, error) { sa, err := unix.Getsockname(u.sysFd) if err != nil { return nil, err @@ -119,7 +119,7 @@ func (u *Conn) LocalAddr() (*Addr, error) { return addr, nil } -func (u *Conn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) { +func (u *StdConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) { plaintext := make([]byte, MTU) h := &header.H{} fwPacket := &firewall.Packet{} @@ -150,7 +150,7 @@ func (u *Conn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall } } -func (u *Conn) ReadSingle(msgs []rawMessage) (int, error) { +func (u *StdConn) ReadSingle(msgs []rawMessage) (int, error) { for { n, _, err := unix.Syscall6( unix.SYS_RECVMSG, @@ -171,7 +171,7 @@ func (u *Conn) ReadSingle(msgs []rawMessage) (int, error) { } } -func (u *Conn) ReadMulti(msgs []rawMessage) (int, error) { +func (u *StdConn) ReadMulti(msgs []rawMessage) (int, error) { for { n, _, err := unix.Syscall6( unix.SYS_RECVMMSG, @@ -191,7 +191,7 @@ func (u *Conn) ReadMulti(msgs []rawMessage) (int, error) { } } -func (u *Conn) WriteTo(b []byte, addr *Addr) error { +func (u *StdConn) WriteTo(b []byte, addr *Addr) error { var rsa unix.RawSockaddrInet6 rsa.Family = unix.AF_INET6 @@ -221,7 +221,7 @@ func (u *Conn) WriteTo(b []byte, addr *Addr) error { } } -func (u *Conn) ReloadConfig(c *config.C) { +func (u *StdConn) ReloadConfig(c *config.C) { b := c.GetInt("listen.read_buffer", 0) if b > 0 { err := u.SetRecvBuffer(b) @@ -253,7 +253,7 @@ func (u *Conn) ReloadConfig(c *config.C) { } } -func (u *Conn) getMemInfo(meminfo *_SK_MEMINFO) error { +func (u *StdConn) getMemInfo(meminfo *_SK_MEMINFO) error { var vallen uint32 = 4 * _SK_MEMINFO_VARS _, _, err := unix.Syscall6(unix.SYS_GETSOCKOPT, uintptr(u.sysFd), uintptr(unix.SOL_SOCKET), uintptr(unix.SO_MEMINFO), uintptr(unsafe.Pointer(meminfo)), uintptr(unsafe.Pointer(&vallen)), 0) if err != 0 { @@ -262,11 +262,11 @@ func (u *Conn) getMemInfo(meminfo *_SK_MEMINFO) error { return nil } -func NewUDPStatsEmitter(udpConns []*Conn) func() { +func NewUDPStatsEmitter(udpConns []Conn) func() { // Check if our kernel supports SO_MEMINFO before registering the gauges var udpGauges [][_SK_MEMINFO_VARS]metrics.Gauge var meminfo _SK_MEMINFO - if err := udpConns[0].getMemInfo(&meminfo); err == nil { + if err := udpConns[0].(*StdConn).getMemInfo(&meminfo); err == nil { udpGauges = make([][_SK_MEMINFO_VARS]metrics.Gauge, len(udpConns)) for i := range udpConns { udpGauges[i] = [_SK_MEMINFO_VARS]metrics.Gauge{ @@ -285,7 +285,7 @@ func NewUDPStatsEmitter(udpConns []*Conn) func() { return func() { for i, gauges := range udpGauges { - if err := udpConns[i].getMemInfo(&meminfo); err == nil { + if err := udpConns[i].(*StdConn).getMemInfo(&meminfo); err == nil { for j := 0; j < _SK_MEMINFO_VARS; j++ { gauges[j].Update(int64(meminfo[j])) } diff --git a/udp/udp_linux_32.go b/udp/udp_linux_32.go index 06cd38224..523968c23 100644 --- a/udp/udp_linux_32.go +++ b/udp/udp_linux_32.go @@ -30,7 +30,7 @@ type rawMessage struct { Len uint32 } -func (u *Conn) PrepareRawMessages(n int) ([]rawMessage, [][]byte, [][]byte) { +func (u *StdConn) PrepareRawMessages(n int) ([]rawMessage, [][]byte, [][]byte) { msgs := make([]rawMessage, n) buffers := make([][]byte, n) names := make([][]byte, n) diff --git a/udp/udp_linux_64.go b/udp/udp_linux_64.go index c442405b6..a54f1dfd9 100644 --- a/udp/udp_linux_64.go +++ b/udp/udp_linux_64.go @@ -33,7 +33,7 @@ type rawMessage struct { Pad0 [4]byte } -func (u *Conn) PrepareRawMessages(n int) ([]rawMessage, [][]byte, [][]byte) { +func (u *StdConn) PrepareRawMessages(n int) ([]rawMessage, [][]byte, [][]byte) { msgs := make([]rawMessage, n) buffers := make([][]byte, n) names := make([][]byte, n) diff --git a/udp/udp_tester.go b/udp/udp_tester.go index 8b5e53162..4396c4832 100644 --- a/udp/udp_tester.go +++ b/udp/udp_tester.go @@ -36,7 +36,7 @@ func (u *Packet) Copy() *Packet { return n } -type Conn struct { +type TesterConn struct { Addr *Addr RxPackets chan *Packet // Packets to receive into nebula @@ -45,8 +45,8 @@ type Conn struct { l *logrus.Logger } -func NewListener(l *logrus.Logger, ip net.IP, port int, _ bool, _ int) (*Conn, error) { - return &Conn{ +func NewListener(l *logrus.Logger, ip net.IP, port int, _ bool, _ int) (Conn, error) { + return &TesterConn{ Addr: &Addr{ip, uint16(port)}, RxPackets: make(chan *Packet, 10), TxPackets: make(chan *Packet, 10), @@ -57,7 +57,7 @@ func NewListener(l *logrus.Logger, ip net.IP, port int, _ bool, _ int) (*Conn, e // Send will place a UdpPacket onto the receive queue for nebula to consume // this is an encrypted packet or a handshake message in most cases // packets were transmitted from another nebula node, you can send them with Tun.Send -func (u *Conn) Send(packet *Packet) { +func (u *TesterConn) Send(packet *Packet) { h := &header.H{} if err := h.Parse(packet.Data); err != nil { panic(err) @@ -74,7 +74,7 @@ func (u *Conn) Send(packet *Packet) { // Get will pull a UdpPacket from the transmit queue // nebula meant to send this message on the network, it will be encrypted // packets were ingested from the tun side (in most cases), you can send them with Tun.Send -func (u *Conn) Get(block bool) *Packet { +func (u *TesterConn) Get(block bool) *Packet { if block { return <-u.TxPackets } @@ -91,7 +91,7 @@ func (u *Conn) Get(block bool) *Packet { // Below this is boilerplate implementation to make nebula actually work //********************************************************************************************************************// -func (u *Conn) WriteTo(b []byte, addr *Addr) error { +func (u *TesterConn) WriteTo(b []byte, addr *Addr) error { p := &Packet{ Data: make([]byte, len(b), len(b)), FromIp: make([]byte, 16), @@ -108,7 +108,7 @@ func (u *Conn) WriteTo(b []byte, addr *Addr) error { return nil } -func (u *Conn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) { +func (u *TesterConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) { plaintext := make([]byte, MTU) h := &header.H{} fwPacket := &firewall.Packet{} @@ -126,17 +126,17 @@ func (u *Conn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall } } -func (u *Conn) ReloadConfig(*config.C) {} +func (u *TesterConn) ReloadConfig(*config.C) {} -func NewUDPStatsEmitter(_ []*Conn) func() { +func NewUDPStatsEmitter(_ []Conn) func() { // No UDP stats for non-linux return func() {} } -func (u *Conn) LocalAddr() (*Addr, error) { +func (u *TesterConn) LocalAddr() (*Addr, error) { return u.Addr, nil } -func (u *Conn) Rebind() error { +func (u *TesterConn) Rebind() error { return nil } diff --git a/udp/udp_windows.go b/udp/udp_windows.go index 1f2ce6475..1456edebb 100644 --- a/udp/udp_windows.go +++ b/udp/udp_windows.go @@ -24,6 +24,6 @@ func NewListenConfig(multi bool) net.ListenConfig { } } -func (u *Conn) Rebind() error { +func (u *GenericConn) Rebind() error { return nil } From 8ba5d64dbcb8319965441198444b85e1f714d51e Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 22 Jun 2023 12:13:31 -0400 Subject: [PATCH 08/52] Add support for naming FreeBSD tun devices (#903) --- Makefile | 10 ++- examples/config.yml | 1 - overlay/tun_darwin.go | 10 +-- overlay/tun_freebsd.go | 138 +++++++++++++++++++++++++++++++++++------ overlay/tun_linux.go | 8 --- overlay/tun_notwin.go | 14 +++++ 6 files changed, 141 insertions(+), 40 deletions(-) create mode 100644 overlay/tun_notwin.go diff --git a/Makefile b/Makefile index 68f5ca733..fecd8894b 100644 --- a/Makefile +++ b/Makefile @@ -44,10 +44,13 @@ ALL_LINUX = linux-amd64 \ linux-mips-softfloat \ linux-riscv64 +ALL_FREEBSD = freebsd-amd64 \ + freebsd-arm64 + ALL = $(ALL_LINUX) \ + $(ALL_FREEBSD) \ darwin-amd64 \ darwin-arm64 \ - freebsd-amd64 \ windows-amd64 \ windows-arm64 @@ -75,7 +78,7 @@ release: $(ALL:%=build/nebula-%.tar.gz) release-linux: $(ALL_LINUX:%=build/nebula-%.tar.gz) -release-freebsd: build/nebula-freebsd-amd64.tar.gz +release-freebsd: $(ALL_FREEBSD:%=build/nebula-%.tar.gz) release-boringcrypto: build/nebula-linux-$(shell go env GOARCH)-boringcrypto.tar.gz @@ -93,6 +96,9 @@ bin-darwin: build/darwin-amd64/nebula build/darwin-amd64/nebula-cert bin-freebsd: build/freebsd-amd64/nebula build/freebsd-amd64/nebula-cert mv $? . +bin-freebsd-arm64: build/freebsd-arm64/nebula build/freebsd-arm64/nebula-cert + mv $? . + bin-boringcrypto: build/linux-$(shell go env GOARCH)-boringcrypto/nebula build/linux-$(shell go env GOARCH)-boringcrypto/nebula-cert mv $? . diff --git a/examples/config.yml b/examples/config.yml index ad49a3c5a..a7acb737c 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -194,7 +194,6 @@ tun: disabled: false # Name of the device. If not set, a default will be chosen by the OS. # For macOS: if set, must be in the form `utun[0-9]+`. - # For FreeBSD: Required to be set, must be in the form `tun[0-9]+`. dev: nebula1 # Toggles forwarding of local broadcast packets, the address of which depends on the ip/mask encoded in pki.cert drop_local_broadcast: false diff --git a/overlay/tun_darwin.go b/overlay/tun_darwin.go index fd3429d0c..428e38f3c 100644 --- a/overlay/tun_darwin.go +++ b/overlay/tun_darwin.go @@ -47,14 +47,6 @@ type ifReq struct { pad [8]byte } -func ioctl(a1, a2, a3 uintptr) error { - _, _, errno := unix.Syscall(unix.SYS_IOCTL, a1, a2, a3) - if errno != 0 { - return errno - } - return nil -} - var sockaddrCtlSize uintptr = 32 const ( @@ -194,10 +186,10 @@ func (t *tun) Activate() error { unix.SOCK_DGRAM, unix.IPPROTO_IP, ) - if err != nil { return err } + defer unix.Close(s) fd := uintptr(s) diff --git a/overlay/tun_freebsd.go b/overlay/tun_freebsd.go index 99cbdb058..8a5295461 100644 --- a/overlay/tun_freebsd.go +++ b/overlay/tun_freebsd.go @@ -4,21 +4,44 @@ package overlay import ( + "bytes" + "errors" "fmt" "io" + "io/fs" "net" "os" "os/exec" - "regexp" "strconv" - "strings" + "syscall" + "unsafe" "github.com/sirupsen/logrus" "github.com/slackhq/nebula/cidr" "github.com/slackhq/nebula/iputil" ) -var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`) +const ( + // FIODGNAME is defined in sys/sys/filio.h on FreeBSD + // For 32-bit systems, use FIODGNAME_32 (not defined in this file: 0x80086678) + FIODGNAME = 0x80106678 +) + +type fiodgnameArg struct { + length int32 + pad [4]byte + buf unsafe.Pointer +} + +type ifreqRename struct { + Name [16]byte + Data uintptr +} + +type ifreqDestroy struct { + Name [16]byte + pad [16]byte +} type tun struct { Device string @@ -33,8 +56,23 @@ type tun struct { func (t *tun) Close() error { if t.ReadWriteCloser != nil { - return t.ReadWriteCloser.Close() + if err := t.ReadWriteCloser.Close(); err != nil { + return err + } + + s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP) + if err != nil { + return err + } + defer syscall.Close(s) + + ifreq := ifreqDestroy{Name: t.deviceBytes()} + + // Destroy the interface + err = ioctl(uintptr(s), syscall.SIOCIFDESTROY, uintptr(unsafe.Pointer(&ifreq))) + return err } + return nil } @@ -43,34 +81,87 @@ func newTunFromFd(_ *logrus.Logger, _ int, _ *net.IPNet, _ int, _ []Route, _ int } func newTun(l *logrus.Logger, deviceName string, cidr *net.IPNet, defaultMTU int, routes []Route, _ int, _ bool, _ bool) (*tun, error) { - routeTree, err := makeRouteTree(l, routes, false) + // Try to open existing tun device + var file *os.File + var err error + if deviceName != "" { + file, err = os.OpenFile("/dev/"+deviceName, os.O_RDWR, 0) + } + if errors.Is(err, fs.ErrNotExist) || deviceName == "" { + // If the device doesn't already exist, request a new one and rename it + file, err = os.OpenFile("/dev/tun", os.O_RDWR, 0) + } + if err != nil { + return nil, err + } + + rawConn, err := file.SyscallConn() if err != nil { + return nil, fmt.Errorf("SyscallConn: %v", err) + } + + var name [16]byte + var ctrlErr error + rawConn.Control(func(fd uintptr) { + // Read the name of the interface + arg := fiodgnameArg{length: 16, buf: unsafe.Pointer(&name)} + ctrlErr = ioctl(fd, FIODGNAME, uintptr(unsafe.Pointer(&arg))) + }) + if ctrlErr != nil { return nil, err } - if strings.HasPrefix(deviceName, "/dev/") { - deviceName = strings.TrimPrefix(deviceName, "/dev/") + ifName := string(bytes.TrimRight(name[:], "\x00")) + if deviceName == "" { + deviceName = ifName } - if !deviceNameRE.MatchString(deviceName) { - return nil, fmt.Errorf("tun.dev must match `tun[0-9]+`") + + // If the name doesn't match the desired interface name, rename it now + if ifName != deviceName { + s, err := syscall.Socket( + syscall.AF_INET, + syscall.SOCK_DGRAM, + syscall.IPPROTO_IP, + ) + if err != nil { + return nil, err + } + defer syscall.Close(s) + + fd := uintptr(s) + + var fromName [16]byte + var toName [16]byte + copy(fromName[:], ifName) + copy(toName[:], deviceName) + + ifrr := ifreqRename{ + Name: fromName, + Data: uintptr(unsafe.Pointer(&toName)), + } + + // Set the device name + ioctl(fd, syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&ifrr))) } + + routeTree, err := makeRouteTree(l, routes, false) + if err != nil { + return nil, err + } + return &tun{ - Device: deviceName, - cidr: cidr, - MTU: defaultMTU, - Routes: routes, - routeTree: routeTree, - l: l, + ReadWriteCloser: file, + Device: deviceName, + cidr: cidr, + MTU: defaultMTU, + Routes: routes, + routeTree: routeTree, + l: l, }, nil } func (t *tun) Activate() error { var err error - t.ReadWriteCloser, err = os.OpenFile("/dev/"+t.Device, os.O_RDWR, 0) - if err != nil { - return fmt.Errorf("activate failed: %v", err) - } - // TODO use syscalls instead of exec.Command t.l.Debug("command: ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String()) if err = exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String()).Run(); err != nil { @@ -120,3 +211,10 @@ func (t *tun) Name() string { func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) { return nil, fmt.Errorf("TODO: multiqueue not implemented for freebsd") } + +func (t *tun) deviceBytes() (o [16]byte) { + for i, c := range t.Device { + o[i] = byte(c) + } + return +} diff --git a/overlay/tun_linux.go b/overlay/tun_linux.go index 783318660..8751a3f6d 100644 --- a/overlay/tun_linux.go +++ b/overlay/tun_linux.go @@ -43,14 +43,6 @@ type ifReq struct { pad [8]byte } -func ioctl(a1, a2, a3 uintptr) error { - _, _, errno := unix.Syscall(unix.SYS_IOCTL, a1, a2, a3) - if errno != 0 { - return errno - } - return nil -} - type ifreqAddr struct { Name [16]byte Addr unix.RawSockaddrInet4 diff --git a/overlay/tun_notwin.go b/overlay/tun_notwin.go new file mode 100644 index 000000000..2fab9274b --- /dev/null +++ b/overlay/tun_notwin.go @@ -0,0 +1,14 @@ +//go:build !windows +// +build !windows + +package overlay + +import "syscall" + +func ioctl(a1, a2, a3 uintptr) error { + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, a1, a2, a3) + if errno != 0 { + return errno + } + return nil +} From a3e59a38eff2a4942fb42938a8243fe73aa62650 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Mon, 10 Jul 2023 12:43:48 -0500 Subject: [PATCH 09/52] Use registered io on Windows when possible (#905) --- Makefile | 4 +- go.mod | 1 + go.sum | 2 + interface.go | 7 + udp/conn.go | 4 + udp/udp_android.go | 5 + udp/udp_darwin.go | 5 + udp/udp_freebsd.go | 5 + udp/udp_generic.go | 8 +- udp/udp_linux.go | 9 +- udp/udp_rio_windows.go | 403 +++++++++++++++++++++++++++++++++++++++++ udp/udp_tester.go | 6 + udp/udp_windows.go | 21 ++- 13 files changed, 472 insertions(+), 8 deletions(-) create mode 100644 udp/udp_rio_windows.go diff --git a/Makefile b/Makefile index fecd8894b..7eaa07f36 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,8 @@ ifeq ($(OS),Windows_NT) GOISMIN := $(shell IF "$(GOVERSION)" GEQ "$(GOMINVERSION)" ECHO 1) NEBULA_CMD_SUFFIX = .exe NULL_FILE = nul + # RIO on windows does pointer stuff that makes go vet angry + VET_FLAGS = -unsafeptr=false else GOVERSION := $(shell go version | awk '{print substr($$3, 3)}') GOISMIN := $(shell expr "$(GOVERSION)" ">=" "$(GOMINVERSION)") @@ -143,7 +145,7 @@ build/nebula-%.zip: build/%/nebula.exe build/%/nebula-cert.exe cd build/$* && zip ../nebula-$*.zip nebula.exe nebula-cert.exe vet: - go vet -v ./... + go vet $(VET_FLAGS) -v ./... test: go test -v ./... diff --git a/go.mod b/go.mod index 52c2e92d1..9e92752d2 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,7 @@ require ( golang.org/x/sys v0.8.0 golang.org/x/term v0.8.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 google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 452a1d237..ce47641ec 100644 --- a/go.sum +++ b/go.sum @@ -219,6 +219,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= +golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b h1:J1CaxgLerRR5lgx3wnr6L04cJFbWoceSK9JWBdglINo= +golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/interface.go b/interface.go index 82ab0f04c..b5d43d295 100644 --- a/interface.go +++ b/interface.go @@ -413,6 +413,13 @@ func (f *Interface) emitStats(ctx context.Context, i time.Duration) { func (f *Interface) Close() error { f.closed.Store(true) + for _, u := range f.writers { + err := u.Close() + if err != nil { + f.l.WithError(err).Error("Error while closing udp socket") + } + } + // Release the tun device return f.inside.Close() } diff --git a/udp/conn.go b/udp/conn.go index 33520db37..a2c24a1f1 100644 --- a/udp/conn.go +++ b/udp/conn.go @@ -26,6 +26,7 @@ type Conn interface { ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) WriteTo(b []byte, addr *Addr) error ReloadConfig(c *config.C) + Close() error } type NoopConn struct{} @@ -45,3 +46,6 @@ func (NoopConn) WriteTo(_ []byte, _ *Addr) error { func (NoopConn) ReloadConfig(_ *config.C) { return } +func (NoopConn) Close() error { + return nil +} diff --git a/udp/udp_android.go b/udp/udp_android.go index 08cde96c9..8d6907488 100644 --- a/udp/udp_android.go +++ b/udp/udp_android.go @@ -8,9 +8,14 @@ import ( "net" "syscall" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) +func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) { + return NewGenericListener(l, ip, port, multi, batch) +} + func NewListenConfig(multi bool) net.ListenConfig { return net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { diff --git a/udp/udp_darwin.go b/udp/udp_darwin.go index 260ce4435..afbf240d8 100644 --- a/udp/udp_darwin.go +++ b/udp/udp_darwin.go @@ -10,9 +10,14 @@ import ( "net" "syscall" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) +func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) { + return NewGenericListener(l, ip, port, multi, batch) +} + func NewListenConfig(multi bool) net.ListenConfig { return net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { diff --git a/udp/udp_freebsd.go b/udp/udp_freebsd.go index 920f91b6d..3c14face3 100644 --- a/udp/udp_freebsd.go +++ b/udp/udp_freebsd.go @@ -10,9 +10,14 @@ import ( "net" "syscall" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) +func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) { + return NewGenericListener(l, ip, port, multi, batch) +} + func NewListenConfig(multi bool) net.ListenConfig { return net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { diff --git a/udp/udp_generic.go b/udp/udp_generic.go index 490cc61b8..1dd6d1de7 100644 --- a/udp/udp_generic.go +++ b/udp/udp_generic.go @@ -23,7 +23,9 @@ type GenericConn struct { l *logrus.Logger } -func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) { +var _ Conn = &GenericConn{} + +func NewGenericListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) { lc := NewListenConfig(multi) pc, err := lc.ListenPacket(context.TODO(), "udp", net.JoinHostPort(ip.String(), fmt.Sprintf("%v", port))) if err != nil { @@ -80,8 +82,8 @@ func (u *GenericConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *f // Just read one packet at a time n, rua, err := u.ReadFromUDP(buffer) if err != nil { - u.l.WithError(err).Error("Failed to read packets") - continue + u.l.WithError(err).Debug("udp socket is closed, exiting read loop") + return } udpAddr.IP = rua.IP diff --git a/udp/udp_linux.go b/udp/udp_linux.go index 60defaa11..ca050bb40 100644 --- a/udp/udp_linux.go +++ b/udp/udp_linux.go @@ -137,8 +137,8 @@ func (u *StdConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firew for { n, err := read(msgs) if err != nil { - u.l.WithError(err).Error("Failed to read packets") - continue + u.l.WithError(err).Debug("udp socket is closed, exiting read loop") + return } //metric.Update(int64(n)) @@ -262,6 +262,11 @@ func (u *StdConn) getMemInfo(meminfo *_SK_MEMINFO) error { return nil } +func (u *StdConn) Close() error { + //TODO: this will not interrupt the read loop + return syscall.Close(u.sysFd) +} + func NewUDPStatsEmitter(udpConns []Conn) func() { // Check if our kernel supports SO_MEMINFO before registering the gauges var udpGauges [][_SK_MEMINFO_VARS]metrics.Gauge diff --git a/udp/udp_rio_windows.go b/udp/udp_rio_windows.go new file mode 100644 index 000000000..31c1a554c --- /dev/null +++ b/udp/udp_rio_windows.go @@ -0,0 +1,403 @@ +//go:build !e2e_testing +// +build !e2e_testing + +// Inspired by https://git.zx2c4.com/wireguard-go/tree/conn/bind_windows.go + +package udp + +import ( + "errors" + "fmt" + "io" + "net" + "sync" + "sync/atomic" + "syscall" + "unsafe" + + "github.com/sirupsen/logrus" + "github.com/slackhq/nebula/config" + "github.com/slackhq/nebula/firewall" + "github.com/slackhq/nebula/header" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/wireguard/conn/winrio" +) + +// Assert we meet the standard conn interface +var _ Conn = &RIOConn{} + +//go:linkname procyield runtime.procyield +func procyield(cycles uint32) + +const ( + packetsPerRing = 1024 + bytesPerPacket = 2048 - 32 + receiveSpins = 15 +) + +type ringPacket struct { + addr windows.RawSockaddrInet6 + data [bytesPerPacket]byte +} + +type ringBuffer struct { + packets uintptr + head, tail uint32 + id winrio.BufferId + iocp windows.Handle + isFull bool + cq winrio.Cq + mu sync.Mutex + overlapped windows.Overlapped +} + +type RIOConn struct { + isOpen atomic.Bool + l *logrus.Logger + sock windows.Handle + rx, tx ringBuffer + rq winrio.Rq + results [packetsPerRing]winrio.Result +} + +func NewRIOListener(l *logrus.Logger, ip net.IP, port int) (*RIOConn, error) { + if !winrio.Initialize() { + return nil, errors.New("could not initialize winrio") + } + + u := &RIOConn{l: l} + + addr := [16]byte{} + copy(addr[:], ip.To16()) + err := u.bind(&windows.SockaddrInet6{Addr: addr, Port: port}) + if err != nil { + return nil, fmt.Errorf("bind: %w", err) + } + + for i := 0; i < packetsPerRing; i++ { + err = u.insertReceiveRequest() + if err != nil { + return nil, fmt.Errorf("init rx ring: %w", err) + } + } + + u.isOpen.Store(true) + return u, nil +} + +func (u *RIOConn) bind(sa windows.Sockaddr) error { + var err error + u.sock, err = winrio.Socket(windows.AF_INET6, windows.SOCK_DGRAM, windows.IPPROTO_UDP) + if err != nil { + return err + } + + // Enable v4 for this socket + syscall.SetsockoptInt(syscall.Handle(u.sock), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) + + err = u.rx.Open() + if err != nil { + return err + } + + err = u.tx.Open() + if err != nil { + return err + } + + u.rq, err = winrio.CreateRequestQueue(u.sock, packetsPerRing, 1, packetsPerRing, 1, u.rx.cq, u.tx.cq, 0) + if err != nil { + return err + } + + err = windows.Bind(u.sock, sa) + if err != nil { + return err + } + + return nil +} + +func (u *RIOConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) { + plaintext := make([]byte, MTU) + buffer := make([]byte, MTU) + h := &header.H{} + fwPacket := &firewall.Packet{} + udpAddr := &Addr{IP: make([]byte, 16)} + nb := make([]byte, 12, 12) + + for { + // Just read one packet at a time + n, rua, err := u.receive(buffer) + if err != nil { + u.l.WithError(err).Debug("udp socket is closed, exiting read loop") + return + } + + udpAddr.IP = rua.Addr[:] + p := (*[2]byte)(unsafe.Pointer(&udpAddr.Port)) + p[0] = byte(rua.Port >> 8) + p[1] = byte(rua.Port) + r(udpAddr, plaintext[:0], buffer[:n], h, fwPacket, lhf, nb, q, cache.Get(u.l)) + } +} + +func (u *RIOConn) insertReceiveRequest() error { + packet := u.rx.Push() + dataBuffer := &winrio.Buffer{ + Id: u.rx.id, + Offset: uint32(uintptr(unsafe.Pointer(&packet.data[0])) - u.rx.packets), + Length: uint32(len(packet.data)), + } + addressBuffer := &winrio.Buffer{ + Id: u.rx.id, + Offset: uint32(uintptr(unsafe.Pointer(&packet.addr)) - u.rx.packets), + Length: uint32(unsafe.Sizeof(packet.addr)), + } + + return winrio.ReceiveEx(u.rq, dataBuffer, 1, nil, addressBuffer, nil, nil, 0, uintptr(unsafe.Pointer(packet))) +} + +func (u *RIOConn) receive(buf []byte) (int, windows.RawSockaddrInet6, error) { + if !u.isOpen.Load() { + return 0, windows.RawSockaddrInet6{}, net.ErrClosed + } + + u.rx.mu.Lock() + defer u.rx.mu.Unlock() + + var err error + var count uint32 + var results [1]winrio.Result + +retry: + count = 0 + for tries := 0; count == 0 && tries < receiveSpins; tries++ { + if tries > 0 { + if !u.isOpen.Load() { + return 0, windows.RawSockaddrInet6{}, net.ErrClosed + } + procyield(1) + } + + count = winrio.DequeueCompletion(u.rx.cq, results[:]) + } + + if count == 0 { + err = winrio.Notify(u.rx.cq) + if err != nil { + return 0, windows.RawSockaddrInet6{}, err + } + var bytes uint32 + var key uintptr + var overlapped *windows.Overlapped + err = windows.GetQueuedCompletionStatus(u.rx.iocp, &bytes, &key, &overlapped, windows.INFINITE) + if err != nil { + return 0, windows.RawSockaddrInet6{}, err + } + + if !u.isOpen.Load() { + return 0, windows.RawSockaddrInet6{}, net.ErrClosed + } + + count = winrio.DequeueCompletion(u.rx.cq, results[:]) + if count == 0 { + return 0, windows.RawSockaddrInet6{}, io.ErrNoProgress + + } + } + + u.rx.Return(1) + err = u.insertReceiveRequest() + if err != nil { + return 0, windows.RawSockaddrInet6{}, err + } + + // We limit the MTU well below the 65k max for practicality, but this means a remote host can still send us + // huge packets. Just try again when this happens. The infinite loop this could cause is still limited to + // attacker bandwidth, just like the rest of the receive path. + if windows.Errno(results[0].Status) == windows.WSAEMSGSIZE { + goto retry + } + + if results[0].Status != 0 { + return 0, windows.RawSockaddrInet6{}, windows.Errno(results[0].Status) + } + + packet := (*ringPacket)(unsafe.Pointer(uintptr(results[0].RequestContext))) + ep := packet.addr + n := copy(buf, packet.data[:results[0].BytesTransferred]) + return n, ep, nil +} + +func (u *RIOConn) WriteTo(buf []byte, addr *Addr) error { + if !u.isOpen.Load() { + return net.ErrClosed + } + + if len(buf) > bytesPerPacket { + return io.ErrShortBuffer + } + + u.tx.mu.Lock() + defer u.tx.mu.Unlock() + + count := winrio.DequeueCompletion(u.tx.cq, u.results[:]) + if count == 0 && u.tx.isFull { + err := winrio.Notify(u.tx.cq) + if err != nil { + return err + } + + var bytes uint32 + var key uintptr + var overlapped *windows.Overlapped + err = windows.GetQueuedCompletionStatus(u.tx.iocp, &bytes, &key, &overlapped, windows.INFINITE) + if err != nil { + return err + } + + if !u.isOpen.Load() { + return net.ErrClosed + } + + count = winrio.DequeueCompletion(u.tx.cq, u.results[:]) + if count == 0 { + return io.ErrNoProgress + } + } + + if count > 0 { + u.tx.Return(count) + } + + packet := u.tx.Push() + packet.addr.Family = windows.AF_INET6 + p := (*[2]byte)(unsafe.Pointer(&packet.addr.Port)) + p[0] = byte(addr.Port >> 8) + p[1] = byte(addr.Port) + copy(packet.addr.Addr[:], addr.IP.To16()) + copy(packet.data[:], buf) + + dataBuffer := &winrio.Buffer{ + Id: u.tx.id, + Offset: uint32(uintptr(unsafe.Pointer(&packet.data[0])) - u.tx.packets), + Length: uint32(len(buf)), + } + + addressBuffer := &winrio.Buffer{ + Id: u.tx.id, + Offset: uint32(uintptr(unsafe.Pointer(&packet.addr)) - u.tx.packets), + Length: uint32(unsafe.Sizeof(packet.addr)), + } + + return winrio.SendEx(u.rq, dataBuffer, 1, nil, addressBuffer, nil, nil, 0, 0) +} + +func (u *RIOConn) LocalAddr() (*Addr, error) { + sa, err := windows.Getsockname(u.sock) + if err != nil { + return nil, err + } + + v6 := sa.(*windows.SockaddrInet6) + return &Addr{ + IP: v6.Addr[:], + Port: uint16(v6.Port), + }, nil +} + +func (u *RIOConn) Rebind() error { + return nil +} + +func (u *RIOConn) ReloadConfig(*config.C) {} + +func (u *RIOConn) Close() error { + if !u.isOpen.CompareAndSwap(true, false) { + return nil + } + + windows.PostQueuedCompletionStatus(u.rx.iocp, 0, 0, nil) + windows.PostQueuedCompletionStatus(u.tx.iocp, 0, 0, nil) + + u.rx.CloseAndZero() + u.tx.CloseAndZero() + if u.sock != 0 { + windows.CloseHandle(u.sock) + } + return nil +} + +func (ring *ringBuffer) Push() *ringPacket { + for ring.isFull { + panic("ring is full") + } + ret := (*ringPacket)(unsafe.Pointer(ring.packets + (uintptr(ring.tail%packetsPerRing) * unsafe.Sizeof(ringPacket{})))) + ring.tail += 1 + if ring.tail%packetsPerRing == ring.head%packetsPerRing { + ring.isFull = true + } + return ret +} + +func (ring *ringBuffer) Return(count uint32) { + if ring.head%packetsPerRing == ring.tail%packetsPerRing && !ring.isFull { + return + } + ring.head += count + ring.isFull = false +} + +func (ring *ringBuffer) CloseAndZero() { + if ring.cq != 0 { + winrio.CloseCompletionQueue(ring.cq) + ring.cq = 0 + } + + if ring.iocp != 0 { + windows.CloseHandle(ring.iocp) + ring.iocp = 0 + } + + if ring.id != 0 { + winrio.DeregisterBuffer(ring.id) + ring.id = 0 + } + + if ring.packets != 0 { + windows.VirtualFree(ring.packets, 0, windows.MEM_RELEASE) + ring.packets = 0 + } + + ring.head = 0 + ring.tail = 0 + ring.isFull = false +} + +func (ring *ringBuffer) Open() error { + var err error + packetsLen := unsafe.Sizeof(ringPacket{}) * packetsPerRing + ring.packets, err = windows.VirtualAlloc(0, packetsLen, windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE) + if err != nil { + return err + } + + ring.id, err = winrio.RegisterPointer(unsafe.Pointer(ring.packets), uint32(packetsLen)) + if err != nil { + return err + } + + ring.iocp, err = windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0) + if err != nil { + return err + } + + ring.cq, err = winrio.CreateIOCPCompletionQueue(packetsPerRing, ring.iocp, 0, &ring.overlapped) + if err != nil { + return err + } + + return nil +} diff --git a/udp/udp_tester.go b/udp/udp_tester.go index 4396c4832..f03a69cbe 100644 --- a/udp/udp_tester.go +++ b/udp/udp_tester.go @@ -140,3 +140,9 @@ func (u *TesterConn) LocalAddr() (*Addr, error) { func (u *TesterConn) Rebind() error { return nil } + +func (u *TesterConn) Close() error { + close(u.RxPackets) + close(u.TxPackets) + return nil +} diff --git a/udp/udp_windows.go b/udp/udp_windows.go index 1456edebb..ebcace670 100644 --- a/udp/udp_windows.go +++ b/udp/udp_windows.go @@ -3,14 +3,31 @@ package udp -// Windows support is primarily implemented in udp_generic, besides NewListenConfig - import ( "fmt" "net" "syscall" + + "github.com/sirupsen/logrus" ) +func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) { + if multi { + //NOTE: Technically we can support it with RIO but it wouldn't be at the socket level + // The udp stack would need to be reworked to hide away the implementation differences between + // Windows and Linux + return nil, fmt.Errorf("multiple udp listeners not supported on windows") + } + + rc, err := NewRIOListener(l, ip, port) + if err == nil { + return rc, nil + } + + l.WithError(err).Error("Falling back to standard udp sockets") + return NewGenericListener(l, ip, port, multi, batch) +} + func NewListenConfig(multi bool) net.ListenConfig { return net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { From 7e380bde7e02c26b0ba96305a8bc0671ed7102b1 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Mon, 10 Jul 2023 15:19:05 -0400 Subject: [PATCH 10/52] Document new DNS config options (#879) --- examples/config.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/config.yml b/examples/config.yml index a7acb737c..23ff50561 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -21,6 +21,19 @@ pki: static_host_map: "192.168.100.1": ["100.64.22.11:4242"] +# The static_map config stanza can be used to configure how the static_host_map behaves. +#static_map: + # cadence determines how frequently DNS is re-queried for updated IP addresses when a static_host_map entry contains + # a DNS name. + #cadence: 30s + + # network determines the type of IP addresses to ask the DNS server for. The default is "ip4" because nodes typically + # do not know their public IPv4 address. Connecting to the Lighthouse via IPv4 allows the Lighthouse to detect the + # public address. Other valid options are "ip6" and "ip" (returns both.) + #network: ip4 + + # lookup_timeout is the DNS query timeout. + #lookup_timeout: 250ms lighthouse: # am_lighthouse is used to enable lighthouse functionality for a node. This should ONLY be true on nodes From c5ce945852853ad450a0dc38cc744b54bfac96e4 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Thu, 20 Jul 2023 21:30:38 -0500 Subject: [PATCH 11/52] Update README to include a link to go install docs (#919) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 925aa614c..6a7e5f21b 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ For each host, copy the nebula binary to the host, along with `config.yml` from ## Building Nebula from source -Download go and clone this repo. Change to the nebula directory. +Make sure you have [go](https://go.dev/doc/install) installed and clone this repo. Change to the nebula directory. To build nebula for all platforms: `make all` From f5db03c834641dd1fb3fbd99682e1cf89a99925d Mon Sep 17 00:00:00 2001 From: Wade Simmons Date: Fri, 21 Jul 2023 17:21:58 -0400 Subject: [PATCH 12/52] add dependabot config (#922) This should give us PRs weekly with dependency updates, and also let us manually check for updates when needed. - https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..603f65329 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" From 1e3c15589659b63c280a1c1534b0afed863bd4b9 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Mon, 24 Jul 2023 11:30:18 -0500 Subject: [PATCH 13/52] Attempt to notify systemd of service readiness on linux (#929) --- cmd/nebula/main.go | 1 + cmd/nebula/notify_linux.go | 42 +++++++++++++++++++++++++ cmd/nebula/notify_notlinux.go | 10 ++++++ dist/arch/nebula.service | 2 ++ dist/fedora/nebula.service | 2 ++ examples/service_scripts/nebula.service | 2 ++ 6 files changed, 59 insertions(+) create mode 100644 cmd/nebula/notify_linux.go create mode 100644 cmd/nebula/notify_notlinux.go diff --git a/cmd/nebula/main.go b/cmd/nebula/main.go index e9b285e7a..9461035b1 100644 --- a/cmd/nebula/main.go +++ b/cmd/nebula/main.go @@ -65,6 +65,7 @@ func main() { if !*configTest { ctrl.Start() + notifyReady(l) ctrl.ShutdownBlock() } diff --git a/cmd/nebula/notify_linux.go b/cmd/nebula/notify_linux.go new file mode 100644 index 000000000..8c3dca558 --- /dev/null +++ b/cmd/nebula/notify_linux.go @@ -0,0 +1,42 @@ +package main + +import ( + "net" + "os" + "time" + + "github.com/sirupsen/logrus" +) + +// SdNotifyReady tells systemd the service is ready and dependent services can now be started +// https://www.freedesktop.org/software/systemd/man/sd_notify.html +// https://www.freedesktop.org/software/systemd/man/systemd.service.html +const SdNotifyReady = "READY=1" + +func notifyReady(l *logrus.Logger) { + sockName := os.Getenv("NOTIFY_SOCKET") + if sockName == "" { + l.Debugln("NOTIFY_SOCKET systemd env var not set, not sending ready signal") + return + } + + conn, err := net.DialTimeout("unixgram", sockName, time.Second) + if err != nil { + l.WithError(err).Error("failed to connect to systemd notification socket") + return + } + defer conn.Close() + + err = conn.SetWriteDeadline(time.Now().Add(time.Second)) + if err != nil { + l.WithError(err).Error("failed to set the write deadline for the systemd notification socket") + return + } + + if _, err = conn.Write([]byte(SdNotifyReady)); err != nil { + l.WithError(err).Error("failed to signal the systemd notification socket") + return + } + + l.Debugln("notified systemd the service is ready") +} diff --git a/cmd/nebula/notify_notlinux.go b/cmd/nebula/notify_notlinux.go new file mode 100644 index 000000000..e7758e094 --- /dev/null +++ b/cmd/nebula/notify_notlinux.go @@ -0,0 +1,10 @@ +//go:build !linux +// +build !linux + +package main + +import "github.com/sirupsen/logrus" + +func notifyReady(_ *logrus.Logger) { + // No init service to notify +} diff --git a/dist/arch/nebula.service b/dist/arch/nebula.service index 7e5335aa8..831c71a53 100644 --- a/dist/arch/nebula.service +++ b/dist/arch/nebula.service @@ -4,6 +4,8 @@ Wants=basic.target network-online.target nss-lookup.target time-sync.target After=basic.target network.target network-online.target [Service] +Type=notify +NotifyAccess=main SyslogIdentifier=nebula ExecReload=/bin/kill -HUP $MAINPID ExecStart=/usr/bin/nebula -config /etc/nebula/config.yml diff --git a/dist/fedora/nebula.service b/dist/fedora/nebula.service index 21a99c558..0f947ead4 100644 --- a/dist/fedora/nebula.service +++ b/dist/fedora/nebula.service @@ -5,6 +5,8 @@ After=basic.target network.target network-online.target Before=sshd.service [Service] +Type=notify +NotifyAccess=main SyslogIdentifier=nebula ExecReload=/bin/kill -HUP $MAINPID ExecStart=/usr/bin/nebula -config /etc/nebula/config.yml diff --git a/examples/service_scripts/nebula.service b/examples/service_scripts/nebula.service index fd7a06710..ab5218f8d 100644 --- a/examples/service_scripts/nebula.service +++ b/examples/service_scripts/nebula.service @@ -5,6 +5,8 @@ After=basic.target network.target network-online.target Before=sshd.service [Service] +Type=notify +NotifyAccess=main SyslogIdentifier=nebula ExecReload=/bin/kill -HUP $MAINPID ExecStart=/usr/local/bin/nebula -config /etc/nebula/config.yml From 8caaff71095616584f6eafdf30db879846a244a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:51:31 -0400 Subject: [PATCH 14/52] Bump github.com/stretchr/testify from 1.8.2 to 1.8.4 (#924) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.2 to 1.8.4. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.2...v1.8.4) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 9e92752d2..27c8392f4 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.8.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 diff --git a/go.sum b/go.sum index ce47641ec..159fc9948 100644 --- a/go.sum +++ b/go.sum @@ -130,16 +130,12 @@ github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= From 52c9e360e78935bc34873a010c5ad428cddb23ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:52:29 -0400 Subject: [PATCH 15/52] Bump github.com/miekg/dns from 1.1.54 to 1.1.55 (#925) Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.54 to 1.1.55. - [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release) - [Commits](https://github.com/miekg/dns/compare/v1.1.54...v1.1.55) --- updated-dependencies: - dependency-name: github.com/miekg/dns dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 27c8392f4..bf8e2829d 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/google/gopacket v1.1.19 github.com/imdario/mergo v0.3.15 github.com/kardianos/service v1.2.2 - github.com/miekg/dns v1.1.54 + github.com/miekg/dns v1.1.55 github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f github.com/prometheus/client_golang v1.15.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 diff --git a/go.sum b/go.sum index 159fc9948..7485b8893 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.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= -github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= +github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= 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= From a10baeee9258d98da898b3aef96125edf826bd4f Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Mon, 24 Jul 2023 12:37:52 -0500 Subject: [PATCH 16/52] Pull hostmap and pending hostmap apart, remove unused functions (#843) --- connection_manager_test.go | 20 ++-- control.go | 63 +++++++------ control_test.go | 10 +- control_tester.go | 6 +- dns_server.go | 4 +- handshake.go | 2 +- handshake_ix.go | 2 +- handshake_manager.go | 151 +++++++++++++++++++++++------- handshake_manager_test.go | 8 +- hostmap.go | 187 +++++++++++-------------------------- hostmap_test.go | 28 +++--- inside.go | 11 +-- main.go | 4 +- outside.go | 17 ++-- relay_manager.go | 11 ++- ssh.go | 61 ++++++------ 16 files changed, 291 insertions(+), 294 deletions(-) diff --git a/connection_manager_test.go b/connection_manager_test.go index bfe57c8f0..642e0554c 100644 --- a/connection_manager_test.go +++ b/connection_manager_test.go @@ -42,7 +42,7 @@ func Test_NewConnectionManagerTest(t *testing.T) { preferredRanges := []*net.IPNet{localrange} // Very incomplete mock objects - hostMap := NewHostMap(l, "test", vpncidr, preferredRanges) + hostMap := NewHostMap(l, vpncidr, preferredRanges) cs := &CertState{ rawCertificate: []byte{}, privateKey: []byte{}, @@ -121,7 +121,7 @@ func Test_NewConnectionManagerTest2(t *testing.T) { preferredRanges := []*net.IPNet{localrange} // Very incomplete mock objects - hostMap := NewHostMap(l, "test", vpncidr, preferredRanges) + hostMap := NewHostMap(l, vpncidr, preferredRanges) cs := &CertState{ rawCertificate: []byte{}, privateKey: []byte{}, @@ -207,7 +207,7 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { _, vpncidr, _ := net.ParseCIDR("172.1.1.1/24") _, localrange, _ := net.ParseCIDR("10.1.1.1/24") preferredRanges := []*net.IPNet{localrange} - hostMap := NewHostMap(l, "test", vpncidr, preferredRanges) + hostMap := NewHostMap(l, vpncidr, preferredRanges) // Generate keys for CA and peer's cert. pubCA, privCA, _ := ed25519.GenerateKey(rand.Reader) @@ -268,12 +268,16 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { punchy := NewPunchyFromConfig(l, config.NewC(l)) nc := newConnectionManager(ctx, l, ifce, 5, 10, punchy) ifce.connectionManager = nc - hostinfo, _ := nc.hostMap.AddVpnIp(vpnIp, nil) - hostinfo.ConnectionState = &ConnectionState{ - certState: cs, - peerCert: &peerCert, - H: &noise.HandshakeState{}, + + hostinfo := &HostInfo{ + vpnIp: vpnIp, + ConnectionState: &ConnectionState{ + certState: cs, + peerCert: &peerCert, + H: &noise.HandshakeState{}, + }, } + nc.hostMap.unlockedAddHostInfo(hostinfo, ifce) // Move ahead 45s. // Check if to disconnect with invalid certificate. diff --git a/control.go b/control.go index 203278dff..07b42f2ea 100644 --- a/control.go +++ b/control.go @@ -17,6 +17,15 @@ import ( // Every interaction here needs to take extra care to copy memory and not return or use arguments "as is" when touching // core. This means copying IP objects, slices, de-referencing pointers and taking the actual value, etc +type controlEach func(h *HostInfo) + +type controlHostLister interface { + QueryVpnIp(vpnIp iputil.VpnIp) *HostInfo + ForEachIndex(each controlEach) + ForEachVpnIp(each controlEach) + GetPreferredRanges() []*net.IPNet +} + type Control struct { f *Interface l *logrus.Logger @@ -98,7 +107,7 @@ func (c *Control) RebindUDPServer() { // ListHostmapHosts returns details about the actual or pending (handshaking) hostmap by vpn ip func (c *Control) ListHostmapHosts(pendingMap bool) []ControlHostInfo { if pendingMap { - return listHostMapHosts(c.f.handshakeManager.pendingHostMap) + return listHostMapHosts(c.f.handshakeManager) } else { return listHostMapHosts(c.f.hostMap) } @@ -107,7 +116,7 @@ func (c *Control) ListHostmapHosts(pendingMap bool) []ControlHostInfo { // ListHostmapIndexes returns details about the actual or pending (handshaking) hostmap by local index id func (c *Control) ListHostmapIndexes(pendingMap bool) []ControlHostInfo { if pendingMap { - return listHostMapIndexes(c.f.handshakeManager.pendingHostMap) + return listHostMapIndexes(c.f.handshakeManager) } else { return listHostMapIndexes(c.f.hostMap) } @@ -115,15 +124,15 @@ func (c *Control) ListHostmapIndexes(pendingMap bool) []ControlHostInfo { // GetHostInfoByVpnIp returns a single tunnels hostInfo, or nil if not found func (c *Control) GetHostInfoByVpnIp(vpnIp iputil.VpnIp, pending bool) *ControlHostInfo { - var hm *HostMap + var hl controlHostLister if pending { - hm = c.f.handshakeManager.pendingHostMap + hl = c.f.handshakeManager } else { - hm = c.f.hostMap + hl = c.f.hostMap } - h, err := hm.QueryVpnIp(vpnIp) - if err != nil { + h := hl.QueryVpnIp(vpnIp) + if h == nil { return nil } @@ -133,8 +142,8 @@ func (c *Control) GetHostInfoByVpnIp(vpnIp iputil.VpnIp, pending bool) *ControlH // SetRemoteForTunnel forces a tunnel to use a specific remote func (c *Control) SetRemoteForTunnel(vpnIp iputil.VpnIp, addr udp.Addr) *ControlHostInfo { - hostInfo, err := c.f.hostMap.QueryVpnIp(vpnIp) - if err != nil { + hostInfo := c.f.hostMap.QueryVpnIp(vpnIp) + if hostInfo == nil { return nil } @@ -145,8 +154,8 @@ func (c *Control) SetRemoteForTunnel(vpnIp iputil.VpnIp, addr udp.Addr) *Control // CloseTunnel closes a fully established tunnel. If localOnly is false it will notify the remote end as well. func (c *Control) CloseTunnel(vpnIp iputil.VpnIp, localOnly bool) bool { - hostInfo, err := c.f.hostMap.QueryVpnIp(vpnIp) - if err != nil { + hostInfo := c.f.hostMap.QueryVpnIp(vpnIp) + if hostInfo == nil { return false } @@ -241,28 +250,20 @@ func copyHostInfo(h *HostInfo, preferredRanges []*net.IPNet) ControlHostInfo { return chi } -func listHostMapHosts(hm *HostMap) []ControlHostInfo { - hm.RLock() - hosts := make([]ControlHostInfo, len(hm.Hosts)) - i := 0 - for _, v := range hm.Hosts { - hosts[i] = copyHostInfo(v, hm.preferredRanges) - i++ - } - hm.RUnlock() - +func listHostMapHosts(hl controlHostLister) []ControlHostInfo { + hosts := make([]ControlHostInfo, 0) + pr := hl.GetPreferredRanges() + hl.ForEachVpnIp(func(hostinfo *HostInfo) { + hosts = append(hosts, copyHostInfo(hostinfo, pr)) + }) return hosts } -func listHostMapIndexes(hm *HostMap) []ControlHostInfo { - hm.RLock() - hosts := make([]ControlHostInfo, len(hm.Indexes)) - i := 0 - for _, v := range hm.Indexes { - hosts[i] = copyHostInfo(v, hm.preferredRanges) - i++ - } - hm.RUnlock() - +func listHostMapIndexes(hl controlHostLister) []ControlHostInfo { + hosts := make([]ControlHostInfo, 0) + pr := hl.GetPreferredRanges() + hl.ForEachIndex(func(hostinfo *HostInfo) { + hosts = append(hosts, copyHostInfo(hostinfo, pr)) + }) return hosts } diff --git a/control_test.go b/control_test.go index de46991f0..56a2b2f72 100644 --- a/control_test.go +++ b/control_test.go @@ -18,7 +18,7 @@ func TestControl_GetHostInfoByVpnIp(t *testing.T) { l := test.NewLogger() // Special care must be taken to re-use all objects provided to the hostmap and certificate in the expectedInfo object // To properly ensure we are not exposing core memory to the caller - hm := NewHostMap(l, "test", &net.IPNet{}, make([]*net.IPNet, 0)) + hm := NewHostMap(l, &net.IPNet{}, make([]*net.IPNet, 0)) remote1 := udp.NewAddr(net.ParseIP("0.0.0.100"), 4444) remote2 := udp.NewAddr(net.ParseIP("1:2:3:4:5:6:7:8"), 4444) ipNet := net.IPNet{ @@ -50,7 +50,7 @@ func TestControl_GetHostInfoByVpnIp(t *testing.T) { remotes := NewRemoteList(nil) remotes.unlockedPrependV4(0, NewIp4AndPort(remote1.IP, uint32(remote1.Port))) remotes.unlockedPrependV6(0, NewIp6AndPort(remote2.IP, uint32(remote2.Port))) - hm.Add(iputil.Ip2VpnIp(ipNet.IP), &HostInfo{ + hm.unlockedAddHostInfo(&HostInfo{ remote: remote1, remotes: remotes, ConnectionState: &ConnectionState{ @@ -64,9 +64,9 @@ func TestControl_GetHostInfoByVpnIp(t *testing.T) { relayForByIp: map[iputil.VpnIp]*Relay{}, relayForByIdx: map[uint32]*Relay{}, }, - }) + }, &Interface{}) - hm.Add(iputil.Ip2VpnIp(ipNet2.IP), &HostInfo{ + hm.unlockedAddHostInfo(&HostInfo{ remote: remote1, remotes: remotes, ConnectionState: &ConnectionState{ @@ -80,7 +80,7 @@ func TestControl_GetHostInfoByVpnIp(t *testing.T) { relayForByIp: map[iputil.VpnIp]*Relay{}, relayForByIdx: map[uint32]*Relay{}, }, - }) + }, &Interface{}) c := Control{ f: &Interface{ diff --git a/control_tester.go b/control_tester.go index 340ba1c5e..dd1a77418 100644 --- a/control_tester.go +++ b/control_tester.go @@ -147,12 +147,12 @@ func (c *Control) GetUDPAddr() string { } func (c *Control) KillPendingTunnel(vpnIp net.IP) bool { - hostinfo, ok := c.f.handshakeManager.pendingHostMap.Hosts[iputil.Ip2VpnIp(vpnIp)] - if !ok { + hostinfo := c.f.handshakeManager.QueryVpnIp(iputil.Ip2VpnIp(vpnIp)) + if hostinfo == nil { return false } - c.f.handshakeManager.pendingHostMap.DeleteHostInfo(hostinfo) + c.f.handshakeManager.DeleteHostInfo(hostinfo) return true } diff --git a/dns_server.go b/dns_server.go index 19bc5ced7..3109b4cf7 100644 --- a/dns_server.go +++ b/dns_server.go @@ -47,8 +47,8 @@ func (d *dnsRecords) QueryCert(data string) string { return "" } iip := iputil.Ip2VpnIp(ip) - hostinfo, err := d.hostMap.QueryVpnIp(iip) - if err != nil { + hostinfo := d.hostMap.QueryVpnIp(iip) + if hostinfo == nil { return "" } q := hostinfo.GetCert() diff --git a/handshake.go b/handshake.go index 1f2f03a62..8cfba214b 100644 --- a/handshake.go +++ b/handshake.go @@ -20,7 +20,7 @@ func HandleIncomingHandshake(f *Interface, addr *udp.Addr, via *ViaSender, packe case 1: ixHandshakeStage1(f, addr, via, packet, h) case 2: - newHostinfo, _ := f.handshakeManager.QueryIndex(h.RemoteIndex) + newHostinfo := f.handshakeManager.QueryIndex(h.RemoteIndex) tearDown := ixHandshakeStage2(f, addr, via, newHostinfo, packet, h) if tearDown && newHostinfo != nil { f.handshakeManager.DeleteHostInfo(newHostinfo) diff --git a/handshake_ix.go b/handshake_ix.go index b6b5658fd..70263b96a 100644 --- a/handshake_ix.go +++ b/handshake_ix.go @@ -422,7 +422,7 @@ func ixHandshakeStage2(f *Interface, addr *udp.Addr, via *ViaSender, hostinfo *H Info("Incorrect host responded to handshake") // Release our old handshake from pending, it should not continue - f.handshakeManager.pendingHostMap.DeleteHostInfo(hostinfo) + f.handshakeManager.DeleteHostInfo(hostinfo) // Create a new hostinfo/handshake for the intended vpn ip //TODO: this adds it to the timer wheel in a way that aggressively retries diff --git a/handshake_manager.go b/handshake_manager.go index 02b27bbda..a70f4dbc3 100644 --- a/handshake_manager.go +++ b/handshake_manager.go @@ -7,6 +7,7 @@ import ( "encoding/binary" "errors" "net" + "sync" "time" "github.com/rcrowley/go-metrics" @@ -42,7 +43,12 @@ type HandshakeConfig struct { } type HandshakeManager struct { - pendingHostMap *HostMap + // Mutex for interacting with the vpnIps and indexes maps + sync.RWMutex + + vpnIps map[iputil.VpnIp]*HostInfo + indexes map[uint32]*HostInfo + mainHostMap *HostMap lightHouse *LightHouse outside udp.Conn @@ -59,7 +65,8 @@ type HandshakeManager struct { func NewHandshakeManager(l *logrus.Logger, tunCidr *net.IPNet, preferredRanges []*net.IPNet, mainHostMap *HostMap, lightHouse *LightHouse, outside udp.Conn, config HandshakeConfig) *HandshakeManager { return &HandshakeManager{ - pendingHostMap: NewHostMap(l, "pending", tunCidr, preferredRanges), + vpnIps: map[iputil.VpnIp]*HostInfo{}, + indexes: map[uint32]*HostInfo{}, mainHostMap: mainHostMap, lightHouse: lightHouse, outside: outside, @@ -101,8 +108,8 @@ func (c *HandshakeManager) NextOutboundHandshakeTimerTick(now time.Time, f EncWr } func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, lighthouseTriggered bool) { - hostinfo, err := c.pendingHostMap.QueryVpnIp(vpnIp) - if err != nil { + hostinfo := c.QueryVpnIp(vpnIp) + if hostinfo == nil { return } hostinfo.Lock() @@ -111,7 +118,7 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light // We may have raced to completion but now that we have a lock we should ensure we have not yet completed. if hostinfo.HandshakeComplete { // Ensure we don't exist in the pending hostmap anymore since we have completed - c.pendingHostMap.DeleteHostInfo(hostinfo) + c.DeleteHostInfo(hostinfo) return } @@ -125,14 +132,14 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light // If we are out of time, clean up if hostinfo.HandshakeCounter >= c.config.retries { - hostinfo.logger(c.l).WithField("udpAddrs", hostinfo.remotes.CopyAddrs(c.pendingHostMap.preferredRanges)). + hostinfo.logger(c.l).WithField("udpAddrs", hostinfo.remotes.CopyAddrs(c.mainHostMap.preferredRanges)). WithField("initiatorIndex", hostinfo.localIndexId). WithField("remoteIndex", hostinfo.remoteIndexId). WithField("handshake", m{"stage": 1, "style": "ix_psk0"}). WithField("durationNs", time.Since(hostinfo.handshakeStart).Nanoseconds()). Info("Handshake timed out") c.metricTimedOut.Inc(1) - c.pendingHostMap.DeleteHostInfo(hostinfo) + c.DeleteHostInfo(hostinfo) return } @@ -144,7 +151,7 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light hostinfo.remotes = c.lightHouse.QueryCache(vpnIp) } - remotes := hostinfo.remotes.CopyAddrs(c.pendingHostMap.preferredRanges) + remotes := hostinfo.remotes.CopyAddrs(c.mainHostMap.preferredRanges) remotesHaveChanged := !udp.AddrSlice(remotes).Equal(hostinfo.HandshakeLastRemotes) // We only care about a lighthouse trigger if we have new remotes to send to. @@ -168,9 +175,9 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light // Send the handshake to all known ips, stage 2 takes care of assigning the hostinfo.remote based on the first to reply var sentTo []*udp.Addr - hostinfo.remotes.ForEach(c.pendingHostMap.preferredRanges, func(addr *udp.Addr, _ bool) { + hostinfo.remotes.ForEach(c.mainHostMap.preferredRanges, func(addr *udp.Addr, _ bool) { c.messageMetrics.Tx(header.Handshake, header.MessageSubType(hostinfo.HandshakePacket[0][1]), 1) - err = c.outside.WriteTo(hostinfo.HandshakePacket[0], addr) + err := c.outside.WriteTo(hostinfo.HandshakePacket[0], addr) if err != nil { hostinfo.logger(c.l).WithField("udpAddr", addr). WithField("initiatorIndex", hostinfo.localIndexId). @@ -204,9 +211,9 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light if *relay == vpnIp || *relay == c.lightHouse.myVpnIp { continue } - relayHostInfo, err := c.mainHostMap.QueryVpnIp(*relay) - if err != nil || relayHostInfo.remote == nil { - hostinfo.logger(c.l).WithError(err).WithField("relay", relay.String()).Info("Establish tunnel to relay target") + relayHostInfo := c.mainHostMap.QueryVpnIp(*relay) + if relayHostInfo == nil || relayHostInfo.remote == nil { + hostinfo.logger(c.l).WithField("relay", relay.String()).Info("Establish tunnel to relay target") f.Handshake(*relay) continue } @@ -289,14 +296,35 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light } } +// AddVpnIp will try to handshake with the provided vpn ip and return the hostinfo for it. func (c *HandshakeManager) AddVpnIp(vpnIp iputil.VpnIp, init func(*HostInfo)) *HostInfo { - hostinfo, created := c.pendingHostMap.AddVpnIp(vpnIp, init) + // A write lock is used to avoid having to recheck the map and trading a read lock for a write lock + c.Lock() + defer c.Unlock() + + if hostinfo, ok := c.vpnIps[vpnIp]; ok { + // We are already tracking this vpn ip + return hostinfo + } - if created { - c.OutboundHandshakeTimer.Add(vpnIp, c.config.tryInterval) - c.metricInitiated.Inc(1) + hostinfo := &HostInfo{ + vpnIp: vpnIp, + HandshakePacket: make(map[uint8][]byte, 0), + relayState: RelayState{ + relays: map[iputil.VpnIp]struct{}{}, + relayForByIp: map[iputil.VpnIp]*Relay{}, + relayForByIdx: map[uint32]*Relay{}, + }, } + if init != nil { + init(hostinfo) + } + + c.vpnIps[vpnIp] = hostinfo + c.metricInitiated.Inc(1) + c.OutboundHandshakeTimer.Add(vpnIp, c.config.tryInterval) + return hostinfo } @@ -318,8 +346,8 @@ var ( // ErrLocalIndexCollision if we already have an entry in the main or pending // hostmap for the hostinfo.localIndexId. func (c *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket uint8, f *Interface) (*HostInfo, error) { - c.pendingHostMap.Lock() - defer c.pendingHostMap.Unlock() + c.Lock() + defer c.Unlock() c.mainHostMap.Lock() defer c.mainHostMap.Unlock() @@ -350,7 +378,7 @@ func (c *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket return existingIndex, ErrLocalIndexCollision } - existingIndex, found = c.pendingHostMap.Indexes[hostinfo.localIndexId] + existingIndex, found = c.indexes[hostinfo.localIndexId] if found && existingIndex != hostinfo { // We have a collision, but for a different hostinfo return existingIndex, ErrLocalIndexCollision @@ -373,8 +401,8 @@ func (c *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket // won't have a localIndexId collision because we already have an entry in the // pendingHostMap. An existing hostinfo is returned if there was one. func (c *HandshakeManager) Complete(hostinfo *HostInfo, f *Interface) { - c.pendingHostMap.Lock() - defer c.pendingHostMap.Unlock() + c.Lock() + defer c.Unlock() c.mainHostMap.Lock() defer c.mainHostMap.Unlock() @@ -388,7 +416,7 @@ func (c *HandshakeManager) Complete(hostinfo *HostInfo, f *Interface) { } // We need to remove from the pending hostmap first to avoid undoing work when after to the main hostmap. - c.pendingHostMap.unlockedDeleteHostInfo(hostinfo) + c.unlockedDeleteHostInfo(hostinfo) c.mainHostMap.unlockedAddHostInfo(hostinfo, f) } @@ -396,8 +424,8 @@ func (c *HandshakeManager) Complete(hostinfo *HostInfo, f *Interface) { // and adds it to the pendingHostMap. Will error if we are unable to generate // a unique localIndexId func (c *HandshakeManager) AddIndexHostInfo(h *HostInfo) error { - c.pendingHostMap.Lock() - defer c.pendingHostMap.Unlock() + c.Lock() + defer c.Unlock() c.mainHostMap.RLock() defer c.mainHostMap.RUnlock() @@ -407,12 +435,12 @@ func (c *HandshakeManager) AddIndexHostInfo(h *HostInfo) error { return err } - _, inPending := c.pendingHostMap.Indexes[index] + _, inPending := c.indexes[index] _, inMain := c.mainHostMap.Indexes[index] if !inMain && !inPending { h.localIndexId = index - c.pendingHostMap.Indexes[index] = h + c.indexes[index] = h return nil } } @@ -420,22 +448,73 @@ func (c *HandshakeManager) AddIndexHostInfo(h *HostInfo) error { return errors.New("failed to generate unique localIndexId") } -func (c *HandshakeManager) addRemoteIndexHostInfo(index uint32, h *HostInfo) { - c.pendingHostMap.addRemoteIndexHostInfo(index, h) +func (c *HandshakeManager) DeleteHostInfo(hostinfo *HostInfo) { + c.Lock() + defer c.Unlock() + c.unlockedDeleteHostInfo(hostinfo) } -func (c *HandshakeManager) DeleteHostInfo(hostinfo *HostInfo) { - //l.Debugln("Deleting pending hostinfo :", hostinfo) - c.pendingHostMap.DeleteHostInfo(hostinfo) +func (c *HandshakeManager) unlockedDeleteHostInfo(hostinfo *HostInfo) { + delete(c.vpnIps, hostinfo.vpnIp) + if len(c.vpnIps) == 0 { + c.vpnIps = map[iputil.VpnIp]*HostInfo{} + } + + delete(c.indexes, hostinfo.localIndexId) + if len(c.vpnIps) == 0 { + c.indexes = map[uint32]*HostInfo{} + } + + if c.l.Level >= logrus.DebugLevel { + c.l.WithField("hostMap", m{"mapTotalSize": len(c.vpnIps), + "vpnIp": hostinfo.vpnIp, "indexNumber": hostinfo.localIndexId, "remoteIndexNumber": hostinfo.remoteIndexId}). + Debug("Pending hostmap hostInfo deleted") + } +} + +func (c *HandshakeManager) QueryVpnIp(vpnIp iputil.VpnIp) *HostInfo { + c.RLock() + defer c.RUnlock() + return c.vpnIps[vpnIp] +} + +func (c *HandshakeManager) QueryIndex(index uint32) *HostInfo { + c.RLock() + defer c.RUnlock() + return c.indexes[index] +} + +func (c *HandshakeManager) GetPreferredRanges() []*net.IPNet { + return c.mainHostMap.preferredRanges } -func (c *HandshakeManager) QueryIndex(index uint32) (*HostInfo, error) { - return c.pendingHostMap.QueryIndex(index) +func (c *HandshakeManager) ForEachVpnIp(f controlEach) { + c.RLock() + defer c.RUnlock() + + for _, v := range c.vpnIps { + f(v) + } +} + +func (c *HandshakeManager) ForEachIndex(f controlEach) { + c.RLock() + defer c.RUnlock() + + for _, v := range c.indexes { + f(v) + } } func (c *HandshakeManager) EmitStats() { - c.pendingHostMap.EmitStats("pending") - c.mainHostMap.EmitStats("main") + c.RLock() + hostLen := len(c.vpnIps) + indexLen := len(c.indexes) + c.RUnlock() + + metrics.GetOrRegisterGauge("hostmap.pending.hosts", nil).Update(int64(hostLen)) + metrics.GetOrRegisterGauge("hostmap.pending.indexes", nil).Update(int64(indexLen)) + c.mainHostMap.EmitStats() } // Utility functions below diff --git a/handshake_manager_test.go b/handshake_manager_test.go index 612ea4470..383e90084 100644 --- a/handshake_manager_test.go +++ b/handshake_manager_test.go @@ -20,7 +20,7 @@ func Test_NewHandshakeManagerVpnIp(t *testing.T) { ip := iputil.Ip2VpnIp(net.ParseIP("172.1.1.2")) preferredRanges := []*net.IPNet{localrange} mw := &mockEncWriter{} - mainHM := NewHostMap(l, "test", vpncidr, preferredRanges) + mainHM := NewHostMap(l, vpncidr, preferredRanges) lh := newTestLighthouse() blah := NewHandshakeManager(l, tuncidr, preferredRanges, mainHM, lh, &udp.NoopConn{}, defaultHandshakeConfig) @@ -48,7 +48,7 @@ func Test_NewHandshakeManagerVpnIp(t *testing.T) { assert.Len(t, mainHM.Hosts, 0) // Confirm they are in the pending index list - assert.Contains(t, blah.pendingHostMap.Hosts, ip) + assert.Contains(t, blah.vpnIps, ip) // Jump ahead `HandshakeRetries` ticks, offset by one to get the sleep logic right for i := 1; i <= DefaultHandshakeRetries+1; i++ { @@ -57,13 +57,13 @@ func Test_NewHandshakeManagerVpnIp(t *testing.T) { } // Confirm they are still in the pending index list - assert.Contains(t, blah.pendingHostMap.Hosts, ip) + assert.Contains(t, blah.vpnIps, ip) // Tick 1 more time, a minute will certainly flush it out blah.NextOutboundHandshakeTimerTick(now.Add(time.Minute), mw) // Confirm they have been removed - assert.NotContains(t, blah.pendingHostMap.Hosts, ip) + assert.NotContains(t, blah.vpnIps, ip) } func testCountTimerWheelEntries(tw *LockingTimerWheel[iputil.VpnIp]) (c int) { diff --git a/hostmap.go b/hostmap.go index e5949add2..c7f607c07 100644 --- a/hostmap.go +++ b/hostmap.go @@ -2,7 +2,6 @@ package nebula import ( "errors" - "fmt" "net" "sync" "sync/atomic" @@ -52,7 +51,6 @@ type Relay struct { type HostMap struct { sync.RWMutex //Because we concurrently read and write to our maps - name string Indexes map[uint32]*HostInfo Relays map[uint32]*HostInfo // Maps a Relay IDX to a Relay HostInfo object RemoteIndexes map[uint32]*HostInfo @@ -203,13 +201,13 @@ type HostInfo struct { remotes *RemoteList promoteCounter atomic.Uint32 ConnectionState *ConnectionState - handshakeStart time.Time //todo: this an entry in the handshake manager - HandshakeReady bool //todo: being in the manager means you are ready - HandshakeCounter int //todo: another handshake manager entry - HandshakeLastRemotes []*udp.Addr //todo: another handshake manager entry, which remotes we sent to last time - HandshakeComplete bool //todo: this should go away in favor of ConnectionState.ready - HandshakePacket map[uint8][]byte //todo: this is other handshake manager entry - packetStore []*cachedPacket //todo: this is other handshake manager entry + handshakeStart time.Time //todo: this an entry in the handshake manager + HandshakeReady bool //todo: being in the manager means you are ready + HandshakeCounter int //todo: another handshake manager entry + HandshakeLastRemotes []*udp.Addr //todo: another handshake manager entry, which remotes we sent to last time + HandshakeComplete bool //todo: this should go away in favor of ConnectionState.ready + HandshakePacket map[uint8][]byte + packetStore []*cachedPacket //todo: this is other handshake manager entry remoteIndexId uint32 localIndexId uint32 vpnIp iputil.VpnIp @@ -255,13 +253,12 @@ type cachedPacketMetrics struct { dropped metrics.Counter } -func NewHostMap(l *logrus.Logger, name string, vpnCIDR *net.IPNet, preferredRanges []*net.IPNet) *HostMap { +func NewHostMap(l *logrus.Logger, vpnCIDR *net.IPNet, preferredRanges []*net.IPNet) *HostMap { h := map[iputil.VpnIp]*HostInfo{} i := map[uint32]*HostInfo{} r := map[uint32]*HostInfo{} relays := map[uint32]*HostInfo{} m := HostMap{ - name: name, Indexes: i, Relays: relays, RemoteIndexes: r, @@ -273,8 +270,8 @@ func NewHostMap(l *logrus.Logger, name string, vpnCIDR *net.IPNet, preferredRang return &m } -// UpdateStats takes a name and reports host and index counts to the stats collection system -func (hm *HostMap) EmitStats(name string) { +// EmitStats reports host, index, and relay counts to the stats collection system +func (hm *HostMap) EmitStats() { hm.RLock() hostLen := len(hm.Hosts) indexLen := len(hm.Indexes) @@ -282,10 +279,10 @@ func (hm *HostMap) EmitStats(name string) { relaysLen := len(hm.Relays) hm.RUnlock() - metrics.GetOrRegisterGauge("hostmap."+name+".hosts", nil).Update(int64(hostLen)) - metrics.GetOrRegisterGauge("hostmap."+name+".indexes", nil).Update(int64(indexLen)) - metrics.GetOrRegisterGauge("hostmap."+name+".remoteIndexes", nil).Update(int64(remoteIndexLen)) - metrics.GetOrRegisterGauge("hostmap."+name+".relayIndexes", nil).Update(int64(relaysLen)) + metrics.GetOrRegisterGauge("hostmap.main.hosts", nil).Update(int64(hostLen)) + metrics.GetOrRegisterGauge("hostmap.main.indexes", nil).Update(int64(indexLen)) + metrics.GetOrRegisterGauge("hostmap.main.remoteIndexes", nil).Update(int64(remoteIndexLen)) + metrics.GetOrRegisterGauge("hostmap.main.relayIndexes", nil).Update(int64(relaysLen)) } func (hm *HostMap) RemoveRelay(localIdx uint32) { @@ -299,88 +296,6 @@ func (hm *HostMap) RemoveRelay(localIdx uint32) { hm.Unlock() } -func (hm *HostMap) GetIndexByVpnIp(vpnIp iputil.VpnIp) (uint32, error) { - hm.RLock() - if i, ok := hm.Hosts[vpnIp]; ok { - index := i.localIndexId - hm.RUnlock() - return index, nil - } - hm.RUnlock() - return 0, errors.New("vpn IP not found") -} - -func (hm *HostMap) Add(ip iputil.VpnIp, hostinfo *HostInfo) { - hm.Lock() - hm.Hosts[ip] = hostinfo - hm.Unlock() -} - -func (hm *HostMap) AddVpnIp(vpnIp iputil.VpnIp, init func(hostinfo *HostInfo)) (hostinfo *HostInfo, created bool) { - hm.RLock() - if h, ok := hm.Hosts[vpnIp]; !ok { - hm.RUnlock() - h = &HostInfo{ - vpnIp: vpnIp, - HandshakePacket: make(map[uint8][]byte, 0), - relayState: RelayState{ - relays: map[iputil.VpnIp]struct{}{}, - relayForByIp: map[iputil.VpnIp]*Relay{}, - relayForByIdx: map[uint32]*Relay{}, - }, - } - if init != nil { - init(h) - } - hm.Lock() - hm.Hosts[vpnIp] = h - hm.Unlock() - return h, true - } else { - hm.RUnlock() - return h, false - } -} - -// Only used by pendingHostMap when the remote index is not initially known -func (hm *HostMap) addRemoteIndexHostInfo(index uint32, h *HostInfo) { - hm.Lock() - h.remoteIndexId = index - hm.RemoteIndexes[index] = h - hm.Unlock() - - if hm.l.Level > logrus.DebugLevel { - hm.l.WithField("hostMap", m{"mapName": hm.name, "indexNumber": index, "mapTotalSize": len(hm.Indexes), - "hostinfo": m{"existing": true, "localIndexId": h.localIndexId, "hostId": h.vpnIp}}). - Debug("Hostmap remoteIndex added") - } -} - -// DeleteReverseIndex is used to clean up on recv_error -// This function should only ever be called on the pending hostmap -func (hm *HostMap) DeleteReverseIndex(index uint32) { - hm.Lock() - hostinfo, ok := hm.RemoteIndexes[index] - if ok { - delete(hm.Indexes, hostinfo.localIndexId) - delete(hm.RemoteIndexes, index) - - // Check if we have an entry under hostId that matches the same hostinfo - // instance. Clean it up as well if we do (they might not match in pendingHostmap) - var hostinfo2 *HostInfo - hostinfo2, ok = hm.Hosts[hostinfo.vpnIp] - if ok && hostinfo2 == hostinfo { - delete(hm.Hosts, hostinfo.vpnIp) - } - } - hm.Unlock() - - if hm.l.Level >= logrus.DebugLevel { - hm.l.WithField("hostMap", m{"mapName": hm.name, "indexNumber": index, "mapTotalSize": len(hm.Indexes)}). - Debug("Hostmap remote index deleted") - } -} - // DeleteHostInfo will fully unlink the hostinfo and return true if it was the final hostinfo for this vpn ip func (hm *HostMap) DeleteHostInfo(hostinfo *HostInfo) bool { // Delete the host itself, ensuring it's not modified anymore @@ -393,12 +308,6 @@ func (hm *HostMap) DeleteHostInfo(hostinfo *HostInfo) bool { return final } -func (hm *HostMap) DeleteRelayIdx(localIdx uint32) { - hm.Lock() - defer hm.Unlock() - delete(hm.RemoteIndexes, localIdx) -} - func (hm *HostMap) MakePrimary(hostinfo *HostInfo) { hm.Lock() defer hm.Unlock() @@ -476,7 +385,7 @@ func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo) { } if hm.l.Level >= logrus.DebugLevel { - hm.l.WithField("hostMap", m{"mapName": hm.name, "mapTotalSize": len(hm.Hosts), + hm.l.WithField("hostMap", m{"mapTotalSize": len(hm.Hosts), "vpnIp": hostinfo.vpnIp, "indexNumber": hostinfo.localIndexId, "remoteIndexNumber": hostinfo.remoteIndexId}). Debug("Hostmap hostInfo deleted") } @@ -486,55 +395,41 @@ func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo) { } } -func (hm *HostMap) QueryIndex(index uint32) (*HostInfo, error) { - //TODO: we probably just want to return bool instead of error, or at least a static error +func (hm *HostMap) QueryIndex(index uint32) *HostInfo { hm.RLock() if h, ok := hm.Indexes[index]; ok { hm.RUnlock() - return h, nil + return h } else { hm.RUnlock() - return nil, errors.New("unable to find index") + return nil } } -// Retrieves a HostInfo by Index. Returns whether the HostInfo is primary at time of query. -// This helper exists so that the hostinfo.prev pointer can be read while the hostmap lock is held. -func (hm *HostMap) QueryIndexIsPrimary(index uint32) (*HostInfo, bool, error) { - //TODO: we probably just want to return bool instead of error, or at least a static error - hm.RLock() - if h, ok := hm.Indexes[index]; ok { - hm.RUnlock() - return h, h.prev == nil, nil - } else { - hm.RUnlock() - return nil, false, errors.New("unable to find index") - } -} -func (hm *HostMap) QueryRelayIndex(index uint32) (*HostInfo, error) { +func (hm *HostMap) QueryRelayIndex(index uint32) *HostInfo { //TODO: we probably just want to return bool instead of error, or at least a static error hm.RLock() if h, ok := hm.Relays[index]; ok { hm.RUnlock() - return h, nil + return h } else { hm.RUnlock() - return nil, errors.New("unable to find index") + return nil } } -func (hm *HostMap) QueryReverseIndex(index uint32) (*HostInfo, error) { +func (hm *HostMap) QueryReverseIndex(index uint32) *HostInfo { hm.RLock() if h, ok := hm.RemoteIndexes[index]; ok { hm.RUnlock() - return h, nil + return h } else { hm.RUnlock() - return nil, fmt.Errorf("unable to find reverse index or connectionstate nil in %s hostmap", hm.name) + return nil } } -func (hm *HostMap) QueryVpnIp(vpnIp iputil.VpnIp) (*HostInfo, error) { +func (hm *HostMap) QueryVpnIp(vpnIp iputil.VpnIp) *HostInfo { return hm.queryVpnIp(vpnIp, nil) } @@ -558,11 +453,11 @@ func (hm *HostMap) QueryVpnIpRelayFor(targetIp, relayHostIp iputil.VpnIp) (*Host // PromoteBestQueryVpnIp will attempt to lazily switch to the best remote every // `PromoteEvery` calls to this function for a given host. -func (hm *HostMap) PromoteBestQueryVpnIp(vpnIp iputil.VpnIp, ifce *Interface) (*HostInfo, error) { +func (hm *HostMap) PromoteBestQueryVpnIp(vpnIp iputil.VpnIp, ifce *Interface) *HostInfo { return hm.queryVpnIp(vpnIp, ifce) } -func (hm *HostMap) queryVpnIp(vpnIp iputil.VpnIp, promoteIfce *Interface) (*HostInfo, error) { +func (hm *HostMap) queryVpnIp(vpnIp iputil.VpnIp, promoteIfce *Interface) *HostInfo { hm.RLock() if h, ok := hm.Hosts[vpnIp]; ok { hm.RUnlock() @@ -570,12 +465,12 @@ func (hm *HostMap) queryVpnIp(vpnIp iputil.VpnIp, promoteIfce *Interface) (*Host if promoteIfce != nil && !promoteIfce.lightHouse.amLighthouse { h.TryPromoteBest(hm.preferredRanges, promoteIfce) } - return h, nil + return h } hm.RUnlock() - return nil, errors.New("unable to find host") + return nil } // unlockedAddHostInfo assumes you have a write-lock and will add a hostinfo object to the hostmap Indexes and RemoteIndexes maps. @@ -598,7 +493,7 @@ func (hm *HostMap) unlockedAddHostInfo(hostinfo *HostInfo, f *Interface) { hm.RemoteIndexes[hostinfo.remoteIndexId] = hostinfo if hm.l.Level >= logrus.DebugLevel { - hm.l.WithField("hostMap", m{"mapName": hm.name, "vpnIp": hostinfo.vpnIp, "mapTotalSize": len(hm.Hosts), + hm.l.WithField("hostMap", m{"vpnIp": hostinfo.vpnIp, "mapTotalSize": len(hm.Hosts), "hostinfo": m{"existing": true, "localIndexId": hostinfo.localIndexId, "hostId": hostinfo.vpnIp}}). Debug("Hostmap vpnIp added") } @@ -614,6 +509,28 @@ func (hm *HostMap) unlockedAddHostInfo(hostinfo *HostInfo, f *Interface) { } } +func (hm *HostMap) GetPreferredRanges() []*net.IPNet { + return hm.preferredRanges +} + +func (hm *HostMap) ForEachVpnIp(f controlEach) { + hm.RLock() + defer hm.RUnlock() + + for _, v := range hm.Hosts { + f(v) + } +} + +func (hm *HostMap) ForEachIndex(f controlEach) { + hm.RLock() + defer hm.RUnlock() + + for _, v := range hm.Indexes { + f(v) + } +} + // TryPromoteBest handles re-querying lighthouses and probing for better paths // NOTE: It is an error to call this if you are a lighthouse since they should not roam clients! func (i *HostInfo) TryPromoteBest(preferredRanges []*net.IPNet, ifce *Interface) { diff --git a/hostmap_test.go b/hostmap_test.go index e523a216f..c1c0dcead 100644 --- a/hostmap_test.go +++ b/hostmap_test.go @@ -11,7 +11,7 @@ import ( func TestHostMap_MakePrimary(t *testing.T) { l := test.NewLogger() hm := NewHostMap( - l, "test", + l, &net.IPNet{ IP: net.IP{10, 0, 0, 1}, Mask: net.IPMask{255, 255, 255, 0}, @@ -32,7 +32,7 @@ func TestHostMap_MakePrimary(t *testing.T) { hm.unlockedAddHostInfo(h1, f) // Make sure we go h1 -> h2 -> h3 -> h4 - prim, _ := hm.QueryVpnIp(1) + prim := hm.QueryVpnIp(1) assert.Equal(t, h1.localIndexId, prim.localIndexId) assert.Equal(t, h2.localIndexId, prim.next.localIndexId) assert.Nil(t, prim.prev) @@ -47,7 +47,7 @@ func TestHostMap_MakePrimary(t *testing.T) { hm.MakePrimary(h3) // Make sure we go h3 -> h1 -> h2 -> h4 - prim, _ = hm.QueryVpnIp(1) + prim = hm.QueryVpnIp(1) assert.Equal(t, h3.localIndexId, prim.localIndexId) assert.Equal(t, h1.localIndexId, prim.next.localIndexId) assert.Nil(t, prim.prev) @@ -62,7 +62,7 @@ func TestHostMap_MakePrimary(t *testing.T) { hm.MakePrimary(h4) // Make sure we go h4 -> h3 -> h1 -> h2 - prim, _ = hm.QueryVpnIp(1) + prim = hm.QueryVpnIp(1) assert.Equal(t, h4.localIndexId, prim.localIndexId) assert.Equal(t, h3.localIndexId, prim.next.localIndexId) assert.Nil(t, prim.prev) @@ -77,7 +77,7 @@ func TestHostMap_MakePrimary(t *testing.T) { hm.MakePrimary(h4) // Make sure we go h4 -> h3 -> h1 -> h2 - prim, _ = hm.QueryVpnIp(1) + prim = hm.QueryVpnIp(1) assert.Equal(t, h4.localIndexId, prim.localIndexId) assert.Equal(t, h3.localIndexId, prim.next.localIndexId) assert.Nil(t, prim.prev) @@ -92,7 +92,7 @@ func TestHostMap_MakePrimary(t *testing.T) { func TestHostMap_DeleteHostInfo(t *testing.T) { l := test.NewLogger() hm := NewHostMap( - l, "test", + l, &net.IPNet{ IP: net.IP{10, 0, 0, 1}, Mask: net.IPMask{255, 255, 255, 0}, @@ -119,11 +119,11 @@ func TestHostMap_DeleteHostInfo(t *testing.T) { // h6 should be deleted assert.Nil(t, h6.next) assert.Nil(t, h6.prev) - _, err := hm.QueryIndex(h6.localIndexId) - assert.Error(t, err) + h := hm.QueryIndex(h6.localIndexId) + assert.Nil(t, h) // Make sure we go h1 -> h2 -> h3 -> h4 -> h5 - prim, _ := hm.QueryVpnIp(1) + prim := hm.QueryVpnIp(1) assert.Equal(t, h1.localIndexId, prim.localIndexId) assert.Equal(t, h2.localIndexId, prim.next.localIndexId) assert.Nil(t, prim.prev) @@ -142,7 +142,7 @@ func TestHostMap_DeleteHostInfo(t *testing.T) { assert.Nil(t, h1.next) // Make sure we go h2 -> h3 -> h4 -> h5 - prim, _ = hm.QueryVpnIp(1) + prim = hm.QueryVpnIp(1) assert.Equal(t, h2.localIndexId, prim.localIndexId) assert.Equal(t, h3.localIndexId, prim.next.localIndexId) assert.Nil(t, prim.prev) @@ -160,7 +160,7 @@ func TestHostMap_DeleteHostInfo(t *testing.T) { assert.Nil(t, h3.next) // Make sure we go h2 -> h4 -> h5 - prim, _ = hm.QueryVpnIp(1) + prim = hm.QueryVpnIp(1) assert.Equal(t, h2.localIndexId, prim.localIndexId) assert.Equal(t, h4.localIndexId, prim.next.localIndexId) assert.Nil(t, prim.prev) @@ -176,7 +176,7 @@ func TestHostMap_DeleteHostInfo(t *testing.T) { assert.Nil(t, h5.next) // Make sure we go h2 -> h4 - prim, _ = hm.QueryVpnIp(1) + prim = hm.QueryVpnIp(1) assert.Equal(t, h2.localIndexId, prim.localIndexId) assert.Equal(t, h4.localIndexId, prim.next.localIndexId) assert.Nil(t, prim.prev) @@ -190,7 +190,7 @@ func TestHostMap_DeleteHostInfo(t *testing.T) { assert.Nil(t, h2.next) // Make sure we only have h4 - prim, _ = hm.QueryVpnIp(1) + prim = hm.QueryVpnIp(1) assert.Equal(t, h4.localIndexId, prim.localIndexId) assert.Nil(t, prim.prev) assert.Nil(t, prim.next) @@ -202,6 +202,6 @@ func TestHostMap_DeleteHostInfo(t *testing.T) { assert.Nil(t, h4.next) // Make sure we have nil - prim, _ = hm.QueryVpnIp(1) + prim = hm.QueryVpnIp(1) assert.Nil(t, prim) } diff --git a/inside.go b/inside.go index 18148b67e..0d4392666 100644 --- a/inside.go +++ b/inside.go @@ -121,14 +121,10 @@ func (f *Interface) getOrHandshake(vpnIp iputil.VpnIp) *HostInfo { return nil } } - hostinfo, err := f.hostMap.PromoteBestQueryVpnIp(vpnIp, f) - //if err != nil || hostinfo.ConnectionState == nil { - if err != nil { - hostinfo, err = f.handshakeManager.pendingHostMap.QueryVpnIp(vpnIp) - if err != nil { - hostinfo = f.handshakeManager.AddVpnIp(vpnIp, f.initHostInfo) - } + hostinfo := f.hostMap.PromoteBestQueryVpnIp(vpnIp, f) + if hostinfo == nil { + hostinfo = f.handshakeManager.AddVpnIp(vpnIp, f.initHostInfo) } ci := hostinfo.ConnectionState @@ -137,6 +133,7 @@ func (f *Interface) getOrHandshake(vpnIp iputil.VpnIp) *HostInfo { } // Handshake is not ready, we need to grab the lock now before we start the handshake process + //TODO: move this to handshake manager hostinfo.Lock() defer hostinfo.Unlock() diff --git a/main.go b/main.go index e4c262413..5845b7603 100644 --- a/main.go +++ b/main.go @@ -212,7 +212,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg } } - hostMap := NewHostMap(l, "main", tunCidr, preferredRanges) + hostMap := NewHostMap(l, tunCidr, preferredRanges) hostMap.metricsEnabled = c.GetBool("stats.message_metrics", false) l. @@ -339,7 +339,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg //TODO: check if we _should_ be emitting stats go ifce.emitStats(ctx, c.GetDuration("stats.interval", time.Second*10)) - attachCommands(l, c, ssh, hostMap, handshakeManager.pendingHostMap, lightHouse, ifce) + attachCommands(l, c, ssh, ifce) // Start DNS server last to allow using the nebula IP as lighthouse.dns.host var dnsStart func() diff --git a/outside.go b/outside.go index 19f5931f2..19a980bfa 100644 --- a/outside.go +++ b/outside.go @@ -64,9 +64,9 @@ func (f *Interface) readOutsidePackets(addr *udp.Addr, via *ViaSender, out []byt var hostinfo *HostInfo // verify if we've seen this index before, otherwise respond to the handshake initiation if h.Type == header.Message && h.Subtype == header.MessageRelay { - hostinfo, _ = f.hostMap.QueryRelayIndex(h.RemoteIndex) + hostinfo = f.hostMap.QueryRelayIndex(h.RemoteIndex) } else { - hostinfo, _ = f.hostMap.QueryIndex(h.RemoteIndex) + hostinfo = f.hostMap.QueryIndex(h.RemoteIndex) } var ci *ConnectionState @@ -449,12 +449,9 @@ func (f *Interface) handleRecvError(addr *udp.Addr, h *header.H) { Debug("Recv error received") } - // First, clean up in the pending hostmap - f.handshakeManager.pendingHostMap.DeleteReverseIndex(h.RemoteIndex) - - hostinfo, err := f.hostMap.QueryReverseIndex(h.RemoteIndex) - if err != nil { - f.l.Debugln(err, ": ", h.RemoteIndex) + hostinfo := f.hostMap.QueryReverseIndex(h.RemoteIndex) + if hostinfo == nil { + f.l.WithField("remoteIndex", h.RemoteIndex).Debugln("Did not find remote index in main hostmap") return } @@ -464,14 +461,14 @@ func (f *Interface) handleRecvError(addr *udp.Addr, h *header.H) { if !hostinfo.RecvErrorExceeded() { return } + if hostinfo.remote != nil && !hostinfo.remote.Equals(addr) { f.l.Infoln("Someone spoofing recv_errors? ", addr, hostinfo.remote) return } f.closeTunnel(hostinfo) - // We also delete it from pending hostmap to allow for - // fast reconnect. + // We also delete it from pending hostmap to allow for fast reconnect. f.handshakeManager.DeleteHostInfo(hostinfo) } diff --git a/relay_manager.go b/relay_manager.go index fb90eecc3..8f6365293 100644 --- a/relay_manager.go +++ b/relay_manager.go @@ -131,9 +131,9 @@ func (rm *relayManager) handleCreateRelayResponse(h *HostInfo, f *Interface, m * return } // I'm the middle man. Let the initiator know that the I've established the relay they requested. - peerHostInfo, err := rm.hostmap.QueryVpnIp(relay.PeerIp) - if err != nil { - rm.l.WithError(err).WithField("relayTo", relay.PeerIp).Error("Can't find a HostInfo for peer") + peerHostInfo := rm.hostmap.QueryVpnIp(relay.PeerIp) + if peerHostInfo == nil { + rm.l.WithField("relayTo", relay.PeerIp).Error("Can't find a HostInfo for peer") return } peerRelay, ok := peerHostInfo.relayState.QueryRelayForByIp(target) @@ -240,8 +240,8 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N if !rm.GetAmRelay() { return } - peer, err := rm.hostmap.QueryVpnIp(target) - if err != nil { + peer := rm.hostmap.QueryVpnIp(target) + if peer == nil { // Try to establish a connection to this host. If we get a future relay request, // we'll be ready! f.getOrHandshake(target) @@ -253,6 +253,7 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N } sendCreateRequest := false var index uint32 + var err error targetRelay, ok := peer.relayState.QueryRelayForByIp(from) if ok { index = targetRelay.LocalIndex diff --git a/ssh.go b/ssh.go index 6223314fe..0f624dbe5 100644 --- a/ssh.go +++ b/ssh.go @@ -3,6 +3,7 @@ package nebula import ( "bytes" "encoding/json" + "errors" "flag" "fmt" "io/ioutil" @@ -168,7 +169,7 @@ func configSSH(l *logrus.Logger, ssh *sshd.SSHServer, c *config.C) (func(), erro return runner, nil } -func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap *HostMap, pendingHostMap *HostMap, lightHouse *LightHouse, ifce *Interface) { +func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Interface) { ssh.RegisterCommand(&sshd.Command{ Name: "list-hostmap", ShortDescription: "List all known previously connected hosts", @@ -181,7 +182,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap return fl, &s }, Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshListHostMap(hostMap, fs, w) + return sshListHostMap(f.hostMap, fs, w) }, }) @@ -197,7 +198,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap return fl, &s }, Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshListHostMap(pendingHostMap, fs, w) + return sshListHostMap(f.handshakeManager, fs, w) }, }) @@ -212,7 +213,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap return fl, &s }, Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshListLighthouseMap(lightHouse, fs, w) + return sshListLighthouseMap(f.lightHouse, fs, w) }, }) @@ -277,7 +278,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap Name: "version", ShortDescription: "Prints the currently running version of nebula", Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshVersion(ifce, fs, a, w) + return sshVersion(f, fs, a, w) }, }) @@ -293,7 +294,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap return fl, &s }, Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshPrintCert(ifce, fs, a, w) + return sshPrintCert(f, fs, a, w) }, }) @@ -307,7 +308,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap return fl, &s }, Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshPrintTunnel(ifce, fs, a, w) + return sshPrintTunnel(f, fs, a, w) }, }) @@ -321,7 +322,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap return fl, &s }, Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshPrintRelays(ifce, fs, a, w) + return sshPrintRelays(f, fs, a, w) }, }) @@ -335,7 +336,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap return fl, &s }, Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshChangeRemote(ifce, fs, a, w) + return sshChangeRemote(f, fs, a, w) }, }) @@ -349,7 +350,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap return fl, &s }, Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshCloseTunnel(ifce, fs, a, w) + return sshCloseTunnel(f, fs, a, w) }, }) @@ -364,7 +365,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap return fl, &s }, Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshCreateTunnel(ifce, fs, a, w) + return sshCreateTunnel(f, fs, a, w) }, }) @@ -373,12 +374,12 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap ShortDescription: "Query the lighthouses for the provided vpn ip", Help: "This command is asynchronous. Only currently known udp ips will be printed.", Callback: func(fs interface{}, a []string, w sshd.StringWriter) error { - return sshQueryLighthouse(ifce, fs, a, w) + return sshQueryLighthouse(f, fs, a, w) }, }) } -func sshListHostMap(hostMap *HostMap, a interface{}, w sshd.StringWriter) error { +func sshListHostMap(hl controlHostLister, a interface{}, w sshd.StringWriter) error { fs, ok := a.(*sshListHostMapFlags) if !ok { //TODO: error @@ -387,9 +388,9 @@ func sshListHostMap(hostMap *HostMap, a interface{}, w sshd.StringWriter) error var hm []ControlHostInfo if fs.ByIndex { - hm = listHostMapIndexes(hostMap) + hm = listHostMapIndexes(hl) } else { - hm = listHostMapHosts(hostMap) + hm = listHostMapHosts(hl) } sort.Slice(hm, func(i, j int) bool { @@ -546,8 +547,8 @@ func sshCloseTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringWr return w.WriteLine(fmt.Sprintf("The provided vpn ip could not be parsed: %s", a[0])) } - hostInfo, err := ifce.hostMap.QueryVpnIp(vpnIp) - if err != nil { + hostInfo := ifce.hostMap.QueryVpnIp(vpnIp) + if hostInfo == nil { return w.WriteLine(fmt.Sprintf("Could not find tunnel for vpn ip: %v", a[0])) } @@ -588,12 +589,12 @@ func sshCreateTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringW return w.WriteLine(fmt.Sprintf("The provided vpn ip could not be parsed: %s", a[0])) } - hostInfo, _ := ifce.hostMap.QueryVpnIp(vpnIp) + hostInfo := ifce.hostMap.QueryVpnIp(vpnIp) if hostInfo != nil { return w.WriteLine(fmt.Sprintf("Tunnel already exists")) } - hostInfo, _ = ifce.handshakeManager.pendingHostMap.QueryVpnIp(vpnIp) + hostInfo = ifce.handshakeManager.QueryVpnIp(vpnIp) if hostInfo != nil { return w.WriteLine(fmt.Sprintf("Tunnel already handshaking")) } @@ -645,8 +646,8 @@ func sshChangeRemote(ifce *Interface, fs interface{}, a []string, w sshd.StringW return w.WriteLine(fmt.Sprintf("The provided vpn ip could not be parsed: %s", a[0])) } - hostInfo, err := ifce.hostMap.QueryVpnIp(vpnIp) - if err != nil { + hostInfo := ifce.hostMap.QueryVpnIp(vpnIp) + if hostInfo == nil { return w.WriteLine(fmt.Sprintf("Could not find tunnel for vpn ip: %v", a[0])) } @@ -765,8 +766,8 @@ func sshPrintCert(ifce *Interface, fs interface{}, a []string, w sshd.StringWrit return w.WriteLine(fmt.Sprintf("The provided vpn ip could not be parsed: %s", a[0])) } - hostInfo, err := ifce.hostMap.QueryVpnIp(vpnIp) - if err != nil { + hostInfo := ifce.hostMap.QueryVpnIp(vpnIp) + if hostInfo == nil { return w.WriteLine(fmt.Sprintf("Could not find tunnel for vpn ip: %v", a[0])) } @@ -851,9 +852,9 @@ func sshPrintRelays(ifce *Interface, fs interface{}, a []string, w sshd.StringWr for k, v := range relays { ro := RelayOutput{NebulaIp: v.vpnIp} co.Relays = append(co.Relays, &ro) - relayHI, err := ifce.hostMap.QueryVpnIp(v.vpnIp) - if err != nil { - ro.RelayForIps = append(ro.RelayForIps, RelayFor{Error: err}) + relayHI := ifce.hostMap.QueryVpnIp(v.vpnIp) + if relayHI == nil { + ro.RelayForIps = append(ro.RelayForIps, RelayFor{Error: errors.New("could not find hostinfo")}) continue } for _, vpnIp := range relayHI.relayState.CopyRelayForIps() { @@ -889,8 +890,8 @@ func sshPrintRelays(ifce *Interface, fs interface{}, a []string, w sshd.StringWr rf.Error = fmt.Errorf("hostmap LocalIndex '%v' does not match RelayState LocalIndex", k) } } - relayedHI, err := ifce.hostMap.QueryVpnIp(vpnIp) - if err == nil { + relayedHI := ifce.hostMap.QueryVpnIp(vpnIp) + if relayedHI != nil { rf.RelayedThrough = append(rf.RelayedThrough, relayedHI.relayState.CopyRelayIps()...) } @@ -925,8 +926,8 @@ func sshPrintTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringWr return w.WriteLine(fmt.Sprintf("The provided vpn ip could not be parsed: %s", a[0])) } - hostInfo, err := ifce.hostMap.QueryVpnIp(vpnIp) - if err != nil { + hostInfo := ifce.hostMap.QueryVpnIp(vpnIp) + if hostInfo == nil { return w.WriteLine(fmt.Sprintf("Could not find tunnel for vpn ip: %v", a[0])) } From 96f51f78ea6d623fc5fc22476c74652bef842771 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 13:53:39 -0400 Subject: [PATCH 17/52] Bump golang.org/x/sys from 0.8.0 to 0.10.0 (#926) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.8.0 to 0.10.0. - [Commits](https://github.com/golang/sys/compare/v0.8.0...v0.10.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bf8e2829d..bf4ba5723 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( golang.org/x/crypto v0.8.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/net v0.9.0 - golang.org/x/sys v0.8.0 + golang.org/x/sys v0.10.0 golang.org/x/term v0.8.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b diff --git a/go.sum b/go.sum index 7485b8893..0ef16a833 100644 --- a/go.sum +++ b/go.sum @@ -194,8 +194,8 @@ 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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= From e5af94e27a6907c5adbe5eea2649e480bce1af89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 13:56:09 -0400 Subject: [PATCH 18/52] Bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0 (#927) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.15.1 to 1.16.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.15.1...v1.16.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index bf4ba5723..fd23700bb 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/kardianos/service v1.2.2 github.com/miekg/dns v1.1.55 github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f - github.com/prometheus/client_golang v1.15.1 + github.com/prometheus/client_golang v1.16.0 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/sirupsen/logrus v1.9.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e @@ -41,7 +41,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + 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 diff --git a/go.sum b/go.sum index 0ef16a833..6acdf2360 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -113,8 +113,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -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.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= 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= From 9c6592b1599b7c0dc77825a11641869dec89db8d Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Wed, 26 Jul 2023 12:52:14 -0500 Subject: [PATCH 19/52] Guard e2e udp and tun channels when closed (#934) --- e2e/handshakes_test.go | 2 ++ overlay/tun_tester.go | 15 ++++++++++++++- udp/udp_tester.go | 19 ++++++++++++++++--- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/e2e/handshakes_test.go b/e2e/handshakes_test.go index aa6260314..d02a1a733 100644 --- a/e2e/handshakes_test.go +++ b/e2e/handshakes_test.go @@ -410,6 +410,8 @@ func TestStage1RaceRelays(t *testing.T) { p := r.RouteForAllUntilTxTun(myControl) _ = p + r.FlushAll() + myControl.Stop() theirControl.Stop() relayControl.Stop() diff --git a/overlay/tun_tester.go b/overlay/tun_tester.go index 3a49dcbb5..a2a57e13f 100644 --- a/overlay/tun_tester.go +++ b/overlay/tun_tester.go @@ -8,6 +8,7 @@ import ( "io" "net" "os" + "sync/atomic" "github.com/sirupsen/logrus" "github.com/slackhq/nebula/cidr" @@ -21,6 +22,7 @@ type TestTun struct { routeTree *cidr.Tree4 l *logrus.Logger + closed atomic.Bool rxPackets chan []byte // Packets to receive into nebula TxPackets chan []byte // Packets transmitted outside by nebula } @@ -50,6 +52,10 @@ func newTunFromFd(_ *logrus.Logger, _ int, _ *net.IPNet, _ int, _ []Route, _ int // These are unencrypted ip layer frames destined for another nebula node. // packets should exit the udp side, capture them with udpConn.Get func (t *TestTun) Send(packet []byte) { + if t.closed.Load() { + return + } + if t.l.Level >= logrus.DebugLevel { t.l.WithField("dataLen", len(packet)).Debug("Tun receiving injected packet") } @@ -98,6 +104,10 @@ func (t *TestTun) Name() string { } func (t *TestTun) Write(b []byte) (n int, err error) { + if t.closed.Load() { + return 0, io.ErrClosedPipe + } + packet := make([]byte, len(b), len(b)) copy(packet, b) t.TxPackets <- packet @@ -105,7 +115,10 @@ func (t *TestTun) Write(b []byte) (n int, err error) { } func (t *TestTun) Close() error { - close(t.rxPackets) + if t.closed.CompareAndSwap(false, true) { + close(t.rxPackets) + close(t.TxPackets) + } return nil } diff --git a/udp/udp_tester.go b/udp/udp_tester.go index f03a69cbe..55985f47f 100644 --- a/udp/udp_tester.go +++ b/udp/udp_tester.go @@ -5,7 +5,9 @@ package udp import ( "fmt" + "io" "net" + "sync/atomic" "github.com/sirupsen/logrus" "github.com/slackhq/nebula/config" @@ -42,7 +44,8 @@ type TesterConn struct { RxPackets chan *Packet // Packets to receive into nebula TxPackets chan *Packet // Packets transmitted outside by nebula - l *logrus.Logger + closed atomic.Bool + l *logrus.Logger } func NewListener(l *logrus.Logger, ip net.IP, port int, _ bool, _ int) (Conn, error) { @@ -58,6 +61,10 @@ func NewListener(l *logrus.Logger, ip net.IP, port int, _ bool, _ int) (Conn, er // this is an encrypted packet or a handshake message in most cases // packets were transmitted from another nebula node, you can send them with Tun.Send func (u *TesterConn) Send(packet *Packet) { + if u.closed.Load() { + return + } + h := &header.H{} if err := h.Parse(packet.Data); err != nil { panic(err) @@ -92,6 +99,10 @@ func (u *TesterConn) Get(block bool) *Packet { //********************************************************************************************************************// func (u *TesterConn) WriteTo(b []byte, addr *Addr) error { + if u.closed.Load() { + return io.ErrClosedPipe + } + p := &Packet{ Data: make([]byte, len(b), len(b)), FromIp: make([]byte, 16), @@ -142,7 +153,9 @@ func (u *TesterConn) Rebind() error { } func (u *TesterConn) Close() error { - close(u.RxPackets) - close(u.TxPackets) + if u.closed.CompareAndSwap(false, true) { + close(u.RxPackets) + close(u.TxPackets) + } return nil } From 03e70210a5ec6354e4c9ebb9c485bcb1122f1098 Mon Sep 17 00:00:00 2001 From: c0repwn3r <36366790+c0repwn3r@users.noreply.github.com> Date: Thu, 27 Jul 2023 14:44:47 -0400 Subject: [PATCH 20/52] Add support for NetBSD (#916) --- Makefile | 3 +- examples/config.yml | 1 + overlay/tun_netbsd.go | 156 ++++++++++++++++++++++++++++++++++++++++++ udp/udp_netbsd.go | 46 +++++++++++++ 4 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 overlay/tun_netbsd.go create mode 100644 udp/udp_netbsd.go diff --git a/Makefile b/Makefile index 7eaa07f36..7d9d49733 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,8 @@ ALL = $(ALL_LINUX) \ darwin-amd64 \ darwin-arm64 \ windows-amd64 \ - windows-arm64 + windows-arm64 \ + netbsd-amd64 e2e: $(TEST_ENV) go test -tags=e2e_testing -count=1 $(TEST_FLAGS) ./e2e diff --git a/examples/config.yml b/examples/config.yml index 23ff50561..5c28fea7c 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -207,6 +207,7 @@ tun: disabled: false # Name of the device. If not set, a default will be chosen by the OS. # For macOS: if set, must be in the form `utun[0-9]+`. + # For NetBSD: Required to be set, must be in the form `tun[0-9]+` dev: nebula1 # Toggles forwarding of local broadcast packets, the address of which depends on the ip/mask encoded in pki.cert drop_local_broadcast: false diff --git a/overlay/tun_netbsd.go b/overlay/tun_netbsd.go new file mode 100644 index 000000000..cd171496e --- /dev/null +++ b/overlay/tun_netbsd.go @@ -0,0 +1,156 @@ +//go:build !e2e_testing +// +build !e2e_testing + +package overlay + +import ( + "fmt" + "io" + "net" + "os" + "os/exec" + "regexp" + "strconv" + "syscall" + "unsafe" + + "github.com/sirupsen/logrus" + "github.com/slackhq/nebula/cidr" + "github.com/slackhq/nebula/iputil" +) + +type ifreqDestroy struct { + Name [16]byte + pad [16]byte +} + +type tun struct { + Device string + cidr *net.IPNet + MTU int + Routes []Route + routeTree *cidr.Tree4 + l *logrus.Logger + + io.ReadWriteCloser +} + +func (t *tun) Close() error { + if t.ReadWriteCloser != nil { + if err := t.ReadWriteCloser.Close(); err != nil { + return err + } + + s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP) + if err != nil { + return err + } + defer syscall.Close(s) + + ifreq := ifreqDestroy{Name: t.deviceBytes()} + + err = ioctl(uintptr(s), syscall.SIOCIFDESTROY, uintptr(unsafe.Pointer(&ifreq))) + + return err + } + return nil +} + +func newTunFromFd(_ *logrus.Logger, _ int, _ *net.IPNet, _ int, _ []Route, _ int, _ bool) (*tun, error) { + return nil, fmt.Errorf("newTunFromFd not supported in NetBSD") +} + +var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`) + +func newTun(l *logrus.Logger, deviceName string, cidr *net.IPNet, defaultMTU int, routes []Route, _ int, _ bool, _ bool) (*tun, error) { + // Try to open tun device + var file *os.File + var err error + if deviceName == "" { + return nil, fmt.Errorf("a device name in the format of /dev/tunN must be specified") + } + if !deviceNameRE.MatchString(deviceName) { + return nil, fmt.Errorf("a device name in the format of /dev/tunN must be specified") + } + file, err = os.OpenFile("/dev/"+deviceName, os.O_RDWR, 0) + + if err != nil { + return nil, err + } + + routeTree, err := makeRouteTree(l, routes, false) + + if err != nil { + return nil, err + } + + return &tun{ + ReadWriteCloser: file, + Device: deviceName, + cidr: cidr, + MTU: defaultMTU, + Routes: routes, + routeTree: routeTree, + l: l, + }, nil +} + +func (t *tun) Activate() error { + var err error + + // TODO use syscalls instead of exec.Command + t.l.Debug("command: ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String()) + if err = exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String()).Run(); err != nil { + return fmt.Errorf("failed to run 'ifconfig': %s", err) + } + t.l.Debug("command: route", "-n", "add", "-net", t.cidr.String(), t.cidr.IP.String()) + if err = exec.Command("/sbin/route", "-n", "add", "-net", t.cidr.String(), t.cidr.IP.String()).Run(); err != nil { + return fmt.Errorf("failed to run 'route add': %s", err) + } + t.l.Debug("command: ifconfig", t.Device, "mtu", strconv.Itoa(t.MTU)) + if err = exec.Command("/sbin/ifconfig", t.Device, "mtu", strconv.Itoa(t.MTU)).Run(); err != nil { + return fmt.Errorf("failed to run 'ifconfig': %s", err) + } + // Unsafe path routes + for _, r := range t.Routes { + if r.Via == nil || !r.Install { + // We don't allow route MTUs so only install routes with a via + continue + } + + t.l.Debug("command: route", "-n", "add", "-net", r.Cidr.String(), t.cidr.IP.String()) + if err = exec.Command("/sbin/route", "-n", "add", "-net", r.Cidr.String(), t.cidr.IP.String()).Run(); err != nil { + return fmt.Errorf("failed to run 'route add' for unsafe_route %s: %s", r.Cidr.String(), err) + } + } + + return nil +} + +func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp { + r := t.routeTree.MostSpecificContains(ip) + if r != nil { + return r.(iputil.VpnIp) + } + + return 0 +} + +func (t *tun) Cidr() *net.IPNet { + return t.cidr +} + +func (t *tun) Name() string { + return t.Device +} + +func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) { + return nil, fmt.Errorf("TODO: multiqueue not implemented for netbsd") +} + +func (t *tun) deviceBytes() (o [16]byte) { + for i, c := range t.Device { + o[i] = byte(c) + } + return +} diff --git a/udp/udp_netbsd.go b/udp/udp_netbsd.go new file mode 100644 index 000000000..3c14face3 --- /dev/null +++ b/udp/udp_netbsd.go @@ -0,0 +1,46 @@ +//go:build !e2e_testing +// +build !e2e_testing + +package udp + +// FreeBSD support is primarily implemented in udp_generic, besides NewListenConfig + +import ( + "fmt" + "net" + "syscall" + + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) { + return NewGenericListener(l, ip, port, multi, batch) +} + +func NewListenConfig(multi bool) net.ListenConfig { + return net.ListenConfig{ + Control: func(network, address string, c syscall.RawConn) error { + if multi { + var controlErr error + err := c.Control(func(fd uintptr) { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { + controlErr = fmt.Errorf("SO_REUSEPORT failed: %v", err) + return + } + }) + if err != nil { + return err + } + if controlErr != nil { + return controlErr + } + } + return nil + }, + } +} + +func (u *GenericConn) Rebind() error { + return nil +} From 0bffa76b5eee14cf653471107f7d72a10c21739b Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Thu, 27 Jul 2023 14:27:35 -0500 Subject: [PATCH 21/52] Build for openbsd (#812) --- .github/workflows/release.yml | 4 +- Makefile | 15 ++- overlay/tun_netbsd.go | 22 ++-- overlay/tun_openbsd.go | 174 +++++++++++++++++++++++++++++ udp/{udp_freebsd.go => udp_bsd.go} | 3 +- 5 files changed, 205 insertions(+), 13 deletions(-) create mode 100644 overlay/tun_openbsd.go rename udp/{udp_freebsd.go => udp_bsd.go} (92%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a5590d3fb..f4efa389d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ name: Create release and upload binaries jobs: build-linux: - name: Build Linux All + name: Build Linux/BSD All runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -19,7 +19,7 @@ jobs: - name: Build run: | - make BUILD_NUMBER="${GITHUB_REF#refs/tags/v}" release-linux release-freebsd + make BUILD_NUMBER="${GITHUB_REF#refs/tags/v}" release-linux release-freebsd release-openbsd release-netbsd mkdir release mv build/*.tar.gz release diff --git a/Makefile b/Makefile index 7d9d49733..a70e4ae41 100644 --- a/Makefile +++ b/Makefile @@ -49,13 +49,20 @@ ALL_LINUX = linux-amd64 \ ALL_FREEBSD = freebsd-amd64 \ freebsd-arm64 +ALL_OPENBSD = openbsd-amd64 \ + openbsd-arm64 + +ALL_NETBSD = netbsd-amd64 \ + netbsd-arm64 + ALL = $(ALL_LINUX) \ $(ALL_FREEBSD) \ + $(ALL_OPENBSD) \ + $(ALL_NETBSD) \ darwin-amd64 \ darwin-arm64 \ windows-amd64 \ - windows-arm64 \ - netbsd-amd64 + windows-arm64 e2e: $(TEST_ENV) go test -tags=e2e_testing -count=1 $(TEST_FLAGS) ./e2e @@ -83,6 +90,10 @@ release-linux: $(ALL_LINUX:%=build/nebula-%.tar.gz) release-freebsd: $(ALL_FREEBSD:%=build/nebula-%.tar.gz) +release-openbsd: $(ALL_OPENBSD:%=build/nebula-%.tar.gz) + +release-netbsd: $(ALL_NETBSD:%=build/nebula-%.tar.gz) + release-boringcrypto: build/nebula-linux-$(shell go env GOARCH)-boringcrypto.tar.gz BUILD_ARGS = -trimpath diff --git a/overlay/tun_netbsd.go b/overlay/tun_netbsd.go index cd171496e..4d7f89751 100644 --- a/overlay/tun_netbsd.go +++ b/overlay/tun_netbsd.go @@ -99,16 +99,21 @@ func (t *tun) Activate() error { var err error // TODO use syscalls instead of exec.Command - t.l.Debug("command: ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String()) - if err = exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String()).Run(); err != nil { + cmd := exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String()) + t.l.Debug("command: ", cmd.String()) + if err = cmd.Run(); err != nil { return fmt.Errorf("failed to run 'ifconfig': %s", err) } - t.l.Debug("command: route", "-n", "add", "-net", t.cidr.String(), t.cidr.IP.String()) - if err = exec.Command("/sbin/route", "-n", "add", "-net", t.cidr.String(), t.cidr.IP.String()).Run(); err != nil { + + cmd = exec.Command("/sbin/route", "-n", "add", "-net", t.cidr.String(), t.cidr.IP.String()) + t.l.Debug("command: ", cmd.String()) + if err = cmd.Run(); err != nil { return fmt.Errorf("failed to run 'route add': %s", err) } - t.l.Debug("command: ifconfig", t.Device, "mtu", strconv.Itoa(t.MTU)) - if err = exec.Command("/sbin/ifconfig", t.Device, "mtu", strconv.Itoa(t.MTU)).Run(); err != nil { + + cmd = exec.Command("/sbin/ifconfig", t.Device, "mtu", strconv.Itoa(t.MTU)) + t.l.Debug("command: ", cmd.String()) + if err = cmd.Run(); err != nil { return fmt.Errorf("failed to run 'ifconfig': %s", err) } // Unsafe path routes @@ -118,8 +123,9 @@ func (t *tun) Activate() error { continue } - t.l.Debug("command: route", "-n", "add", "-net", r.Cidr.String(), t.cidr.IP.String()) - if err = exec.Command("/sbin/route", "-n", "add", "-net", r.Cidr.String(), t.cidr.IP.String()).Run(); err != nil { + cmd = exec.Command("/sbin/route", "-n", "add", "-net", r.Cidr.String(), t.cidr.IP.String()) + t.l.Debug("command: ", cmd.String()) + if err = cmd.Run(); err != nil { return fmt.Errorf("failed to run 'route add' for unsafe_route %s: %s", r.Cidr.String(), err) } } diff --git a/overlay/tun_openbsd.go b/overlay/tun_openbsd.go new file mode 100644 index 000000000..709fb427f --- /dev/null +++ b/overlay/tun_openbsd.go @@ -0,0 +1,174 @@ +//go:build !e2e_testing +// +build !e2e_testing + +package overlay + +import ( + "fmt" + "io" + "net" + "os" + "os/exec" + "regexp" + "strconv" + "syscall" + + "github.com/sirupsen/logrus" + "github.com/slackhq/nebula/cidr" + "github.com/slackhq/nebula/iputil" +) + +type tun struct { + Device string + cidr *net.IPNet + MTU int + Routes []Route + routeTree *cidr.Tree4 + l *logrus.Logger + + io.ReadWriteCloser + + // cache out buffer since we need to prepend 4 bytes for tun metadata + out []byte +} + +func (t *tun) Close() error { + if t.ReadWriteCloser != nil { + return t.ReadWriteCloser.Close() + } + + return nil +} + +func newTunFromFd(_ *logrus.Logger, _ int, _ *net.IPNet, _ int, _ []Route, _ int, _ bool) (*tun, error) { + return nil, fmt.Errorf("newTunFromFd not supported in OpenBSD") +} + +var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`) + +func newTun(l *logrus.Logger, deviceName string, cidr *net.IPNet, defaultMTU int, routes []Route, _ int, _ bool, _ bool) (*tun, error) { + if deviceName == "" { + return nil, fmt.Errorf("a device name in the format of tunN must be specified") + } + + if !deviceNameRE.MatchString(deviceName) { + return nil, fmt.Errorf("a device name in the format of tunN must be specified") + } + + file, err := os.OpenFile("/dev/"+deviceName, os.O_RDWR, 0) + if err != nil { + return nil, err + } + + routeTree, err := makeRouteTree(l, routes, false) + if err != nil { + return nil, err + } + + return &tun{ + ReadWriteCloser: file, + Device: deviceName, + cidr: cidr, + MTU: defaultMTU, + Routes: routes, + routeTree: routeTree, + l: l, + }, nil +} + +func (t *tun) Activate() error { + var err error + // TODO use syscalls instead of exec.Command + cmd := exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String()) + t.l.Debug("command: ", cmd.String()) + if err = cmd.Run(); err != nil { + return fmt.Errorf("failed to run 'ifconfig': %s", err) + } + + cmd = exec.Command("/sbin/ifconfig", t.Device, "mtu", strconv.Itoa(t.MTU)) + t.l.Debug("command: ", cmd.String()) + if err = cmd.Run(); err != nil { + return fmt.Errorf("failed to run 'ifconfig': %s", err) + } + + cmd = exec.Command("/sbin/route", "-n", "add", "-inet", t.cidr.String(), t.cidr.IP.String()) + t.l.Debug("command: ", cmd.String()) + if err = cmd.Run(); err != nil { + return fmt.Errorf("failed to run 'route add': %s", err) + } + + // Unsafe path routes + for _, r := range t.Routes { + if r.Via == nil || !r.Install { + // We don't allow route MTUs so only install routes with a via + continue + } + + cmd = exec.Command("/sbin/route", "-n", "add", "-inet", r.Cidr.String(), t.cidr.IP.String()) + t.l.Debug("command: ", cmd.String()) + if err = cmd.Run(); err != nil { + return fmt.Errorf("failed to run 'route add' for unsafe_route %s: %s", r.Cidr.String(), err) + } + } + + return nil +} + +func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp { + r := t.routeTree.MostSpecificContains(ip) + if r != nil { + return r.(iputil.VpnIp) + } + + return 0 +} + +func (t *tun) Cidr() *net.IPNet { + return t.cidr +} + +func (t *tun) Name() string { + return t.Device +} + +func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) { + return nil, fmt.Errorf("TODO: multiqueue not implemented for freebsd") +} + +func (t *tun) Read(to []byte) (int, error) { + buf := make([]byte, len(to)+4) + + n, err := t.ReadWriteCloser.Read(buf) + + copy(to, buf[4:]) + return n - 4, err +} + +// Write is only valid for single threaded use +func (t *tun) Write(from []byte) (int, error) { + buf := t.out + if cap(buf) < len(from)+4 { + buf = make([]byte, len(from)+4) + t.out = buf + } + buf = buf[:len(from)+4] + + if len(from) == 0 { + return 0, syscall.EIO + } + + // Determine the IP Family for the NULL L2 Header + ipVer := from[0] >> 4 + if ipVer == 4 { + buf[3] = syscall.AF_INET + } else if ipVer == 6 { + buf[3] = syscall.AF_INET6 + } else { + return 0, fmt.Errorf("unable to determine IP version from packet") + } + + copy(buf[4:], from) + + n, err := t.ReadWriteCloser.Write(buf) + return n - 4, err +} diff --git a/udp/udp_freebsd.go b/udp/udp_bsd.go similarity index 92% rename from udp/udp_freebsd.go rename to udp/udp_bsd.go index 3c14face3..785aa6a74 100644 --- a/udp/udp_freebsd.go +++ b/udp/udp_bsd.go @@ -1,4 +1,5 @@ -//go:build !e2e_testing +//go:build (openbsd || freebsd) && !e2e_testing +// +build openbsd freebsd // +build !e2e_testing package udp From 959b015b3b90861bc94d3f77964fd72130a872cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jul 2023 14:36:36 -0500 Subject: [PATCH 22/52] Bump github.com/sirupsen/logrus from 1.9.0 to 1.9.3 (#933) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fd23700bb..bddb343b5 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( 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 - github.com/sirupsen/logrus v1.9.0 + github.com/sirupsen/logrus v1.9.3 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index 6acdf2360..5bea11c20 100644 --- a/go.sum +++ b/go.sum @@ -122,8 +122,8 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= From 14d01067165145c91f5e99b98b08381bfa0ca711 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Thu, 27 Jul 2023 14:38:10 -0500 Subject: [PATCH 23/52] Send the lh update worker into its own routine instead of taking over the reload routine (#935) --- control.go | 20 ++++++++++++-------- lighthouse.go | 43 +++++++++++++++++++++---------------------- lighthouse_test.go | 29 +++++++++++++++++++++++++++++ main.go | 13 ++++++++++--- 4 files changed, 72 insertions(+), 33 deletions(-) diff --git a/control.go b/control.go index 07b42f2ea..4af115c08 100644 --- a/control.go +++ b/control.go @@ -27,12 +27,13 @@ type controlHostLister interface { } type Control struct { - f *Interface - l *logrus.Logger - cancel context.CancelFunc - sshStart func() - statsStart func() - dnsStart func() + f *Interface + l *logrus.Logger + cancel context.CancelFunc + sshStart func() + statsStart func() + dnsStart func() + lighthouseStart func() } type ControlHostInfo struct { @@ -63,12 +64,15 @@ func (c *Control) Start() { if c.dnsStart != nil { go c.dnsStart() } + if c.lighthouseStart != nil { + c.lighthouseStart() + } // Start reading packets. c.f.run() } -// Stop signals nebula to shutdown, returns after the shutdown is complete +// Stop signals nebula to shutdown and close all tunnels, returns after the shutdown is complete func (c *Control) Stop() { // Stop the handshakeManager (and other services), to prevent new tunnels from // being created while we're shutting them all down. @@ -98,7 +102,7 @@ func (c *Control) RebindUDPServer() { _ = c.f.outside.Rebind() // Trigger a lighthouse update, useful for mobile clients that should have an update interval of 0 - c.f.lightHouse.SendUpdate(c.f) + c.f.lightHouse.SendUpdate() // Let the main interface know that we rebound so that underlying tunnels know to trigger punches from their remotes c.f.rebindCount++ diff --git a/lighthouse.go b/lighthouse.go index f281f9b70..6c46663c9 100644 --- a/lighthouse.go +++ b/lighthouse.go @@ -64,11 +64,10 @@ type LightHouse struct { staticList atomic.Pointer[map[iputil.VpnIp]struct{}] lighthouses atomic.Pointer[map[iputil.VpnIp]struct{}] - interval atomic.Int64 - updateCancel context.CancelFunc - updateParentCtx context.Context - updateUdp EncWriter - nebulaPort uint32 // 32 bits because protobuf does not have a uint16 + interval atomic.Int64 + updateCancel context.CancelFunc + ifce EncWriter + nebulaPort uint32 // 32 bits because protobuf does not have a uint16 advertiseAddrs atomic.Pointer[[]netIpAndPort] @@ -217,7 +216,7 @@ func (lh *LightHouse) reload(c *config.C, initial bool) error { lh.updateCancel() } - lh.LhUpdateWorker(lh.updateParentCtx, lh.updateUdp) + lh.StartUpdateWorker() } } @@ -754,33 +753,33 @@ func NewUDPAddrFromLH6(ipp *Ip6AndPort) *udp.Addr { return udp.NewAddr(lhIp6ToIp(ipp), uint16(ipp.Port)) } -func (lh *LightHouse) LhUpdateWorker(ctx context.Context, f EncWriter) { - lh.updateParentCtx = ctx - lh.updateUdp = f - +func (lh *LightHouse) StartUpdateWorker() { interval := lh.GetUpdateInterval() if lh.amLighthouse || interval == 0 { return } clockSource := time.NewTicker(time.Second * time.Duration(interval)) - updateCtx, cancel := context.WithCancel(ctx) + updateCtx, cancel := context.WithCancel(lh.ctx) lh.updateCancel = cancel - defer clockSource.Stop() - for { - lh.SendUpdate(f) + go func() { + defer clockSource.Stop() - select { - case <-updateCtx.Done(): - return - case <-clockSource.C: - continue + for { + lh.SendUpdate() + + select { + case <-updateCtx.Done(): + return + case <-clockSource.C: + continue + } } - } + }() } -func (lh *LightHouse) SendUpdate(f EncWriter) { +func (lh *LightHouse) SendUpdate() { var v4 []*Ip4AndPort var v6 []*Ip6AndPort @@ -833,7 +832,7 @@ func (lh *LightHouse) SendUpdate(f EncWriter) { } for vpnIp := range lighthouses { - f.SendMessageToVpnIp(header.LightHouse, 0, vpnIp, mm, nb, out) + lh.ifce.SendMessageToVpnIp(header.LightHouse, 0, vpnIp, mm, nb, out) } } diff --git a/lighthouse_test.go b/lighthouse_test.go index 73632ac79..66427e339 100644 --- a/lighthouse_test.go +++ b/lighthouse_test.go @@ -66,6 +66,35 @@ func Test_lhStaticMapping(t *testing.T) { assert.EqualError(t, err, "lighthouse 10.128.0.3 does not have a static_host_map entry") } +func TestReloadLighthouseInterval(t *testing.T) { + l := test.NewLogger() + _, myVpnNet, _ := net.ParseCIDR("10.128.0.1/16") + lh1 := "10.128.0.2" + + c := config.NewC(l) + c.Settings["lighthouse"] = map[interface{}]interface{}{ + "hosts": []interface{}{lh1}, + "interval": "1s", + } + + c.Settings["static_host_map"] = map[interface{}]interface{}{lh1: []interface{}{"1.1.1.1:4242"}} + lh, err := NewLightHouseFromConfig(context.Background(), l, c, myVpnNet, nil, nil) + assert.NoError(t, err) + lh.ifce = &mockEncWriter{} + + // The first one routine is kicked off by main.go currently, lets make sure that one dies + c.ReloadConfigString("lighthouse:\n interval: 5") + assert.Equal(t, int64(5), lh.interval.Load()) + + // Subsequent calls are killed off by the LightHouse.Reload function + c.ReloadConfigString("lighthouse:\n interval: 10") + assert.Equal(t, int64(10), lh.interval.Load()) + + // If this completes then nothing is stealing our reload routine + c.ReloadConfigString("lighthouse:\n interval: 11") + assert.Equal(t, int64(11), lh.interval.Load()) +} + func BenchmarkLighthouseHandleRequest(b *testing.B) { l := test.NewLogger() _, myVpnNet, _ := net.ParseCIDR("10.128.0.1/0") diff --git a/main.go b/main.go index 5845b7603..ab2bd51d1 100644 --- a/main.go +++ b/main.go @@ -315,13 +315,12 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg // TODO: Better way to attach these, probably want a new interface in InterfaceConfig // I don't want to make this initial commit too far-reaching though ifce.writers = udpConns + lightHouse.ifce = ifce ifce.RegisterConfigChangeCallbacks(c) - ifce.reloadSendRecvError(c) go handshakeManager.Run(ctx, ifce) - go lightHouse.LhUpdateWorker(ctx, ifce) } // TODO - stats third-party modules start uncancellable goroutines. Update those libs to accept @@ -348,5 +347,13 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg dnsStart = dnsMain(l, hostMap, c) } - return &Control{ifce, l, cancel, sshStart, statsStart, dnsStart}, nil + return &Control{ + ifce, + l, + cancel, + sshStart, + statsStart, + dnsStart, + lightHouse.StartUpdateWorker, + }, nil } From 0c003b64f1c1828e173d57a74a5d380dd9cece02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jul 2023 14:38:36 -0500 Subject: [PATCH 24/52] Bump golang.org/x/term from 0.8.0 to 0.10.0 (#928) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bddb343b5..9a96fa18a 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/net v0.9.0 golang.org/x/sys v0.10.0 - golang.org/x/term v0.8.0 + golang.org/x/term v0.10.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 diff --git a/go.sum b/go.sum index 5bea11c20..b4737cc88 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= 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= From 0d715effbc11a5e1d6749b9db3eebaf4197b1d84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jul 2023 15:31:36 -0500 Subject: [PATCH 25/52] Bump Apple-Actions/import-codesign-certs from 1 to 2 (#923) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f4efa389d..809670c83 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,7 +75,7 @@ jobs: - name: Import certificates if: env.HAS_SIGNING_CREDS == 'true' - uses: Apple-Actions/import-codesign-certs@v1 + uses: Apple-Actions/import-codesign-certs@v2 with: p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }} From fce93ccb5478895e938b7ea2494ff988d2a11b67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jul 2023 15:42:33 -0500 Subject: [PATCH 26/52] Bump google.golang.org/protobuf from 1.30.0 to 1.31.0 (#930) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9a96fa18a..ebfe12220 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( 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 - google.golang.org/protobuf v1.30.0 + google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index b4737cc88..36d6e5978 100644 --- a/go.sum +++ b/go.sum @@ -228,8 +228,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 38e56a48580eda9f5142da3e9a63869d7777d7e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jul 2023 15:43:16 -0500 Subject: [PATCH 27/52] Bump golang.org/x/net from 0.9.0 to 0.12.0 (#931) --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ebfe12220..fb55bbc39 100644 --- a/go.mod +++ b/go.mod @@ -20,9 +20,9 @@ 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.8.0 + golang.org/x/crypto v0.11.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 - golang.org/x/net v0.9.0 + golang.org/x/net v0.12.0 golang.org/x/sys v0.10.0 golang.org/x/term v0.10.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 diff --git a/go.sum b/go.sum index 36d6e5978..abfed7ad6 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,8 @@ 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.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= 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= @@ -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.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= From ed00f5d530ea09628353df31699dc85536ff9fa4 Mon Sep 17 00:00:00 2001 From: Caleb Jasik Date: Mon, 31 Jul 2023 15:59:20 -0500 Subject: [PATCH 28/52] Remove unused config code (last edited 4yrs ago) (#938) --- main.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/main.go b/main.go index ab2bd51d1..c50a48c99 100644 --- a/main.go +++ b/main.go @@ -220,11 +220,6 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg WithField("preferredRanges", hostMap.preferredRanges). Info("Main HostMap created") - /* - config.SetDefault("promoter.interval", 10) - go hostMap.Promoter(config.GetInt("promoter.interval")) - */ - punchy := NewPunchyFromConfig(l, c) lightHouse, err := NewLightHouseFromConfig(ctx, l, c, tunCidr, udpConns[0], punchy) switch { @@ -255,10 +250,6 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg handshakeManager := NewHandshakeManager(l, tunCidr, preferredRanges, hostMap, lightHouse, udpConns[0], handshakeConfig) lightHouse.handshakeTrigger = handshakeManager.trigger - //TODO: These will be reused for psk - //handshakeMACKey := config.GetString("handshake_mac.key", "") - //handshakeAcceptedMACKeys := config.GetStringSlice("handshake_mac.accepted_keys", []string{}) - serveDns := false if c.GetBool("lighthouse.serve_dns", false) { if c.GetBool("lighthouse.am_lighthouse", false) { From 3d0da7c859eb12e79e99953da0bc9b5af3ebf28c Mon Sep 17 00:00:00 2001 From: Wade Simmons Date: Wed, 2 Aug 2023 14:00:20 -0400 Subject: [PATCH 29/52] update mergo to 1.0.0 (#941) The mergo package has moved to a vanity URL. This causes fun issues with dependabot. Update to the new release: - https://github.com/darccio/mergo/releases/tag/v1.0.0 - https://github.com/darccio/mergo/compare/v0.3.15...v1.0.0 --- config/config.go | 2 +- config/config_test.go | 2 +- e2e/helpers_test.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/config.go b/config/config.go index 966e905bb..d28f85314 100644 --- a/config/config.go +++ b/config/config.go @@ -15,7 +15,7 @@ import ( "syscall" "time" - "github.com/imdario/mergo" + "dario.cat/mergo" "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" ) diff --git a/config/config_test.go b/config/config_test.go index 52bf2e479..1001f8d06 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/imdario/mergo" + "dario.cat/mergo" "github.com/slackhq/nebula/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/e2e/helpers_test.go b/e2e/helpers_test.go index f2e312860..8440a72b7 100644 --- a/e2e/helpers_test.go +++ b/e2e/helpers_test.go @@ -12,9 +12,9 @@ import ( "testing" "time" + "dario.cat/mergo" "github.com/google/gopacket" "github.com/google/gopacket/layers" - "github.com/imdario/mergo" "github.com/sirupsen/logrus" "github.com/slackhq/nebula" "github.com/slackhq/nebula/cert" diff --git a/go.mod b/go.mod index fb55bbc39..4a34488ae 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module github.com/slackhq/nebula go 1.20 require ( + dario.cat/mergo v1.0.0 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be github.com/armon/go-radix v1.0.0 github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432 github.com/flynn/noise v1.0.0 github.com/gogo/protobuf v1.3.2 github.com/google/gopacket v1.1.19 - github.com/imdario/mergo v0.3.15 github.com/kardianos/service v1.2.2 github.com/miekg/dns v1.1.55 github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f diff --git a/go.sum b/go.sum index abfed7ad6..ce7c795a3 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -54,8 +56,6 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= -github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= From 83b6dc7b16e5c61c53da4eed7eb5a7ba3c71e88c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:28:32 -0400 Subject: [PATCH 30/52] Bump golang.org/x/net from 0.12.0 to 0.13.0 (#943) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.12.0 to 0.13.0. - [Commits](https://github.com/golang/net/compare/v0.12.0...v0.13.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4a34488ae..40294cfa1 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.11.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 - golang.org/x/net v0.12.0 + golang.org/x/net v0.13.0 golang.org/x/sys v0.10.0 golang.org/x/term v0.10.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 diff --git a/go.sum b/go.sum index ce7c795a3..1783fe9cb 100644 --- a/go.sum +++ b/go.sum @@ -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.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= From 7364d99e34c6d4a435f347803e1d7307ff40dd73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:07:30 -0500 Subject: [PATCH 31/52] Bump golang.org/x/term from 0.10.0 to 0.11.0 (#946) Bumps [golang.org/x/term](https://github.com/golang/term) from 0.10.0 to 0.11.0. - [Commits](https://github.com/golang/term/compare/v0.10.0...v0.11.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 40294cfa1..9bd104427 100644 --- a/go.mod +++ b/go.mod @@ -23,8 +23,8 @@ require ( golang.org/x/crypto v0.11.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/net v0.13.0 - golang.org/x/sys v0.10.0 - golang.org/x/term v0.10.0 + golang.org/x/sys v0.11.0 + golang.org/x/term v0.11.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 diff --git a/go.sum b/go.sum index 1783fe9cb..aabd8c2c6 100644 --- a/go.sum +++ b/go.sum @@ -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.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +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/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= From 546eb3bfbc7978951e17222500e3b759c5222a10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:28:06 -0500 Subject: [PATCH 32/52] Bump golang.org/x/crypto from 0.11.0 to 0.12.0 (#949) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.11.0 to 0.12.0. - [Commits](https://github.com/golang/crypto/compare/v0.11.0...v0.12.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9bd104427..f7cbe30d1 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ 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.11.0 + golang.org/x/crypto v0.12.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/net v0.13.0 golang.org/x/sys v0.11.0 diff --git a/go.sum b/go.sum index aabd8c2c6..2d26de52a 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,8 @@ 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.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +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/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= From 7ecafbe61d99e1f415ae62dc364b1edb39882472 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 10:04:46 -0500 Subject: [PATCH 33/52] Bump golang.org/x/net from 0.13.0 to 0.14.0 (#947) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.13.0 to 0.14.0. - [Commits](https://github.com/golang/net/compare/v0.13.0...v0.14.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f7cbe30d1..5dcdcade4 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.12.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 - golang.org/x/net v0.13.0 + golang.org/x/net v0.14.0 golang.org/x/sys v0.11.0 golang.org/x/term v0.11.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 diff --git a/go.sum b/go.sum index 2d26de52a..f0fd4b851 100644 --- a/go.sum +++ b/go.sum @@ -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.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= -golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 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= From 5671c6607c12c5ad3cb4690c41e6e0cc9239af8e Mon Sep 17 00:00:00 2001 From: Wade Simmons Date: Tue, 8 Aug 2023 13:15:42 -0400 Subject: [PATCH 34/52] dependabot: group together common deps (#950) Group together deps that are often updated together. - https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 603f65329..abf74a0fa 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,14 @@ updates: directory: "/" schedule: interval: "weekly" + groups: + golang-x-dependencies: + patterns: + - "golang.org/x/*" + zx2c4-dependencies: + patterns: + - "golang.zx2c4.com/*" + protobuf-dependencies: + patterns: + - "github.com/golang/protobuf" + - "google.golang.org/protobuf" From 223cc6e660e9dfc2fc9a6f9af32974e93b79c584 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Tue, 8 Aug 2023 13:26:41 -0500 Subject: [PATCH 35/52] Limit how often a busy tunnel can requery the lighthouse (#940) Co-authored-by: Wade Simmons --- config/config.go | 10 ++++++++++ hostmap.go | 19 +++++++++++++++---- interface.go | 33 +++++++++++++++++++++++++++++++++ main.go | 4 ++++ 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index d28f85314..bc3818dd4 100644 --- a/config/config.go +++ b/config/config.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io/ioutil" + "math" "os" "os/signal" "path/filepath" @@ -236,6 +237,15 @@ func (c *C) GetInt(k string, d int) int { return v } +// GetUint32 will get the uint32 for k or return the default d if not found or invalid +func (c *C) GetUint32(k string, d uint32) uint32 { + r := c.GetInt(k, int(d)) + if uint64(r) > uint64(math.MaxUint32) { + return d + } + return uint32(r) +} + // GetBool will get the bool for k or return the default d if not found or invalid func (c *C) GetBool(k string, d bool) bool { r := strings.ToLower(c.GetString(k, fmt.Sprintf("%v", d))) diff --git a/hostmap.go b/hostmap.go index c7f607c07..829c7c0bb 100644 --- a/hostmap.go +++ b/hostmap.go @@ -17,8 +17,9 @@ import ( ) // const ProbeLen = 100 -const PromoteEvery = 1000 -const ReQueryEvery = 5000 +const defaultPromoteEvery = 1000 // Count of packets sent before we try moving a tunnel to a preferred underlay ip address +const defaultReQueryEvery = 5000 // Count of packets sent before re-querying a hostinfo to the lighthouse +const defaultReQueryWait = time.Minute // Minimum amount of seconds to wait before re-querying a hostinfo the lighthouse. Evaluated every ReQueryEvery const MaxRemotes = 10 // MaxHostInfosPerVpnIp is the max number of hostinfos we will track for a given vpn ip @@ -215,6 +216,10 @@ type HostInfo struct { remoteCidr *cidr.Tree4 relayState RelayState + // nextLHQuery is the earliest we can ask the lighthouse for new information. + // This is used to limit lighthouse re-queries in chatty clients + nextLHQuery atomic.Int64 + // lastRebindCount is the other side of Interface.rebindCount, if these values don't match then we need to ask LH // for a punch from the remote end of this tunnel. The goal being to prime their conntrack for our traffic just like // with a handshake @@ -535,7 +540,7 @@ func (hm *HostMap) ForEachIndex(f controlEach) { // NOTE: It is an error to call this if you are a lighthouse since they should not roam clients! func (i *HostInfo) TryPromoteBest(preferredRanges []*net.IPNet, ifce *Interface) { c := i.promoteCounter.Add(1) - if c%PromoteEvery == 0 { + if c%ifce.tryPromoteEvery.Load() == 0 { // The lock here is currently protecting i.remote access i.RLock() remote := i.remote @@ -563,7 +568,13 @@ func (i *HostInfo) TryPromoteBest(preferredRanges []*net.IPNet, ifce *Interface) } // Re query our lighthouses for new remotes occasionally - if c%ReQueryEvery == 0 && ifce.lightHouse != nil { + if c%ifce.reQueryEvery.Load() == 0 && ifce.lightHouse != nil { + now := time.Now().UnixNano() + if now < i.nextLHQuery.Load() { + return + } + + i.nextLHQuery.Store(now + ifce.reQueryWait.Load()) ifce.lightHouse.QueryServer(i.vpnIp, ifce) } } diff --git a/interface.go b/interface.go index b5d43d295..771aed0e6 100644 --- a/interface.go +++ b/interface.go @@ -46,6 +46,10 @@ type InterfaceConfig struct { relayManager *relayManager punchy *Punchy + tryPromoteEvery uint32 + reQueryEvery uint32 + reQueryWait time.Duration + ConntrackCacheTimeout time.Duration l *logrus.Logger } @@ -72,6 +76,10 @@ type Interface struct { closed atomic.Bool relayManager *relayManager + tryPromoteEvery atomic.Uint32 + reQueryEvery atomic.Uint32 + reQueryWait atomic.Int64 + sendRecvErrorConfig sendRecvErrorConfig // rebindCount is used to decide if an active tunnel should trigger a punch notification through a lighthouse @@ -186,6 +194,10 @@ func NewInterface(ctx context.Context, c *InterfaceConfig) (*Interface, error) { l: c.l, } + ifce.tryPromoteEvery.Store(c.tryPromoteEvery) + ifce.reQueryEvery.Store(c.reQueryEvery) + ifce.reQueryWait.Store(int64(c.reQueryWait)) + ifce.certState.Store(c.certState) ifce.connectionManager = newConnectionManager(ctx, c.l, ifce, c.checkInterval, c.pendingDeletionInterval, c.punchy) @@ -287,6 +299,7 @@ func (f *Interface) RegisterConfigChangeCallbacks(c *config.C) { c.RegisterReloadCallback(f.reloadCertKey) c.RegisterReloadCallback(f.reloadFirewall) c.RegisterReloadCallback(f.reloadSendRecvError) + c.RegisterReloadCallback(f.reloadMisc) for _, udpConn := range f.writers { c.RegisterReloadCallback(udpConn.ReloadConfig) } @@ -389,6 +402,26 @@ func (f *Interface) reloadSendRecvError(c *config.C) { } } +func (f *Interface) reloadMisc(c *config.C) { + if c.HasChanged("counters.try_promote") { + n := c.GetUint32("counters.try_promote", defaultPromoteEvery) + f.tryPromoteEvery.Store(n) + f.l.Info("counters.try_promote has changed") + } + + if c.HasChanged("counters.requery_every_packets") { + n := c.GetUint32("counters.requery_every_packets", defaultReQueryEvery) + f.reQueryEvery.Store(n) + f.l.Info("counters.requery_every_packets has changed") + } + + if c.HasChanged("timers.requery_wait_duration") { + n := c.GetDuration("timers.requery_wait_duration", defaultReQueryWait) + f.reQueryWait.Store(int64(n)) + f.l.Info("timers.requery_wait_duration has changed") + } +} + func (f *Interface) emitStats(ctx context.Context, i time.Duration) { ticker := time.NewTicker(i) defer ticker.Stop() diff --git a/main.go b/main.go index c50a48c99..d050db226 100644 --- a/main.go +++ b/main.go @@ -261,6 +261,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg checkInterval := c.GetInt("timers.connection_alive_interval", 5) pendingDeletionInterval := c.GetInt("timers.pending_deletion_interval", 10) + ifConfig := &InterfaceConfig{ HostMap: hostMap, Inside: tun, @@ -273,6 +274,9 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg lightHouse: lightHouse, checkInterval: time.Second * time.Duration(checkInterval), pendingDeletionInterval: time.Second * time.Duration(pendingDeletionInterval), + tryPromoteEvery: c.GetUint32("counters.try_promote", defaultPromoteEvery), + reQueryEvery: c.GetUint32("counters.requery_every_packets", defaultReQueryEvery), + reQueryWait: c.GetDuration("timers.requery_wait_duration", defaultReQueryWait), DropLocalBroadcast: c.GetBool("tun.drop_local_broadcast", false), DropMulticast: c.GetBool("tun.drop_multicast", false), routines: routines, From 5a131b29756bef96abd50af467adf53274ebc707 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Mon, 14 Aug 2023 21:32:40 -0500 Subject: [PATCH 36/52] Combine ca, cert, and key handling (#952) --- cert.go | 163 ------------------------ cmd/nebula-service/main.go | 9 +- cmd/nebula/main.go | 9 +- connection_manager.go | 10 +- connection_manager_test.go | 35 +++--- connection_state.go | 8 +- control_tester.go | 2 +- handshake_ix.go | 8 +- inside.go | 4 +- interface.go | 57 ++------- lighthouse.go | 2 +- main.go | 46 +++---- outside.go | 2 +- pki.go | 248 +++++++++++++++++++++++++++++++++++++ ssh.go | 2 +- util/error.go | 28 ++++- util/error_test.go | 42 +++++++ 17 files changed, 381 insertions(+), 294 deletions(-) delete mode 100644 cert.go create mode 100644 pki.go diff --git a/cert.go b/cert.go deleted file mode 100644 index bbd29c6d4..000000000 --- a/cert.go +++ /dev/null @@ -1,163 +0,0 @@ -package nebula - -import ( - "errors" - "fmt" - "io/ioutil" - "strings" - "time" - - "github.com/sirupsen/logrus" - "github.com/slackhq/nebula/cert" - "github.com/slackhq/nebula/config" -) - -type CertState struct { - certificate *cert.NebulaCertificate - rawCertificate []byte - rawCertificateNoKey []byte - publicKey []byte - privateKey []byte -} - -func NewCertState(certificate *cert.NebulaCertificate, privateKey []byte) (*CertState, error) { - // Marshal the certificate to ensure it is valid - rawCertificate, err := certificate.Marshal() - if err != nil { - return nil, fmt.Errorf("invalid nebula certificate on interface: %s", err) - } - - publicKey := certificate.Details.PublicKey - cs := &CertState{ - rawCertificate: rawCertificate, - certificate: certificate, // PublicKey has been set to nil above - privateKey: privateKey, - publicKey: publicKey, - } - - cs.certificate.Details.PublicKey = nil - rawCertNoKey, err := cs.certificate.Marshal() - if err != nil { - return nil, fmt.Errorf("error marshalling certificate no key: %s", err) - } - cs.rawCertificateNoKey = rawCertNoKey - // put public key back - cs.certificate.Details.PublicKey = cs.publicKey - return cs, nil -} - -func NewCertStateFromConfig(c *config.C) (*CertState, error) { - var pemPrivateKey []byte - var err error - - privPathOrPEM := c.GetString("pki.key", "") - - if privPathOrPEM == "" { - return nil, errors.New("no pki.key path or PEM data provided") - } - - if strings.Contains(privPathOrPEM, "-----BEGIN") { - pemPrivateKey = []byte(privPathOrPEM) - privPathOrPEM = "" - } else { - pemPrivateKey, err = ioutil.ReadFile(privPathOrPEM) - if err != nil { - return nil, fmt.Errorf("unable to read pki.key file %s: %s", privPathOrPEM, err) - } - } - - rawKey, _, curve, err := cert.UnmarshalPrivateKey(pemPrivateKey) - if err != nil { - return nil, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err) - } - - var rawCert []byte - - pubPathOrPEM := c.GetString("pki.cert", "") - - if pubPathOrPEM == "" { - return nil, errors.New("no pki.cert path or PEM data provided") - } - - if strings.Contains(pubPathOrPEM, "-----BEGIN") { - rawCert = []byte(pubPathOrPEM) - pubPathOrPEM = "" - } else { - rawCert, err = ioutil.ReadFile(pubPathOrPEM) - if err != nil { - return nil, fmt.Errorf("unable to read pki.cert file %s: %s", pubPathOrPEM, err) - } - } - - nebulaCert, _, err := cert.UnmarshalNebulaCertificateFromPEM(rawCert) - if err != nil { - return nil, fmt.Errorf("error while unmarshaling pki.cert %s: %s", pubPathOrPEM, err) - } - - if nebulaCert.Expired(time.Now()) { - return nil, fmt.Errorf("nebula certificate for this host is expired") - } - - if len(nebulaCert.Details.Ips) == 0 { - return nil, fmt.Errorf("no IPs encoded in certificate") - } - - if err = nebulaCert.VerifyPrivateKey(curve, rawKey); err != nil { - return nil, fmt.Errorf("private key is not a pair with public key in nebula cert") - } - - return NewCertState(nebulaCert, rawKey) -} - -func loadCAFromConfig(l *logrus.Logger, c *config.C) (*cert.NebulaCAPool, error) { - var rawCA []byte - var err error - - caPathOrPEM := c.GetString("pki.ca", "") - if caPathOrPEM == "" { - return nil, errors.New("no pki.ca path or PEM data provided") - } - - if strings.Contains(caPathOrPEM, "-----BEGIN") { - rawCA = []byte(caPathOrPEM) - - } else { - rawCA, err = ioutil.ReadFile(caPathOrPEM) - if err != nil { - return nil, fmt.Errorf("unable to read pki.ca file %s: %s", caPathOrPEM, err) - } - } - - CAs, err := cert.NewCAPoolFromBytes(rawCA) - if errors.Is(err, cert.ErrExpired) { - var expired int - for _, cert := range CAs.CAs { - if cert.Expired(time.Now()) { - expired++ - l.WithField("cert", cert).Warn("expired certificate present in CA pool") - } - } - - if expired >= len(CAs.CAs) { - return nil, errors.New("no valid CA certificates present") - } - - } else if err != nil { - return nil, fmt.Errorf("error while adding CA certificate to CA trust store: %s", err) - } - - for _, fp := range c.GetStringSlice("pki.blocklist", []string{}) { - l.WithField("fingerprint", fp).Info("Blocklisting cert") - CAs.BlocklistFingerprint(fp) - } - - // Support deprecated config for at least one minor release to allow for migrations - //TODO: remove in 2022 or later - for _, fp := range c.GetStringSlice("pki.blacklist", []string{}) { - l.WithField("fingerprint", fp).Info("Blocklisting cert") - l.Warn("pki.blacklist is deprecated and will not be supported in a future release. Please migrate your config to use pki.blocklist") - CAs.BlocklistFingerprint(fp) - } - - return CAs, nil -} diff --git a/cmd/nebula-service/main.go b/cmd/nebula-service/main.go index c1de26722..8d0eaa1db 100644 --- a/cmd/nebula-service/main.go +++ b/cmd/nebula-service/main.go @@ -59,13 +59,8 @@ func main() { } ctrl, err := nebula.Main(c, *configTest, Build, l, nil) - - switch v := err.(type) { - case util.ContextualError: - v.Log(l) - os.Exit(1) - case error: - l.WithError(err).Error("Failed to start") + if err != nil { + util.LogWithContextIfNeeded("Failed to start", err, l) os.Exit(1) } diff --git a/cmd/nebula/main.go b/cmd/nebula/main.go index 9461035b1..5cf0a028a 100644 --- a/cmd/nebula/main.go +++ b/cmd/nebula/main.go @@ -53,13 +53,8 @@ func main() { } ctrl, err := nebula.Main(c, *configTest, Build, l, nil) - - switch v := err.(type) { - case util.ContextualError: - v.Log(l) - os.Exit(1) - case error: - l.WithError(err).Error("Failed to start") + if err != nil { + util.LogWithContextIfNeeded("Failed to start", err, l) os.Exit(1) } diff --git a/connection_manager.go b/connection_manager.go index 528cf1b66..62a8dd234 100644 --- a/connection_manager.go +++ b/connection_manager.go @@ -405,8 +405,8 @@ func (n *connectionManager) shouldSwapPrimary(current, primary *HostInfo) bool { return false } - certState := n.intf.certState.Load() - return bytes.Equal(current.ConnectionState.certState.certificate.Signature, certState.certificate.Signature) + certState := n.intf.pki.GetCertState() + return bytes.Equal(current.ConnectionState.certState.Certificate.Signature, certState.Certificate.Signature) } func (n *connectionManager) swapPrimary(current, primary *HostInfo) { @@ -427,7 +427,7 @@ func (n *connectionManager) isInvalidCertificate(now time.Time, hostinfo *HostIn return false } - valid, err := remoteCert.VerifyWithCache(now, n.intf.caPool) + valid, err := remoteCert.VerifyWithCache(now, n.intf.pki.GetCAPool()) if valid { return false } @@ -464,8 +464,8 @@ func (n *connectionManager) sendPunch(hostinfo *HostInfo) { } func (n *connectionManager) tryRehandshake(hostinfo *HostInfo) { - certState := n.intf.certState.Load() - if bytes.Equal(hostinfo.ConnectionState.certState.certificate.Signature, certState.certificate.Signature) { + certState := n.intf.pki.GetCertState() + if bytes.Equal(hostinfo.ConnectionState.certState.Certificate.Signature, certState.Certificate.Signature) { return } diff --git a/connection_manager_test.go b/connection_manager_test.go index 642e0554c..a489bf2bc 100644 --- a/connection_manager_test.go +++ b/connection_manager_test.go @@ -44,10 +44,10 @@ func Test_NewConnectionManagerTest(t *testing.T) { // Very incomplete mock objects hostMap := NewHostMap(l, vpncidr, preferredRanges) cs := &CertState{ - rawCertificate: []byte{}, - privateKey: []byte{}, - certificate: &cert.NebulaCertificate{}, - rawCertificateNoKey: []byte{}, + RawCertificate: []byte{}, + PrivateKey: []byte{}, + Certificate: &cert.NebulaCertificate{}, + RawCertificateNoKey: []byte{}, } lh := newTestLighthouse() @@ -57,10 +57,11 @@ func Test_NewConnectionManagerTest(t *testing.T) { outside: &udp.NoopConn{}, firewall: &Firewall{}, lightHouse: lh, + pki: &PKI{}, handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), l: l, } - ifce.certState.Store(cs) + ifce.pki.cs.Store(cs) // Create manager ctx, cancel := context.WithCancel(context.Background()) @@ -123,10 +124,10 @@ func Test_NewConnectionManagerTest2(t *testing.T) { // Very incomplete mock objects hostMap := NewHostMap(l, vpncidr, preferredRanges) cs := &CertState{ - rawCertificate: []byte{}, - privateKey: []byte{}, - certificate: &cert.NebulaCertificate{}, - rawCertificateNoKey: []byte{}, + RawCertificate: []byte{}, + PrivateKey: []byte{}, + Certificate: &cert.NebulaCertificate{}, + RawCertificateNoKey: []byte{}, } lh := newTestLighthouse() @@ -136,10 +137,11 @@ func Test_NewConnectionManagerTest2(t *testing.T) { outside: &udp.NoopConn{}, firewall: &Firewall{}, lightHouse: lh, + pki: &PKI{}, handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), l: l, } - ifce.certState.Store(cs) + ifce.pki.cs.Store(cs) // Create manager ctx, cancel := context.WithCancel(context.Background()) @@ -242,10 +244,10 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { peerCert.Sign(cert.Curve_CURVE25519, privCA) cs := &CertState{ - rawCertificate: []byte{}, - privateKey: []byte{}, - certificate: &cert.NebulaCertificate{}, - rawCertificateNoKey: []byte{}, + RawCertificate: []byte{}, + PrivateKey: []byte{}, + Certificate: &cert.NebulaCertificate{}, + RawCertificateNoKey: []byte{}, } lh := newTestLighthouse() @@ -258,9 +260,10 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), l: l, disconnectInvalid: true, - caPool: ncp, + pki: &PKI{}, } - ifce.certState.Store(cs) + ifce.pki.cs.Store(cs) + ifce.pki.caPool.Store(ncp) // Create manager ctx, cancel := context.WithCancel(context.Background()) diff --git a/connection_state.go b/connection_state.go index ab818c97d..163e4bc74 100644 --- a/connection_state.go +++ b/connection_state.go @@ -30,15 +30,15 @@ type ConnectionState struct { func (f *Interface) newConnectionState(l *logrus.Logger, initiator bool, pattern noise.HandshakePattern, psk []byte, pskStage int) *ConnectionState { var dhFunc noise.DHFunc - curCertState := f.certState.Load() + curCertState := f.pki.GetCertState() - switch curCertState.certificate.Details.Curve { + switch curCertState.Certificate.Details.Curve { case cert.Curve_CURVE25519: dhFunc = noise.DH25519 case cert.Curve_P256: dhFunc = noiseutil.DHP256 default: - l.Errorf("invalid curve: %s", curCertState.certificate.Details.Curve) + l.Errorf("invalid curve: %s", curCertState.Certificate.Details.Curve) return nil } cs := noise.NewCipherSuite(dhFunc, noiseutil.CipherAESGCM, noise.HashSHA256) @@ -46,7 +46,7 @@ func (f *Interface) newConnectionState(l *logrus.Logger, initiator bool, pattern cs = noise.NewCipherSuite(dhFunc, noise.CipherChaChaPoly, noise.HashSHA256) } - static := noise.DHKey{Private: curCertState.privateKey, Public: curCertState.publicKey} + static := noise.DHKey{Private: curCertState.PrivateKey, Public: curCertState.PublicKey} b := NewBits(ReplayWindow) // Clear out bit 0, we never transmit it and we don't want it showing as packet loss diff --git a/control_tester.go b/control_tester.go index dd1a77418..a26c8bb23 100644 --- a/control_tester.go +++ b/control_tester.go @@ -161,7 +161,7 @@ func (c *Control) GetHostmap() *HostMap { } func (c *Control) GetCert() *cert.NebulaCertificate { - return c.f.certState.Load().certificate + return c.f.pki.GetCertState().Certificate } func (c *Control) ReHandshake(vpnIp iputil.VpnIp) { diff --git a/handshake_ix.go b/handshake_ix.go index 70263b96a..52efdf5e6 100644 --- a/handshake_ix.go +++ b/handshake_ix.go @@ -33,7 +33,7 @@ func ixHandshakeStage0(f *Interface, vpnIp iputil.VpnIp, hostinfo *HostInfo) { hsProto := &NebulaHandshakeDetails{ InitiatorIndex: hostinfo.localIndexId, Time: uint64(time.Now().UnixNano()), - Cert: ci.certState.rawCertificateNoKey, + Cert: ci.certState.RawCertificateNoKey, } hsBytes := []byte{} @@ -91,7 +91,7 @@ func ixHandshakeStage1(f *Interface, addr *udp.Addr, via *ViaSender, packet []by return } - remoteCert, err := RecombineCertAndValidate(ci.H, hs.Details.Cert, f.caPool) + remoteCert, err := RecombineCertAndValidate(ci.H, hs.Details.Cert, f.pki.GetCAPool()) if err != nil { f.l.WithError(err).WithField("udpAddr", addr). WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).WithField("cert", remoteCert). @@ -155,7 +155,7 @@ func ixHandshakeStage1(f *Interface, addr *udp.Addr, via *ViaSender, packet []by Info("Handshake message received") hs.Details.ResponderIndex = myIndex - hs.Details.Cert = ci.certState.rawCertificateNoKey + hs.Details.Cert = ci.certState.RawCertificateNoKey // Update the time in case their clock is way off from ours hs.Details.Time = uint64(time.Now().UnixNano()) @@ -399,7 +399,7 @@ func ixHandshakeStage2(f *Interface, addr *udp.Addr, via *ViaSender, hostinfo *H return true } - remoteCert, err := RecombineCertAndValidate(ci.H, hs.Details.Cert, f.caPool) + remoteCert, err := RecombineCertAndValidate(ci.H, hs.Details.Cert, f.pki.GetCAPool()) if err != nil { f.l.WithError(err).WithField("vpnIp", hostinfo.vpnIp).WithField("udpAddr", addr). WithField("cert", remoteCert).WithField("handshake", m{"stage": 2, "style": "ix_psk0"}). diff --git a/inside.go b/inside.go index 0d4392666..0fac833a6 100644 --- a/inside.go +++ b/inside.go @@ -69,7 +69,7 @@ func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet ci.queueLock.Unlock() } - dropReason := f.firewall.Drop(packet, *fwPacket, false, hostinfo, f.caPool, localCache) + dropReason := f.firewall.Drop(packet, *fwPacket, false, hostinfo, f.pki.GetCAPool(), localCache) if dropReason == nil { f.sendNoMetrics(header.Message, 0, ci, hostinfo, nil, packet, nb, out, q) @@ -183,7 +183,7 @@ func (f *Interface) sendMessageNow(t header.MessageType, st header.MessageSubTyp } // check if packet is in outbound fw rules - dropReason := f.firewall.Drop(p, *fp, false, hostinfo, f.caPool, nil) + dropReason := f.firewall.Drop(p, *fp, false, hostinfo, f.pki.GetCAPool(), nil) if dropReason != nil { if f.l.Level >= logrus.DebugLevel { f.l.WithField("fwPacket", fp). diff --git a/interface.go b/interface.go index 771aed0e6..fbf610a9b 100644 --- a/interface.go +++ b/interface.go @@ -13,7 +13,6 @@ import ( "github.com/rcrowley/go-metrics" "github.com/sirupsen/logrus" - "github.com/slackhq/nebula/cert" "github.com/slackhq/nebula/config" "github.com/slackhq/nebula/firewall" "github.com/slackhq/nebula/header" @@ -28,7 +27,7 @@ type InterfaceConfig struct { HostMap *HostMap Outside udp.Conn Inside overlay.Device - certState *CertState + pki *PKI Cipher string Firewall *Firewall ServeDns bool @@ -41,7 +40,6 @@ type InterfaceConfig struct { routines int MessageMetrics *MessageMetrics version string - caPool *cert.NebulaCAPool disconnectInvalid bool relayManager *relayManager punchy *Punchy @@ -58,7 +56,7 @@ type Interface struct { hostMap *HostMap outside udp.Conn inside overlay.Device - certState atomic.Pointer[CertState] + pki *PKI cipher string firewall *Firewall connectionManager *connectionManager @@ -71,7 +69,6 @@ type Interface struct { dropLocalBroadcast bool dropMulticast bool routines int - caPool *cert.NebulaCAPool disconnectInvalid bool closed atomic.Bool relayManager *relayManager @@ -152,15 +149,17 @@ func NewInterface(ctx context.Context, c *InterfaceConfig) (*Interface, error) { if c.Inside == nil { return nil, errors.New("no inside interface (tun)") } - if c.certState == nil { + if c.pki == nil { return nil, errors.New("no certificate state") } if c.Firewall == nil { return nil, errors.New("no firewall rules") } - myVpnIp := iputil.Ip2VpnIp(c.certState.certificate.Details.Ips[0].IP) + certificate := c.pki.GetCertState().Certificate + myVpnIp := iputil.Ip2VpnIp(certificate.Details.Ips[0].IP) ifce := &Interface{ + pki: c.pki, hostMap: c.HostMap, outside: c.Outside, inside: c.Inside, @@ -170,14 +169,13 @@ func NewInterface(ctx context.Context, c *InterfaceConfig) (*Interface, error) { handshakeManager: c.HandshakeManager, createTime: time.Now(), lightHouse: c.lightHouse, - localBroadcast: myVpnIp | ^iputil.Ip2VpnIp(c.certState.certificate.Details.Ips[0].Mask), + localBroadcast: myVpnIp | ^iputil.Ip2VpnIp(certificate.Details.Ips[0].Mask), dropLocalBroadcast: c.DropLocalBroadcast, dropMulticast: c.DropMulticast, routines: c.routines, version: c.version, writers: make([]udp.Conn, c.routines), readers: make([]io.ReadWriteCloser, c.routines), - caPool: c.caPool, disconnectInvalid: c.disconnectInvalid, myVpnIp: myVpnIp, relayManager: c.relayManager, @@ -198,7 +196,6 @@ func NewInterface(ctx context.Context, c *InterfaceConfig) (*Interface, error) { ifce.reQueryEvery.Store(c.reQueryEvery) ifce.reQueryWait.Store(int64(c.reQueryWait)) - ifce.certState.Store(c.certState) ifce.connectionManager = newConnectionManager(ctx, c.l, ifce, c.checkInterval, c.pendingDeletionInterval, c.punchy) return ifce, nil @@ -295,8 +292,6 @@ func (f *Interface) listenIn(reader io.ReadWriteCloser, i int) { } func (f *Interface) RegisterConfigChangeCallbacks(c *config.C) { - c.RegisterReloadCallback(f.reloadCA) - c.RegisterReloadCallback(f.reloadCertKey) c.RegisterReloadCallback(f.reloadFirewall) c.RegisterReloadCallback(f.reloadSendRecvError) c.RegisterReloadCallback(f.reloadMisc) @@ -305,40 +300,6 @@ func (f *Interface) RegisterConfigChangeCallbacks(c *config.C) { } } -func (f *Interface) reloadCA(c *config.C) { - // reload and check regardless - // todo: need mutex? - newCAs, err := loadCAFromConfig(f.l, c) - if err != nil { - f.l.WithError(err).Error("Could not refresh trusted CA certificates") - return - } - - f.caPool = newCAs - f.l.WithField("fingerprints", f.caPool.GetFingerprints()).Info("Trusted CA certificates refreshed") -} - -func (f *Interface) reloadCertKey(c *config.C) { - // reload and check in all cases - cs, err := NewCertStateFromConfig(c) - if err != nil { - f.l.WithError(err).Error("Could not refresh client cert") - return - } - - // did IP in cert change? if so, don't set - currentCert := f.certState.Load().certificate - oldIPs := currentCert.Details.Ips - newIPs := cs.certificate.Details.Ips - if len(oldIPs) > 0 && len(newIPs) > 0 && oldIPs[0].String() != newIPs[0].String() { - f.l.WithField("new_ip", newIPs[0]).WithField("old_ip", oldIPs[0]).Error("IP in new cert was different from old") - return - } - - f.certState.Store(cs) - f.l.WithField("cert", cs.certificate).Info("Client cert refreshed from disk") -} - func (f *Interface) reloadFirewall(c *config.C) { //TODO: need to trigger/detect if the certificate changed too if c.HasChanged("firewall") == false { @@ -346,7 +307,7 @@ func (f *Interface) reloadFirewall(c *config.C) { return } - fw, err := NewFirewallFromConfig(f.l, f.certState.Load().certificate, c) + fw, err := NewFirewallFromConfig(f.l, f.pki.GetCertState().Certificate, c) if err != nil { f.l.WithError(err).Error("Error while creating firewall during reload") return @@ -438,7 +399,7 @@ func (f *Interface) emitStats(ctx context.Context, i time.Duration) { f.firewall.EmitStats() f.handshakeManager.EmitStats() udpStats() - certExpirationGauge.Update(int64(f.certState.Load().certificate.Details.NotAfter.Sub(time.Now()) / time.Second)) + certExpirationGauge.Update(int64(f.pki.GetCertState().Certificate.Details.NotAfter.Sub(time.Now()) / time.Second)) } } } diff --git a/lighthouse.go b/lighthouse.go index 6c46663c9..9b3b837e0 100644 --- a/lighthouse.go +++ b/lighthouse.go @@ -132,7 +132,7 @@ func NewLightHouseFromConfig(ctx context.Context, l *logrus.Logger, c *config.C, c.RegisterReloadCallback(func(c *config.C) { err := h.reload(c, false) switch v := err.(type) { - case util.ContextualError: + case *util.ContextualError: v.Log(l) case error: l.WithError(err).Error("failed to reload lighthouse") diff --git a/main.go b/main.go index d050db226..4e8448b84 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,6 @@ package nebula import ( "context" "encoding/binary" - "errors" "fmt" "net" "time" @@ -46,7 +45,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg err := configLogger(l, c) if err != nil { - return nil, util.NewContextualError("Failed to configure the logger", nil, err) + return nil, util.ContextualizeIfNeeded("Failed to configure the logger", err) } c.RegisterReloadCallback(func(c *config.C) { @@ -56,28 +55,20 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg } }) - caPool, err := loadCAFromConfig(l, c) + pki, err := NewPKIFromConfig(l, c) if err != nil { - //The errors coming out of loadCA are already nicely formatted - return nil, util.NewContextualError("Failed to load ca from config", nil, err) + return nil, util.ContextualizeIfNeeded("Failed to load PKI from config", err) } - l.WithField("fingerprints", caPool.GetFingerprints()).Debug("Trusted CA fingerprints") - cs, err := NewCertStateFromConfig(c) + certificate := pki.GetCertState().Certificate + fw, err := NewFirewallFromConfig(l, certificate, c) if err != nil { - //The errors coming out of NewCertStateFromConfig are already nicely formatted - return nil, util.NewContextualError("Failed to load certificate from config", nil, err) - } - l.WithField("cert", cs.certificate).Debug("Client nebula certificate") - - fw, err := NewFirewallFromConfig(l, cs.certificate, c) - if err != nil { - return nil, util.NewContextualError("Error while loading firewall rules", nil, err) + return nil, util.ContextualizeIfNeeded("Error while loading firewall rules", err) } l.WithField("firewallHash", fw.GetRuleHash()).Info("Firewall started") // TODO: make sure mask is 4 bytes - tunCidr := cs.certificate.Details.Ips[0] + tunCidr := certificate.Details.Ips[0] ssh, err := sshd.NewSSHServer(l.WithField("subsystem", "sshd")) wireSSHReload(l, ssh, c) @@ -85,7 +76,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg if c.GetBool("sshd.enabled", false) { sshStart, err = configSSH(l, ssh, c) if err != nil { - return nil, util.NewContextualError("Error while configuring the sshd", nil, err) + return nil, util.ContextualizeIfNeeded("Error while configuring the sshd", err) } } @@ -136,7 +127,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg tun, err = overlay.NewDeviceFromConfig(c, l, tunCidr, tunFd, routines) if err != nil { - return nil, util.NewContextualError("Failed to get a tun/tap device", nil, err) + return nil, util.ContextualizeIfNeeded("Failed to get a tun/tap device", err) } defer func() { @@ -160,7 +151,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg } else { listenHost, err = net.ResolveIPAddr("ip", rawListenHost) if err != nil { - return nil, util.NewContextualError("Failed to resolve listen.host", nil, err) + return nil, util.ContextualizeIfNeeded("Failed to resolve listen.host", err) } } @@ -182,7 +173,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg for _, rawPreferredRange := range rawPreferredRanges { _, preferredRange, err := net.ParseCIDR(rawPreferredRange) if err != nil { - return nil, util.NewContextualError("Failed to parse preferred ranges", nil, err) + return nil, util.ContextualizeIfNeeded("Failed to parse preferred ranges", err) } preferredRanges = append(preferredRanges, preferredRange) } @@ -195,7 +186,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg if rawLocalRange != "" { _, localRange, err := net.ParseCIDR(rawLocalRange) if err != nil { - return nil, util.NewContextualError("Failed to parse local_range", nil, err) + return nil, util.ContextualizeIfNeeded("Failed to parse local_range", err) } // Check if the entry for local_range was already specified in @@ -222,11 +213,8 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg punchy := NewPunchyFromConfig(l, c) lightHouse, err := NewLightHouseFromConfig(ctx, l, c, tunCidr, udpConns[0], punchy) - switch { - case errors.As(err, &util.ContextualError{}): - return nil, err - case err != nil: - return nil, util.NewContextualError("Failed to initialize lighthouse handler", nil, err) + if err != nil { + return nil, util.ContextualizeIfNeeded("Failed to initialize lighthouse handler", err) } var messageMetrics *MessageMetrics @@ -266,7 +254,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg HostMap: hostMap, Inside: tun, Outside: udpConns[0], - certState: cs, + pki: pki, Cipher: c.GetString("cipher", "aes"), Firewall: fw, ServeDns: serveDns, @@ -282,7 +270,6 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg routines: routines, MessageMetrics: messageMetrics, version: buildVersion, - caPool: caPool, disconnectInvalid: c.GetBool("pki.disconnect_invalid", false), relayManager: NewRelayManager(ctx, l, hostMap, c), punchy: punchy, @@ -321,9 +308,8 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg // TODO - stats third-party modules start uncancellable goroutines. Update those libs to accept // a context so that they can exit when the context is Done. statsStart, err := startStats(l, c, buildVersion, configTest) - if err != nil { - return nil, util.NewContextualError("Failed to start stats emitter", nil, err) + return nil, util.ContextualizeIfNeeded("Failed to start stats emitter", err) } if configTest { diff --git a/outside.go b/outside.go index 19a980bfa..a9dcdc860 100644 --- a/outside.go +++ b/outside.go @@ -404,7 +404,7 @@ func (f *Interface) decryptToTun(hostinfo *HostInfo, messageCounter uint64, out return false } - dropReason := f.firewall.Drop(out, *fwPacket, true, hostinfo, f.caPool, localCache) + dropReason := f.firewall.Drop(out, *fwPacket, true, hostinfo, f.pki.GetCAPool(), localCache) if dropReason != nil { f.rejectOutside(out, hostinfo.ConnectionState, hostinfo, nb, out, q) if f.l.Level >= logrus.DebugLevel { diff --git a/pki.go b/pki.go new file mode 100644 index 000000000..91478ce51 --- /dev/null +++ b/pki.go @@ -0,0 +1,248 @@ +package nebula + +import ( + "errors" + "fmt" + "os" + "strings" + "sync/atomic" + "time" + + "github.com/sirupsen/logrus" + "github.com/slackhq/nebula/cert" + "github.com/slackhq/nebula/config" + "github.com/slackhq/nebula/util" +) + +type PKI struct { + cs atomic.Pointer[CertState] + caPool atomic.Pointer[cert.NebulaCAPool] + l *logrus.Logger +} + +type CertState struct { + Certificate *cert.NebulaCertificate + RawCertificate []byte + RawCertificateNoKey []byte + PublicKey []byte + PrivateKey []byte +} + +func NewPKIFromConfig(l *logrus.Logger, c *config.C) (*PKI, error) { + pki := &PKI{l: l} + err := pki.reload(c, true) + if err != nil { + return nil, err + } + + c.RegisterReloadCallback(func(c *config.C) { + rErr := pki.reload(c, false) + if rErr != nil { + util.LogWithContextIfNeeded("Failed to reload PKI from config", rErr, l) + } + }) + + return pki, nil +} + +func (p *PKI) GetCertState() *CertState { + return p.cs.Load() +} + +func (p *PKI) GetCAPool() *cert.NebulaCAPool { + return p.caPool.Load() +} + +func (p *PKI) reload(c *config.C, initial bool) error { + err := p.reloadCert(c, initial) + if err != nil { + if initial { + return err + } + err.Log(p.l) + } + + err = p.reloadCAPool(c) + if err != nil { + if initial { + return err + } + err.Log(p.l) + } + + return nil +} + +func (p *PKI) reloadCert(c *config.C, initial bool) *util.ContextualError { + cs, err := newCertStateFromConfig(c) + if err != nil { + return util.NewContextualError("Could not load client cert", nil, err) + } + + if !initial { + // did IP in cert change? if so, don't set + currentCert := p.cs.Load().Certificate + oldIPs := currentCert.Details.Ips + newIPs := cs.Certificate.Details.Ips + if len(oldIPs) > 0 && len(newIPs) > 0 && oldIPs[0].String() != newIPs[0].String() { + return util.NewContextualError( + "IP in new cert was different from old", + m{"new_ip": newIPs[0], "old_ip": oldIPs[0]}, + nil, + ) + } + } + + p.cs.Store(cs) + if initial { + p.l.WithField("cert", cs.Certificate).Debug("Client nebula certificate") + } else { + p.l.WithField("cert", cs.Certificate).Info("Client cert refreshed from disk") + } + return nil +} + +func (p *PKI) reloadCAPool(c *config.C) *util.ContextualError { + caPool, err := loadCAPoolFromConfig(p.l, c) + if err != nil { + return util.NewContextualError("Failed to load ca from config", nil, err) + } + + p.caPool.Store(caPool) + p.l.WithField("fingerprints", caPool.GetFingerprints()).Debug("Trusted CA fingerprints") + return nil +} + +func newCertState(certificate *cert.NebulaCertificate, privateKey []byte) (*CertState, error) { + // Marshal the certificate to ensure it is valid + rawCertificate, err := certificate.Marshal() + if err != nil { + return nil, fmt.Errorf("invalid nebula certificate on interface: %s", err) + } + + publicKey := certificate.Details.PublicKey + cs := &CertState{ + RawCertificate: rawCertificate, + Certificate: certificate, + PrivateKey: privateKey, + PublicKey: publicKey, + } + + cs.Certificate.Details.PublicKey = nil + rawCertNoKey, err := cs.Certificate.Marshal() + if err != nil { + return nil, fmt.Errorf("error marshalling certificate no key: %s", err) + } + cs.RawCertificateNoKey = rawCertNoKey + // put public key back + cs.Certificate.Details.PublicKey = cs.PublicKey + return cs, nil +} + +func newCertStateFromConfig(c *config.C) (*CertState, error) { + var pemPrivateKey []byte + var err error + + privPathOrPEM := c.GetString("pki.key", "") + if privPathOrPEM == "" { + return nil, errors.New("no pki.key path or PEM data provided") + } + + if strings.Contains(privPathOrPEM, "-----BEGIN") { + pemPrivateKey = []byte(privPathOrPEM) + privPathOrPEM = "" + + } else { + pemPrivateKey, err = os.ReadFile(privPathOrPEM) + if err != nil { + return nil, fmt.Errorf("unable to read pki.key file %s: %s", privPathOrPEM, err) + } + } + + rawKey, _, curve, err := cert.UnmarshalPrivateKey(pemPrivateKey) + if err != nil { + return nil, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err) + } + + var rawCert []byte + + pubPathOrPEM := c.GetString("pki.cert", "") + if pubPathOrPEM == "" { + return nil, errors.New("no pki.cert path or PEM data provided") + } + + if strings.Contains(pubPathOrPEM, "-----BEGIN") { + rawCert = []byte(pubPathOrPEM) + pubPathOrPEM = "" + + } else { + rawCert, err = os.ReadFile(pubPathOrPEM) + if err != nil { + return nil, fmt.Errorf("unable to read pki.cert file %s: %s", pubPathOrPEM, err) + } + } + + nebulaCert, _, err := cert.UnmarshalNebulaCertificateFromPEM(rawCert) + if err != nil { + return nil, fmt.Errorf("error while unmarshaling pki.cert %s: %s", pubPathOrPEM, err) + } + + if nebulaCert.Expired(time.Now()) { + return nil, fmt.Errorf("nebula certificate for this host is expired") + } + + if len(nebulaCert.Details.Ips) == 0 { + return nil, fmt.Errorf("no IPs encoded in certificate") + } + + if err = nebulaCert.VerifyPrivateKey(curve, rawKey); err != nil { + return nil, fmt.Errorf("private key is not a pair with public key in nebula cert") + } + + return newCertState(nebulaCert, rawKey) +} + +func loadCAPoolFromConfig(l *logrus.Logger, c *config.C) (*cert.NebulaCAPool, error) { + var rawCA []byte + var err error + + caPathOrPEM := c.GetString("pki.ca", "") + if caPathOrPEM == "" { + return nil, errors.New("no pki.ca path or PEM data provided") + } + + if strings.Contains(caPathOrPEM, "-----BEGIN") { + rawCA = []byte(caPathOrPEM) + + } else { + rawCA, err = os.ReadFile(caPathOrPEM) + if err != nil { + return nil, fmt.Errorf("unable to read pki.ca file %s: %s", caPathOrPEM, err) + } + } + + caPool, err := cert.NewCAPoolFromBytes(rawCA) + if errors.Is(err, cert.ErrExpired) { + var expired int + for _, crt := range caPool.CAs { + if crt.Expired(time.Now()) { + expired++ + l.WithField("cert", crt).Warn("expired certificate present in CA pool") + } + } + + if expired >= len(caPool.CAs) { + return nil, errors.New("no valid CA certificates present") + } + + } else if err != nil { + return nil, fmt.Errorf("error while adding CA certificate to CA trust store: %s", err) + } + + for _, fp := range c.GetStringSlice("pki.blocklist", []string{}) { + l.WithField("fingerprint", fp).Info("Blocklisting cert") + caPool.BlocklistFingerprint(fp) + } + + return caPool, nil +} diff --git a/ssh.go b/ssh.go index 0f624dbe5..44286c89a 100644 --- a/ssh.go +++ b/ssh.go @@ -754,7 +754,7 @@ func sshPrintCert(ifce *Interface, fs interface{}, a []string, w sshd.StringWrit return nil } - cert := ifce.certState.Load().certificate + cert := ifce.pki.GetCertState().Certificate if len(a) > 0 { parsedIp := net.ParseIP(a[0]) if parsedIp == nil { diff --git a/util/error.go b/util/error.go index 7f9bc4792..a11c9c471 100644 --- a/util/error.go +++ b/util/error.go @@ -12,18 +12,38 @@ type ContextualError struct { Context string } -func NewContextualError(msg string, fields map[string]interface{}, realError error) ContextualError { - return ContextualError{Context: msg, Fields: fields, RealError: realError} +func NewContextualError(msg string, fields map[string]interface{}, realError error) *ContextualError { + return &ContextualError{Context: msg, Fields: fields, RealError: realError} } -func (ce ContextualError) Error() string { +// ContextualizeIfNeeded is a helper function to turn an error into a ContextualError if it is not already one +func ContextualizeIfNeeded(msg string, err error) error { + switch err.(type) { + case *ContextualError: + return err + default: + return NewContextualError(msg, nil, err) + } +} + +// LogWithContextIfNeeded is a helper function to log an error line for an error or ContextualError +func LogWithContextIfNeeded(msg string, err error, l *logrus.Logger) { + switch v := err.(type) { + case *ContextualError: + v.Log(l) + default: + l.WithError(err).Error(msg) + } +} + +func (ce *ContextualError) Error() string { if ce.RealError == nil { return ce.Context } return ce.RealError.Error() } -func (ce ContextualError) Unwrap() error { +func (ce *ContextualError) Unwrap() error { if ce.RealError == nil { return errors.New(ce.Context) } diff --git a/util/error_test.go b/util/error_test.go index 747d04e0c..5041f82ce 100644 --- a/util/error_test.go +++ b/util/error_test.go @@ -2,6 +2,7 @@ package util import ( "errors" + "fmt" "testing" "github.com/sirupsen/logrus" @@ -67,3 +68,44 @@ func TestContextualError_Log(t *testing.T) { e.Log(l) assert.Equal(t, []string{"level=error error=error\n"}, tl.Logs) } + +func TestLogWithContextIfNeeded(t *testing.T) { + l := logrus.New() + l.Formatter = &logrus.TextFormatter{ + DisableTimestamp: true, + DisableColors: true, + } + + tl := NewTestLogWriter() + l.Out = tl + + // Test ignoring fallback context + tl.Reset() + e := NewContextualError("test message", m{"field": "1"}, errors.New("error")) + LogWithContextIfNeeded("This should get thrown away", e, l) + assert.Equal(t, []string{"level=error msg=\"test message\" error=error field=1\n"}, tl.Logs) + + // Test using fallback context + tl.Reset() + err := fmt.Errorf("this is a normal error") + LogWithContextIfNeeded("Fallback context woo", err, l) + assert.Equal(t, []string{"level=error msg=\"Fallback context woo\" error=\"this is a normal error\"\n"}, tl.Logs) +} + +func TestContextualizeIfNeeded(t *testing.T) { + // Test ignoring fallback context + e := NewContextualError("test message", m{"field": "1"}, errors.New("error")) + assert.Same(t, e, ContextualizeIfNeeded("should be ignored", e)) + + // Test using fallback context + err := fmt.Errorf("this is a normal error") + cErr := ContextualizeIfNeeded("Fallback context woo", err) + + switch v := cErr.(type) { + case *ContextualError: + assert.Equal(t, err, v.RealError) + default: + t.Error("Error was not wrapped") + t.Fail() + } +} From 7edcf620c0595f1b672ad7d2be5a0db729ed6ea0 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Mon, 21 Aug 2023 14:11:06 -0500 Subject: [PATCH 37/52] We only need the certificate in ConnectionState (#953) --- connection_manager.go | 6 +++--- connection_manager_test.go | 19 ++++++++++--------- connection_state.go | 20 +++++++++++--------- control_tester.go | 2 +- handshake_ix.go | 11 +++++++---- handshake_manager.go | 6 +----- handshake_manager_test.go | 13 ++----------- inside.go | 9 +-------- ssh.go | 2 +- 9 files changed, 37 insertions(+), 51 deletions(-) diff --git a/connection_manager.go b/connection_manager.go index 62a8dd234..81563a4eb 100644 --- a/connection_manager.go +++ b/connection_manager.go @@ -406,7 +406,7 @@ func (n *connectionManager) shouldSwapPrimary(current, primary *HostInfo) bool { } certState := n.intf.pki.GetCertState() - return bytes.Equal(current.ConnectionState.certState.Certificate.Signature, certState.Certificate.Signature) + return bytes.Equal(current.ConnectionState.myCert.Signature, certState.Certificate.Signature) } func (n *connectionManager) swapPrimary(current, primary *HostInfo) { @@ -465,7 +465,7 @@ func (n *connectionManager) sendPunch(hostinfo *HostInfo) { func (n *connectionManager) tryRehandshake(hostinfo *HostInfo) { certState := n.intf.pki.GetCertState() - if bytes.Equal(hostinfo.ConnectionState.certState.Certificate.Signature, certState.Certificate.Signature) { + if bytes.Equal(hostinfo.ConnectionState.myCert.Signature, certState.Certificate.Signature) { return } @@ -474,7 +474,7 @@ func (n *connectionManager) tryRehandshake(hostinfo *HostInfo) { Info("Re-handshaking with remote") //TODO: this is copied from getOrHandshake to keep the extra checks out of the hot path, figure it out - newHostinfo := n.intf.handshakeManager.AddVpnIp(hostinfo.vpnIp, n.intf.initHostInfo) + newHostinfo := n.intf.handshakeManager.AddVpnIp(hostinfo.vpnIp) if !newHostinfo.HandshakeReady { ixHandshakeStage0(n.intf, newHostinfo.vpnIp, newHostinfo) } diff --git a/connection_manager_test.go b/connection_manager_test.go index a489bf2bc..e220819f3 100644 --- a/connection_manager_test.go +++ b/connection_manager_test.go @@ -79,8 +79,8 @@ func Test_NewConnectionManagerTest(t *testing.T) { remoteIndexId: 9901, } hostinfo.ConnectionState = &ConnectionState{ - certState: cs, - H: &noise.HandshakeState{}, + myCert: &cert.NebulaCertificate{}, + H: &noise.HandshakeState{}, } nc.hostMap.unlockedAddHostInfo(hostinfo, ifce) @@ -159,8 +159,8 @@ func Test_NewConnectionManagerTest2(t *testing.T) { remoteIndexId: 9901, } hostinfo.ConnectionState = &ConnectionState{ - certState: cs, - H: &noise.HandshakeState{}, + myCert: &cert.NebulaCertificate{}, + H: &noise.HandshakeState{}, } nc.hostMap.unlockedAddHostInfo(hostinfo, ifce) @@ -222,7 +222,8 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { PublicKey: pubCA, }, } - caCert.Sign(cert.Curve_CURVE25519, privCA) + + assert.NoError(t, caCert.Sign(cert.Curve_CURVE25519, privCA)) ncp := &cert.NebulaCAPool{ CAs: cert.NewCAPool().CAs, } @@ -241,7 +242,7 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { Issuer: "ca", }, } - peerCert.Sign(cert.Curve_CURVE25519, privCA) + assert.NoError(t, peerCert.Sign(cert.Curve_CURVE25519, privCA)) cs := &CertState{ RawCertificate: []byte{}, @@ -275,9 +276,9 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { hostinfo := &HostInfo{ vpnIp: vpnIp, ConnectionState: &ConnectionState{ - certState: cs, - peerCert: &peerCert, - H: &noise.HandshakeState{}, + myCert: &cert.NebulaCertificate{}, + peerCert: &peerCert, + H: &noise.HandshakeState{}, }, } nc.hostMap.unlockedAddHostInfo(hostinfo, ifce) diff --git a/connection_state.go b/connection_state.go index 163e4bc74..52607496e 100644 --- a/connection_state.go +++ b/connection_state.go @@ -18,7 +18,7 @@ type ConnectionState struct { eKey *NebulaCipherState dKey *NebulaCipherState H *noise.HandshakeState - certState *CertState + myCert *cert.NebulaCertificate peerCert *cert.NebulaCertificate initiator bool messageCounter atomic.Uint64 @@ -28,25 +28,27 @@ type ConnectionState struct { ready bool } -func (f *Interface) newConnectionState(l *logrus.Logger, initiator bool, pattern noise.HandshakePattern, psk []byte, pskStage int) *ConnectionState { +func NewConnectionState(l *logrus.Logger, cipher string, certState *CertState, initiator bool, pattern noise.HandshakePattern, psk []byte, pskStage int) *ConnectionState { var dhFunc noise.DHFunc - curCertState := f.pki.GetCertState() - switch curCertState.Certificate.Details.Curve { + switch certState.Certificate.Details.Curve { case cert.Curve_CURVE25519: dhFunc = noise.DH25519 case cert.Curve_P256: dhFunc = noiseutil.DHP256 default: - l.Errorf("invalid curve: %s", curCertState.Certificate.Details.Curve) + l.Errorf("invalid curve: %s", certState.Certificate.Details.Curve) return nil } - cs := noise.NewCipherSuite(dhFunc, noiseutil.CipherAESGCM, noise.HashSHA256) - if f.cipher == "chachapoly" { + + var cs noise.CipherSuite + if cipher == "chachapoly" { cs = noise.NewCipherSuite(dhFunc, noise.CipherChaChaPoly, noise.HashSHA256) + } else { + cs = noise.NewCipherSuite(dhFunc, noiseutil.CipherAESGCM, noise.HashSHA256) } - static := noise.DHKey{Private: curCertState.PrivateKey, Public: curCertState.PublicKey} + static := noise.DHKey{Private: certState.PrivateKey, Public: certState.PublicKey} b := NewBits(ReplayWindow) // Clear out bit 0, we never transmit it and we don't want it showing as packet loss @@ -72,7 +74,7 @@ func (f *Interface) newConnectionState(l *logrus.Logger, initiator bool, pattern initiator: initiator, window: b, ready: false, - certState: curCertState, + myCert: certState.Certificate, } return ci diff --git a/control_tester.go b/control_tester.go index a26c8bb23..680cd5a77 100644 --- a/control_tester.go +++ b/control_tester.go @@ -165,7 +165,7 @@ func (c *Control) GetCert() *cert.NebulaCertificate { } func (c *Control) ReHandshake(vpnIp iputil.VpnIp) { - hostinfo := c.f.handshakeManager.AddVpnIp(vpnIp, c.f.initHostInfo) + hostinfo := c.f.handshakeManager.AddVpnIp(vpnIp) ixHandshakeStage0(c.f, vpnIp, hostinfo) // If this is a static host, we don't need to wait for the HostQueryReply diff --git a/handshake_ix.go b/handshake_ix.go index 52efdf5e6..94f408f89 100644 --- a/handshake_ix.go +++ b/handshake_ix.go @@ -28,12 +28,14 @@ func ixHandshakeStage0(f *Interface, vpnIp iputil.VpnIp, hostinfo *HostInfo) { return } - ci := hostinfo.ConnectionState + certState := f.pki.GetCertState() + ci := NewConnectionState(f.l, f.cipher, certState, true, noise.HandshakeIX, []byte{}, 0) + hostinfo.ConnectionState = ci hsProto := &NebulaHandshakeDetails{ InitiatorIndex: hostinfo.localIndexId, Time: uint64(time.Now().UnixNano()), - Cert: ci.certState.RawCertificateNoKey, + Cert: certState.RawCertificateNoKey, } hsBytes := []byte{} @@ -69,7 +71,8 @@ func ixHandshakeStage0(f *Interface, vpnIp iputil.VpnIp, hostinfo *HostInfo) { } func ixHandshakeStage1(f *Interface, addr *udp.Addr, via *ViaSender, packet []byte, h *header.H) { - ci := f.newConnectionState(f.l, false, noise.HandshakeIX, []byte{}, 0) + certState := f.pki.GetCertState() + ci := NewConnectionState(f.l, f.cipher, certState, false, noise.HandshakeIX, []byte{}, 0) // Mark packet 1 as seen so it doesn't show up as missed ci.window.Update(f.l, 1) @@ -155,7 +158,7 @@ func ixHandshakeStage1(f *Interface, addr *udp.Addr, via *ViaSender, packet []by Info("Handshake message received") hs.Details.ResponderIndex = myIndex - hs.Details.Cert = ci.certState.RawCertificateNoKey + hs.Details.Cert = certState.RawCertificateNoKey // Update the time in case their clock is way off from ours hs.Details.Time = uint64(time.Now().UnixNano()) diff --git a/handshake_manager.go b/handshake_manager.go index a70f4dbc3..e15b79433 100644 --- a/handshake_manager.go +++ b/handshake_manager.go @@ -297,7 +297,7 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light } // AddVpnIp will try to handshake with the provided vpn ip and return the hostinfo for it. -func (c *HandshakeManager) AddVpnIp(vpnIp iputil.VpnIp, init func(*HostInfo)) *HostInfo { +func (c *HandshakeManager) AddVpnIp(vpnIp iputil.VpnIp) *HostInfo { // A write lock is used to avoid having to recheck the map and trading a read lock for a write lock c.Lock() defer c.Unlock() @@ -317,10 +317,6 @@ func (c *HandshakeManager) AddVpnIp(vpnIp iputil.VpnIp, init func(*HostInfo)) *H }, } - if init != nil { - init(hostinfo) - } - c.vpnIps[vpnIp] = hostinfo c.metricInitiated.Inc(1) c.OutboundHandshakeTimer.Add(vpnIp, c.config.tryInterval) diff --git a/handshake_manager_test.go b/handshake_manager_test.go index 383e90084..c6df37d51 100644 --- a/handshake_manager_test.go +++ b/handshake_manager_test.go @@ -28,17 +28,8 @@ func Test_NewHandshakeManagerVpnIp(t *testing.T) { now := time.Now() blah.NextOutboundHandshakeTimerTick(now, mw) - var initCalled bool - initFunc := func(*HostInfo) { - initCalled = true - } - - i := blah.AddVpnIp(ip, initFunc) - assert.True(t, initCalled) - - initCalled = false - i2 := blah.AddVpnIp(ip, initFunc) - assert.False(t, initCalled) + i := blah.AddVpnIp(ip) + i2 := blah.AddVpnIp(ip) assert.Same(t, i, i2) i.remotes = NewRemoteList(nil) diff --git a/inside.go b/inside.go index 0fac833a6..6a0e078ab 100644 --- a/inside.go +++ b/inside.go @@ -1,7 +1,6 @@ package nebula import ( - "github.com/flynn/noise" "github.com/sirupsen/logrus" "github.com/slackhq/nebula/firewall" "github.com/slackhq/nebula/header" @@ -124,7 +123,7 @@ func (f *Interface) getOrHandshake(vpnIp iputil.VpnIp) *HostInfo { hostinfo := f.hostMap.PromoteBestQueryVpnIp(vpnIp, f) if hostinfo == nil { - hostinfo = f.handshakeManager.AddVpnIp(vpnIp, f.initHostInfo) + hostinfo = f.handshakeManager.AddVpnIp(vpnIp) } ci := hostinfo.ConnectionState @@ -168,12 +167,6 @@ func (f *Interface) getOrHandshake(vpnIp iputil.VpnIp) *HostInfo { return hostinfo } -// initHostInfo is the init function to pass to (*HandshakeManager).AddVpnIP that -// will create the initial Noise ConnectionState -func (f *Interface) initHostInfo(hostinfo *HostInfo) { - hostinfo.ConnectionState = f.newConnectionState(f.l, true, noise.HandshakeIX, []byte{}, 0) -} - func (f *Interface) sendMessageNow(t header.MessageType, st header.MessageSubType, hostinfo *HostInfo, p, nb, out []byte) { fp := &firewall.Packet{} err := newPacket(p, false, fp) diff --git a/ssh.go b/ssh.go index 44286c89a..c68e0820b 100644 --- a/ssh.go +++ b/ssh.go @@ -607,7 +607,7 @@ func sshCreateTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringW } } - hostInfo = ifce.handshakeManager.AddVpnIp(vpnIp, ifce.initHostInfo) + hostInfo = ifce.handshakeManager.AddVpnIp(vpnIp) if addr != nil { hostInfo.SetRemote(addr) } From 076ebc6c6e6c40a3e62f42c54d739c749c0791c0 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Mon, 21 Aug 2023 18:51:45 -0500 Subject: [PATCH 38/52] Simplify getting a hostinfo or starting a handshake with one (#954) --- connection_manager.go | 15 +--- connection_manager_test.go | 6 +- connection_state.go | 2 - control_tester.go | 12 +-- handshake_ix.go | 70 ++++++++--------- handshake_manager.go | 149 +++++++++++++++++++++++-------------- handshake_manager_test.go | 14 ++-- hostmap.go | 10 +-- inside.go | 95 ++++++----------------- main.go | 5 +- relay_manager.go | 2 +- ssh.go | 3 +- 12 files changed, 163 insertions(+), 220 deletions(-) diff --git a/connection_manager.go b/connection_manager.go index 81563a4eb..900db07cb 100644 --- a/connection_manager.go +++ b/connection_manager.go @@ -473,18 +473,5 @@ func (n *connectionManager) tryRehandshake(hostinfo *HostInfo) { WithField("reason", "local certificate is not current"). Info("Re-handshaking with remote") - //TODO: this is copied from getOrHandshake to keep the extra checks out of the hot path, figure it out - newHostinfo := n.intf.handshakeManager.AddVpnIp(hostinfo.vpnIp) - if !newHostinfo.HandshakeReady { - ixHandshakeStage0(n.intf, newHostinfo.vpnIp, newHostinfo) - } - - //If this is a static host, we don't need to wait for the HostQueryReply - //We can trigger the handshake right now - if _, ok := n.intf.lightHouse.GetStaticHostList()[hostinfo.vpnIp]; ok { - select { - case n.intf.handshakeManager.trigger <- hostinfo.vpnIp: - default: - } - } + n.intf.handshakeManager.StartHandshake(hostinfo.vpnIp, nil) } diff --git a/connection_manager_test.go b/connection_manager_test.go index e220819f3..e802904e1 100644 --- a/connection_manager_test.go +++ b/connection_manager_test.go @@ -58,7 +58,7 @@ func Test_NewConnectionManagerTest(t *testing.T) { firewall: &Firewall{}, lightHouse: lh, pki: &PKI{}, - handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), + handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), l: l, } ifce.pki.cs.Store(cs) @@ -138,7 +138,7 @@ func Test_NewConnectionManagerTest2(t *testing.T) { firewall: &Firewall{}, lightHouse: lh, pki: &PKI{}, - handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), + handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), l: l, } ifce.pki.cs.Store(cs) @@ -258,7 +258,7 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { outside: &udp.NoopConn{}, firewall: &Firewall{}, lightHouse: lh, - handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), + handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig), l: l, disconnectInvalid: true, pki: &PKI{}, diff --git a/connection_state.go b/connection_state.go index 52607496e..f8c31f65a 100644 --- a/connection_state.go +++ b/connection_state.go @@ -23,14 +23,12 @@ type ConnectionState struct { initiator bool messageCounter atomic.Uint64 window *Bits - queueLock sync.Mutex writeLock sync.Mutex ready bool } func NewConnectionState(l *logrus.Logger, cipher string, certState *CertState, initiator bool, pattern noise.HandshakePattern, psk []byte, pskStage int) *ConnectionState { var dhFunc noise.DHFunc - switch certState.Certificate.Details.Curve { case cert.Curve_CURVE25519: dhFunc = noise.DH25519 diff --git a/control_tester.go b/control_tester.go index 680cd5a77..b786ba383 100644 --- a/control_tester.go +++ b/control_tester.go @@ -165,15 +165,5 @@ func (c *Control) GetCert() *cert.NebulaCertificate { } func (c *Control) ReHandshake(vpnIp iputil.VpnIp) { - hostinfo := c.f.handshakeManager.AddVpnIp(vpnIp) - ixHandshakeStage0(c.f, vpnIp, hostinfo) - - // If this is a static host, we don't need to wait for the HostQueryReply - // We can trigger the handshake right now - if _, ok := c.f.lightHouse.GetStaticHostList()[hostinfo.vpnIp]; ok { - select { - case c.f.handshakeManager.trigger <- hostinfo.vpnIp: - default: - } - } + c.f.handshakeManager.StartHandshake(vpnIp, nil) } diff --git a/handshake_ix.go b/handshake_ix.go index 94f408f89..7e60c7907 100644 --- a/handshake_ix.go +++ b/handshake_ix.go @@ -13,19 +13,12 @@ import ( // This function constructs a handshake packet, but does not actually send it // Sending is done by the handshake manager -func ixHandshakeStage0(f *Interface, vpnIp iputil.VpnIp, hostinfo *HostInfo) { - // This queries the lighthouse if we don't know a remote for the host - // We do it here to provoke the lighthouse to preempt our timer wheel and trigger the stage 1 packet to send - // more quickly, effect is a quicker handshake. - if hostinfo.remote == nil { - f.lightHouse.QueryServer(vpnIp, f) - } - - err := f.handshakeManager.AddIndexHostInfo(hostinfo) +func ixHandshakeStage0(f *Interface, hostinfo *HostInfo) bool { + err := f.handshakeManager.allocateIndex(hostinfo) if err != nil { - f.l.WithError(err).WithField("vpnIp", vpnIp). + f.l.WithError(err).WithField("vpnIp", hostinfo.vpnIp). WithField("handshake", m{"stage": 0, "style": "ix_psk0"}).Error("Failed to generate index") - return + return false } certState := f.pki.GetCertState() @@ -46,9 +39,9 @@ func ixHandshakeStage0(f *Interface, vpnIp iputil.VpnIp, hostinfo *HostInfo) { hsBytes, err = hs.Marshal() if err != nil { - f.l.WithError(err).WithField("vpnIp", vpnIp). + f.l.WithError(err).WithField("vpnIp", hostinfo.vpnIp). WithField("handshake", m{"stage": 0, "style": "ix_psk0"}).Error("Failed to marshal handshake message") - return + return false } h := header.Encode(make([]byte, header.Len), header.Version, header.Handshake, header.HandshakeIXPSK0, 0, 1) @@ -56,9 +49,9 @@ func ixHandshakeStage0(f *Interface, vpnIp iputil.VpnIp, hostinfo *HostInfo) { msg, _, _, err := ci.H.WriteMessage(h, hsBytes) if err != nil { - f.l.WithError(err).WithField("vpnIp", vpnIp). + f.l.WithError(err).WithField("vpnIp", hostinfo.vpnIp). WithField("handshake", m{"stage": 0, "style": "ix_psk0"}).Error("Failed to call noise.WriteMessage") - return + return false } // We are sending handshake packet 1, so we don't expect to receive @@ -68,6 +61,7 @@ func ixHandshakeStage0(f *Interface, vpnIp iputil.VpnIp, hostinfo *HostInfo) { hostinfo.HandshakePacket[0] = msg hostinfo.HandshakeReady = true hostinfo.handshakeStart = time.Now() + return true } func ixHandshakeStage1(f *Interface, addr *udp.Addr, via *ViaSender, packet []byte, h *header.H) { @@ -428,31 +422,27 @@ func ixHandshakeStage2(f *Interface, addr *udp.Addr, via *ViaSender, hostinfo *H f.handshakeManager.DeleteHostInfo(hostinfo) // Create a new hostinfo/handshake for the intended vpn ip - //TODO: this adds it to the timer wheel in a way that aggressively retries - newHostInfo := f.getOrHandshake(hostinfo.vpnIp) - newHostInfo.Lock() - - // Block the current used address - newHostInfo.remotes = hostinfo.remotes - newHostInfo.remotes.BlockRemote(addr) - - // Get the correct remote list for the host we did handshake with - hostinfo.remotes = f.lightHouse.QueryCache(vpnIp) - - f.l.WithField("blockedUdpAddrs", newHostInfo.remotes.CopyBlockedRemotes()).WithField("vpnIp", vpnIp). - WithField("remotes", newHostInfo.remotes.CopyAddrs(f.hostMap.preferredRanges)). - Info("Blocked addresses for handshakes") - - // Swap the packet store to benefit the original intended recipient - hostinfo.ConnectionState.queueLock.Lock() - newHostInfo.packetStore = hostinfo.packetStore - hostinfo.packetStore = []*cachedPacket{} - hostinfo.ConnectionState.queueLock.Unlock() - - // Finally, put the correct vpn ip in the host info, tell them to close the tunnel, and return true to tear down - hostinfo.vpnIp = vpnIp - f.sendCloseTunnel(hostinfo) - newHostInfo.Unlock() + f.handshakeManager.StartHandshake(hostinfo.vpnIp, func(newHostInfo *HostInfo) { + //TODO: this doesnt know if its being added or is being used for caching a packet + // Block the current used address + newHostInfo.remotes = hostinfo.remotes + newHostInfo.remotes.BlockRemote(addr) + + // Get the correct remote list for the host we did handshake with + hostinfo.remotes = f.lightHouse.QueryCache(vpnIp) + + f.l.WithField("blockedUdpAddrs", newHostInfo.remotes.CopyBlockedRemotes()).WithField("vpnIp", vpnIp). + WithField("remotes", newHostInfo.remotes.CopyAddrs(f.hostMap.preferredRanges)). + Info("Blocked addresses for handshakes") + + // Swap the packet store to benefit the original intended recipient + newHostInfo.packetStore = hostinfo.packetStore + hostinfo.packetStore = []*cachedPacket{} + + // Finally, put the correct vpn ip in the host info, tell them to close the tunnel, and return true to tear down + hostinfo.vpnIp = vpnIp + f.sendCloseTunnel(hostinfo) + }) return true } diff --git a/handshake_manager.go b/handshake_manager.go index e15b79433..e2c2cf548 100644 --- a/handshake_manager.go +++ b/handshake_manager.go @@ -57,13 +57,14 @@ type HandshakeManager struct { messageMetrics *MessageMetrics metricInitiated metrics.Counter metricTimedOut metrics.Counter + f *Interface l *logrus.Logger // can be used to trigger outbound handshake for the given vpnIp trigger chan iputil.VpnIp } -func NewHandshakeManager(l *logrus.Logger, tunCidr *net.IPNet, preferredRanges []*net.IPNet, mainHostMap *HostMap, lightHouse *LightHouse, outside udp.Conn, config HandshakeConfig) *HandshakeManager { +func NewHandshakeManager(l *logrus.Logger, mainHostMap *HostMap, lightHouse *LightHouse, outside udp.Conn, config HandshakeConfig) *HandshakeManager { return &HandshakeManager{ vpnIps: map[iputil.VpnIp]*HostInfo{}, indexes: map[uint32]*HostInfo{}, @@ -80,7 +81,7 @@ func NewHandshakeManager(l *logrus.Logger, tunCidr *net.IPNet, preferredRanges [ } } -func (c *HandshakeManager) Run(ctx context.Context, f EncWriter) { +func (c *HandshakeManager) Run(ctx context.Context) { clockSource := time.NewTicker(c.config.tryInterval) defer clockSource.Stop() @@ -89,25 +90,25 @@ func (c *HandshakeManager) Run(ctx context.Context, f EncWriter) { case <-ctx.Done(): return case vpnIP := <-c.trigger: - c.handleOutbound(vpnIP, f, true) + c.handleOutbound(vpnIP, true) case now := <-clockSource.C: - c.NextOutboundHandshakeTimerTick(now, f) + c.NextOutboundHandshakeTimerTick(now) } } } -func (c *HandshakeManager) NextOutboundHandshakeTimerTick(now time.Time, f EncWriter) { +func (c *HandshakeManager) NextOutboundHandshakeTimerTick(now time.Time) { c.OutboundHandshakeTimer.Advance(now) for { vpnIp, has := c.OutboundHandshakeTimer.Purge() if !has { break } - c.handleOutbound(vpnIp, f, false) + c.handleOutbound(vpnIp, false) } } -func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, lighthouseTriggered bool) { +func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, lighthouseTriggered bool) { hostinfo := c.QueryVpnIp(vpnIp) if hostinfo == nil { return @@ -122,14 +123,6 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light return } - // Check if we have a handshake packet to transmit yet - if !hostinfo.HandshakeReady { - // There is currently a slight race in getOrHandshake due to ConnectionState not being part of the HostInfo directly - // Our hostinfo here was added to the pending map and the wheel may have ticked to us before we created ConnectionState - c.OutboundHandshakeTimer.Add(vpnIp, c.config.tryInterval*time.Duration(hostinfo.HandshakeCounter)) - return - } - // If we are out of time, clean up if hostinfo.HandshakeCounter >= c.config.retries { hostinfo.logger(c.l).WithField("udpAddrs", hostinfo.remotes.CopyAddrs(c.mainHostMap.preferredRanges)). @@ -143,6 +136,17 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light return } + // Increment the counter to increase our delay, linear backoff + hostinfo.HandshakeCounter++ + + // Check if we have a handshake packet to transmit yet + if !hostinfo.HandshakeReady { + if !ixHandshakeStage0(c.f, hostinfo) { + c.OutboundHandshakeTimer.Add(vpnIp, c.config.tryInterval*time.Duration(hostinfo.HandshakeCounter)) + return + } + } + // Get a remotes object if we don't already have one. // This is mainly to protect us as this should never be the case // NB ^ This comment doesn't jive. It's how the thing gets initialized. @@ -170,7 +174,7 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light // If we only have 1 remote it is highly likely our query raced with the other host registered within the lighthouse // Our vpnIp here has a tunnel with a lighthouse but has yet to send a host update packet there so we only know about // the learned public ip for them. Query again to short circuit the promotion counter - c.lightHouse.QueryServer(vpnIp, f) + c.lightHouse.QueryServer(vpnIp, c.f) } // Send the handshake to all known ips, stage 2 takes care of assigning the hostinfo.remote based on the first to reply @@ -214,7 +218,7 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light relayHostInfo := c.mainHostMap.QueryVpnIp(*relay) if relayHostInfo == nil || relayHostInfo.remote == nil { hostinfo.logger(c.l).WithField("relay", relay.String()).Info("Establish tunnel to relay target") - f.Handshake(*relay) + c.f.Handshake(*relay) continue } // Check the relay HostInfo to see if we already established a relay through it @@ -222,7 +226,7 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light switch existingRelay.State { case Established: hostinfo.logger(c.l).WithField("relay", relay.String()).Info("Send handshake via relay") - f.SendVia(relayHostInfo, existingRelay, hostinfo.HandshakePacket[0], make([]byte, 12), make([]byte, mtu), false) + c.f.SendVia(relayHostInfo, existingRelay, hostinfo.HandshakePacket[0], make([]byte, 12), make([]byte, mtu), false) case Requested: hostinfo.logger(c.l).WithField("relay", relay.String()).Info("Re-send CreateRelay request") // Re-send the CreateRelay request, in case the previous one was lost. @@ -239,7 +243,7 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light Error("Failed to marshal Control message to create relay") } else { // This must send over the hostinfo, not over hm.Hosts[ip] - f.SendMessageToHostInfo(header.Control, 0, relayHostInfo, msg, make([]byte, 12), make([]byte, mtu)) + c.f.SendMessageToHostInfo(header.Control, 0, relayHostInfo, msg, make([]byte, 12), make([]byte, mtu)) c.l.WithFields(logrus.Fields{ "relayFrom": c.lightHouse.myVpnIp, "relayTo": vpnIp, @@ -274,7 +278,7 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light WithError(err). Error("Failed to marshal Control message to create relay") } else { - f.SendMessageToHostInfo(header.Control, 0, relayHostInfo, msg, make([]byte, 12), make([]byte, mtu)) + c.f.SendMessageToHostInfo(header.Control, 0, relayHostInfo, msg, make([]byte, 12), make([]byte, mtu)) c.l.WithFields(logrus.Fields{ "relayFrom": c.lightHouse.myVpnIp, "relayTo": vpnIp, @@ -287,23 +291,40 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f EncWriter, light } } - // Increment the counter to increase our delay, linear backoff - hostinfo.HandshakeCounter++ - // If a lighthouse triggered this attempt then we are still in the timer wheel and do not need to re-add if !lighthouseTriggered { c.OutboundHandshakeTimer.Add(vpnIp, c.config.tryInterval*time.Duration(hostinfo.HandshakeCounter)) } } -// AddVpnIp will try to handshake with the provided vpn ip and return the hostinfo for it. -func (c *HandshakeManager) AddVpnIp(vpnIp iputil.VpnIp) *HostInfo { - // A write lock is used to avoid having to recheck the map and trading a read lock for a write lock - c.Lock() - defer c.Unlock() +// GetOrHandshake will try to find a hostinfo with a fully formed tunnel or start a new handshake if one is not present +// The 2nd argument will be true if the hostinfo is ready to transmit traffic +func (hm *HandshakeManager) GetOrHandshake(vpnIp iputil.VpnIp, cacheCb func(*HostInfo)) (*HostInfo, bool) { + // Check the main hostmap and maintain a read lock if our host is not there + hm.mainHostMap.RLock() + if h, ok := hm.mainHostMap.Hosts[vpnIp]; ok { + hm.mainHostMap.RUnlock() + // Do not attempt promotion if you are a lighthouse + if !hm.lightHouse.amLighthouse { + h.TryPromoteBest(hm.mainHostMap.preferredRanges, hm.f) + } + return h, true + } + + defer hm.mainHostMap.RUnlock() + return hm.StartHandshake(vpnIp, cacheCb), false +} - if hostinfo, ok := c.vpnIps[vpnIp]; ok { - // We are already tracking this vpn ip +// StartHandshake will ensure a handshake is currently being attempted for the provided vpn ip +func (hm *HandshakeManager) StartHandshake(vpnIp iputil.VpnIp, cacheCb func(*HostInfo)) *HostInfo { + hm.Lock() + defer hm.Unlock() + + if hostinfo, ok := hm.vpnIps[vpnIp]; ok { + // We are already trying to handshake with this vpn ip + if cacheCb != nil { + cacheCb(hostinfo) + } return hostinfo } @@ -317,10 +338,30 @@ func (c *HandshakeManager) AddVpnIp(vpnIp iputil.VpnIp) *HostInfo { }, } - c.vpnIps[vpnIp] = hostinfo - c.metricInitiated.Inc(1) - c.OutboundHandshakeTimer.Add(vpnIp, c.config.tryInterval) + hm.vpnIps[vpnIp] = hostinfo + hm.metricInitiated.Inc(1) + hm.OutboundHandshakeTimer.Add(vpnIp, hm.config.tryInterval) + + if cacheCb != nil { + cacheCb(hostinfo) + } + + // If this is a static host, we don't need to wait for the HostQueryReply + // We can trigger the handshake right now + _, doTrigger := hm.lightHouse.GetStaticHostList()[vpnIp] + if !doTrigger { + // Add any calculated remotes, and trigger early handshake if one found + doTrigger = hm.lightHouse.addCalculatedRemotes(vpnIp) + } + + if doTrigger { + select { + case hm.trigger <- vpnIp: + default: + } + } + hm.lightHouse.QueryServer(vpnIp, hm.f) return hostinfo } @@ -342,10 +383,10 @@ var ( // ErrLocalIndexCollision if we already have an entry in the main or pending // hostmap for the hostinfo.localIndexId. func (c *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket uint8, f *Interface) (*HostInfo, error) { - c.Lock() - defer c.Unlock() c.mainHostMap.Lock() defer c.mainHostMap.Unlock() + c.Lock() + defer c.Unlock() // Check if we already have a tunnel with this vpn ip existingHostInfo, found := c.mainHostMap.Hosts[hostinfo.vpnIp] @@ -396,47 +437,47 @@ func (c *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket // Complete is a simpler version of CheckAndComplete when we already know we // won't have a localIndexId collision because we already have an entry in the // pendingHostMap. An existing hostinfo is returned if there was one. -func (c *HandshakeManager) Complete(hostinfo *HostInfo, f *Interface) { - c.Lock() - defer c.Unlock() - c.mainHostMap.Lock() - defer c.mainHostMap.Unlock() +func (hm *HandshakeManager) Complete(hostinfo *HostInfo, f *Interface) { + hm.mainHostMap.Lock() + defer hm.mainHostMap.Unlock() + hm.Lock() + defer hm.Unlock() - existingRemoteIndex, found := c.mainHostMap.RemoteIndexes[hostinfo.remoteIndexId] + existingRemoteIndex, found := hm.mainHostMap.RemoteIndexes[hostinfo.remoteIndexId] if found && existingRemoteIndex != nil { // We have a collision, but this can happen since we can't control // the remote ID. Just log about the situation as a note. - hostinfo.logger(c.l). + hostinfo.logger(hm.l). WithField("remoteIndex", hostinfo.remoteIndexId).WithField("collision", existingRemoteIndex.vpnIp). Info("New host shadows existing host remoteIndex") } // We need to remove from the pending hostmap first to avoid undoing work when after to the main hostmap. - c.unlockedDeleteHostInfo(hostinfo) - c.mainHostMap.unlockedAddHostInfo(hostinfo, f) + hm.unlockedDeleteHostInfo(hostinfo) + hm.mainHostMap.unlockedAddHostInfo(hostinfo, f) } -// AddIndexHostInfo generates a unique localIndexId for this HostInfo +// allocateIndex generates a unique localIndexId for this HostInfo // and adds it to the pendingHostMap. Will error if we are unable to generate // a unique localIndexId -func (c *HandshakeManager) AddIndexHostInfo(h *HostInfo) error { - c.Lock() - defer c.Unlock() - c.mainHostMap.RLock() - defer c.mainHostMap.RUnlock() +func (hm *HandshakeManager) allocateIndex(h *HostInfo) error { + hm.mainHostMap.RLock() + defer hm.mainHostMap.RUnlock() + hm.Lock() + defer hm.Unlock() for i := 0; i < 32; i++ { - index, err := generateIndex(c.l) + index, err := generateIndex(hm.l) if err != nil { return err } - _, inPending := c.indexes[index] - _, inMain := c.mainHostMap.Indexes[index] + _, inPending := hm.indexes[index] + _, inMain := hm.mainHostMap.Indexes[index] if !inMain && !inPending { h.localIndexId = index - c.indexes[index] = h + hm.indexes[index] = h return nil } } diff --git a/handshake_manager_test.go b/handshake_manager_test.go index c6df37d51..d318a9def 100644 --- a/handshake_manager_test.go +++ b/handshake_manager_test.go @@ -14,22 +14,20 @@ import ( func Test_NewHandshakeManagerVpnIp(t *testing.T) { l := test.NewLogger() - _, tuncidr, _ := net.ParseCIDR("172.1.1.1/24") _, vpncidr, _ := net.ParseCIDR("172.1.1.1/24") _, localrange, _ := net.ParseCIDR("10.1.1.1/24") ip := iputil.Ip2VpnIp(net.ParseIP("172.1.1.2")) preferredRanges := []*net.IPNet{localrange} - mw := &mockEncWriter{} mainHM := NewHostMap(l, vpncidr, preferredRanges) lh := newTestLighthouse() - blah := NewHandshakeManager(l, tuncidr, preferredRanges, mainHM, lh, &udp.NoopConn{}, defaultHandshakeConfig) + blah := NewHandshakeManager(l, mainHM, lh, &udp.NoopConn{}, defaultHandshakeConfig) now := time.Now() - blah.NextOutboundHandshakeTimerTick(now, mw) + blah.NextOutboundHandshakeTimerTick(now) - i := blah.AddVpnIp(ip) - i2 := blah.AddVpnIp(ip) + i := blah.StartHandshake(ip, nil) + i2 := blah.StartHandshake(ip, nil) assert.Same(t, i, i2) i.remotes = NewRemoteList(nil) @@ -44,14 +42,14 @@ func Test_NewHandshakeManagerVpnIp(t *testing.T) { // Jump ahead `HandshakeRetries` ticks, offset by one to get the sleep logic right for i := 1; i <= DefaultHandshakeRetries+1; i++ { now = now.Add(time.Duration(i) * DefaultHandshakeTryInterval) - blah.NextOutboundHandshakeTimerTick(now, mw) + blah.NextOutboundHandshakeTimerTick(now) } // Confirm they are still in the pending index list assert.Contains(t, blah.vpnIps, ip) // Tick 1 more time, a minute will certainly flush it out - blah.NextOutboundHandshakeTimerTick(now.Add(time.Minute), mw) + blah.NextOutboundHandshakeTimerTick(now.Add(time.Minute)) // Confirm they have been removed assert.NotContains(t, blah.vpnIps, ip) diff --git a/hostmap.go b/hostmap.go index 829c7c0bb..f2618c7dc 100644 --- a/hostmap.go +++ b/hostmap.go @@ -456,12 +456,6 @@ func (hm *HostMap) QueryVpnIpRelayFor(targetIp, relayHostIp iputil.VpnIp) (*Host return nil, nil, errors.New("unable to find host with relay") } -// PromoteBestQueryVpnIp will attempt to lazily switch to the best remote every -// `PromoteEvery` calls to this function for a given host. -func (hm *HostMap) PromoteBestQueryVpnIp(vpnIp iputil.VpnIp, ifce *Interface) *HostInfo { - return hm.queryVpnIp(vpnIp, ifce) -} - func (hm *HostMap) queryVpnIp(vpnIp iputil.VpnIp, promoteIfce *Interface) *HostInfo { hm.RLock() if h, ok := hm.Hosts[vpnIp]; ok { @@ -579,7 +573,7 @@ func (i *HostInfo) TryPromoteBest(preferredRanges []*net.IPNet, ifce *Interface) } } -func (i *HostInfo) cachePacket(l *logrus.Logger, t header.MessageType, st header.MessageSubType, packet []byte, f packetCallback, m *cachedPacketMetrics) { +func (i *HostInfo) unlockedCachePacket(l *logrus.Logger, t header.MessageType, st header.MessageSubType, packet []byte, f packetCallback, m *cachedPacketMetrics) { //TODO: return the error so we can log with more context if len(i.packetStore) < 100 { tempPacket := make([]byte, len(packet)) @@ -608,7 +602,6 @@ func (i *HostInfo) handshakeComplete(l *logrus.Logger, m *cachedPacketMetrics) { //TODO: HandshakeComplete means send stored packets and ConnectionState.ready means we are ready to send //TODO: if the transition from HandhsakeComplete to ConnectionState.ready happens all within this function they are identical - i.ConnectionState.queueLock.Lock() i.HandshakeComplete = true //TODO: this should be managed by the handshake state machine to set it based on how many handshake were seen. // Clamping it to 2 gets us out of the woods for now @@ -630,7 +623,6 @@ func (i *HostInfo) handshakeComplete(l *logrus.Logger, m *cachedPacketMetrics) { i.remotes.ResetBlockedRemotes() i.packetStore = make([]*cachedPacket, 0) i.ConnectionState.ready = true - i.ConnectionState.queueLock.Unlock() } func (i *HostInfo) GetCert() *cert.NebulaCertificate { diff --git a/inside.go b/inside.go index 6a0e078ab..2219d2bd6 100644 --- a/inside.go +++ b/inside.go @@ -44,7 +44,10 @@ func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet return } - hostinfo := f.getOrHandshake(fwPacket.RemoteIP) + hostinfo, ready := f.getOrHandshake(fwPacket.RemoteIP, func(h *HostInfo) { + h.unlockedCachePacket(f.l, header.Message, 0, packet, f.sendMessageNow, f.cachedPacketMetrics) + }) + if hostinfo == nil { f.rejectInside(packet, out, q) if f.l.Level >= logrus.DebugLevel { @@ -54,23 +57,14 @@ func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet } return } - ci := hostinfo.ConnectionState - - if !ci.ready { - // Because we might be sending stored packets, lock here to stop new things going to - // the packet queue. - ci.queueLock.Lock() - if !ci.ready { - hostinfo.cachePacket(f.l, header.Message, 0, packet, f.sendMessageNow, f.cachedPacketMetrics) - ci.queueLock.Unlock() - return - } - ci.queueLock.Unlock() + + if !ready { + return } dropReason := f.firewall.Drop(packet, *fwPacket, false, hostinfo, f.pki.GetCAPool(), localCache) if dropReason == nil { - f.sendNoMetrics(header.Message, 0, ci, hostinfo, nil, packet, nb, out, q) + f.sendNoMetrics(header.Message, 0, hostinfo.ConnectionState, hostinfo, nil, packet, nb, out, q) } else { f.rejectInside(packet, out, q) @@ -109,62 +103,20 @@ func (f *Interface) rejectOutside(packet []byte, ci *ConnectionState, hostinfo * } func (f *Interface) Handshake(vpnIp iputil.VpnIp) { - f.getOrHandshake(vpnIp) + f.getOrHandshake(vpnIp, nil) } -// getOrHandshake returns nil if the vpnIp is not routable -func (f *Interface) getOrHandshake(vpnIp iputil.VpnIp) *HostInfo { +// getOrHandshake returns nil if the vpnIp is not routable. +// If the 2nd return var is false then the hostinfo is not ready to be used in a tunnel +func (f *Interface) getOrHandshake(vpnIp iputil.VpnIp, cacheCallback func(info *HostInfo)) (*HostInfo, bool) { if !ipMaskContains(f.lightHouse.myVpnIp, f.lightHouse.myVpnZeros, vpnIp) { vpnIp = f.inside.RouteFor(vpnIp) if vpnIp == 0 { - return nil + return nil, false } } - hostinfo := f.hostMap.PromoteBestQueryVpnIp(vpnIp, f) - if hostinfo == nil { - hostinfo = f.handshakeManager.AddVpnIp(vpnIp) - } - ci := hostinfo.ConnectionState - - if ci != nil && ci.eKey != nil && ci.ready { - return hostinfo - } - - // Handshake is not ready, we need to grab the lock now before we start the handshake process - //TODO: move this to handshake manager - hostinfo.Lock() - defer hostinfo.Unlock() - - // Double check, now that we have the lock - ci = hostinfo.ConnectionState - if ci != nil && ci.eKey != nil && ci.ready { - return hostinfo - } - - // If we have already created the handshake packet, we don't want to call the function at all. - if !hostinfo.HandshakeReady { - ixHandshakeStage0(f, vpnIp, hostinfo) - // FIXME: Maybe make XX selectable, but probably not since psk makes it nearly pointless for us. - //xx_handshakeStage0(f, ip, hostinfo) - - // If this is a static host, we don't need to wait for the HostQueryReply - // We can trigger the handshake right now - _, doTrigger := f.lightHouse.GetStaticHostList()[vpnIp] - if !doTrigger { - // Add any calculated remotes, and trigger early handshake if one found - doTrigger = f.lightHouse.addCalculatedRemotes(vpnIp) - } - - if doTrigger { - select { - case f.handshakeManager.trigger <- vpnIp: - default: - } - } - } - - return hostinfo + return f.handshakeManager.GetOrHandshake(vpnIp, cacheCallback) } func (f *Interface) sendMessageNow(t header.MessageType, st header.MessageSubType, hostinfo *HostInfo, p, nb, out []byte) { @@ -191,7 +143,10 @@ func (f *Interface) sendMessageNow(t header.MessageType, st header.MessageSubTyp // SendMessageToVpnIp handles real ip:port lookup and sends to the current best known address for vpnIp func (f *Interface) SendMessageToVpnIp(t header.MessageType, st header.MessageSubType, vpnIp iputil.VpnIp, p, nb, out []byte) { - hostInfo := f.getOrHandshake(vpnIp) + hostInfo, ready := f.getOrHandshake(vpnIp, func(h *HostInfo) { + h.unlockedCachePacket(f.l, t, st, p, f.SendMessageToHostInfo, f.cachedPacketMetrics) + }) + if hostInfo == nil { if f.l.Level >= logrus.DebugLevel { f.l.WithField("vpnIp", vpnIp). @@ -200,16 +155,8 @@ func (f *Interface) SendMessageToVpnIp(t header.MessageType, st header.MessageSu return } - if !hostInfo.ConnectionState.ready { - // Because we might be sending stored packets, lock here to stop new things going to - // the packet queue. - hostInfo.ConnectionState.queueLock.Lock() - if !hostInfo.ConnectionState.ready { - hostInfo.cachePacket(f.l, t, st, p, f.SendMessageToHostInfo, f.cachedPacketMetrics) - hostInfo.ConnectionState.queueLock.Unlock() - return - } - hostInfo.ConnectionState.queueLock.Unlock() + if !ready { + return } f.SendMessageToHostInfo(t, st, hostInfo, p, nb, out) @@ -229,7 +176,7 @@ func (f *Interface) sendTo(t header.MessageType, st header.MessageSubType, ci *C f.sendNoMetrics(t, st, ci, hostinfo, remote, p, nb, out, 0) } -// sendVia sends a payload through a Relay tunnel. No authentication or encryption is done +// SendVia sends a payload through a Relay tunnel. No authentication or encryption is done // to the payload for the ultimate target host, making this a useful method for sending // handshake messages to peers through relay tunnels. // via is the HostInfo through which the message is relayed. diff --git a/main.go b/main.go index 4e8448b84..883e562d6 100644 --- a/main.go +++ b/main.go @@ -235,7 +235,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg messageMetrics: messageMetrics, } - handshakeManager := NewHandshakeManager(l, tunCidr, preferredRanges, hostMap, lightHouse, udpConns[0], handshakeConfig) + handshakeManager := NewHandshakeManager(l, hostMap, lightHouse, udpConns[0], handshakeConfig) lightHouse.handshakeTrigger = handshakeManager.trigger serveDns := false @@ -302,7 +302,8 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg ifce.RegisterConfigChangeCallbacks(c) ifce.reloadSendRecvError(c) - go handshakeManager.Run(ctx, ifce) + handshakeManager.f = ifce + go handshakeManager.Run(ctx) } // TODO - stats third-party modules start uncancellable goroutines. Update those libs to accept diff --git a/relay_manager.go b/relay_manager.go index 8f6365293..224135362 100644 --- a/relay_manager.go +++ b/relay_manager.go @@ -244,7 +244,7 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N if peer == nil { // Try to establish a connection to this host. If we get a future relay request, // we'll be ready! - f.getOrHandshake(target) + f.Handshake(target) return } if peer.remote == nil { diff --git a/ssh.go b/ssh.go index c68e0820b..30f9aea3d 100644 --- a/ssh.go +++ b/ssh.go @@ -607,11 +607,10 @@ func sshCreateTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringW } } - hostInfo = ifce.handshakeManager.AddVpnIp(vpnIp) + hostInfo = ifce.handshakeManager.StartHandshake(vpnIp, nil) if addr != nil { hostInfo.SetRemote(addr) } - ifce.getOrHandshake(vpnIp) return w.WriteLine("Created") } From 06b480e17751eb5d719ec85cb34e1abd51280a64 Mon Sep 17 00:00:00 2001 From: brad-defined <77982333+brad-defined@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:29:27 -0400 Subject: [PATCH 39/52] Fix relay migration (#964) * Fix for relay migration on rehandshaking issue. On rehandshake, the relay tunnel doesn't migrate to the new hostinfo object correctly, due to an incorrect Nebula IP sent in the CreateRelayRequest message. * Add a test for this case --------- Co-authored-by: Nate Brown --- connection_manager.go | 4 +- e2e/handshakes_test.go | 104 +++++++++++++++++++++++++++++++++++++++++ relay_manager.go | 6 +++ 3 files changed, 112 insertions(+), 2 deletions(-) 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/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) From 790268a1766124af68a4ced3c31462b78632c7a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 11:42:08 -0400 Subject: [PATCH 40/52] Bump golang.org/x/sys from 0.11.0 to 0.12.0 (#968) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.11.0 to 0.12.0. - [Commits](https://github.com/golang/sys/compare/v0.11.0...v0.12.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5dcdcade4..245761b45 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( golang.org/x/crypto v0.12.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/sys v0.12.0 golang.org/x/term v0.11.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b diff --git a/go.sum b/go.sum index f0fd4b851..0afb920f0 100644 --- a/go.sum +++ b/go.sum @@ -194,8 +194,8 @@ 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.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.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= From eea5e6a5df2821fb74b66674ee3a81ddc3d55330 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 11:43:56 -0400 Subject: [PATCH 41/52] Bump actions/checkout from 3 to 4 (#969) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gofmt.yml | 2 +- .github/workflows/release.yml | 8 ++++---- .github/workflows/smoke.yml | 2 +- .github/workflows/test.yml | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) 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/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: From d271df8da8742f2e40722290478890e975a06394 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 12:47:55 -0400 Subject: [PATCH 42/52] Bump golang.org/x/term from 0.11.0 to 0.12.0 (#967) Bumps [golang.org/x/term](https://github.com/golang/term) from 0.11.0 to 0.12.0. - [Commits](https://github.com/golang/term/compare/v0.11.0...v0.12.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 245761b45..86e7998f8 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/net v0.14.0 golang.org/x/sys v0.12.0 - golang.org/x/term v0.11.0 + golang.org/x/term v0.12.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 diff --git a/go.sum b/go.sum index 0afb920f0..13db6d95a 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.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.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 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= From f7e392995ad2fec6d67c2a522e72b4d0848aa799 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Thu, 7 Sep 2023 11:56:09 -0500 Subject: [PATCH 43/52] Fix rebind to not put the socket in blocking mode (#972) --- udp/udp_darwin.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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") + } + }) } From dbdb48f1824694270a9ad732a10f7f4bea36c28d Mon Sep 17 00:00:00 2001 From: Lars Lehtonen Date: Thu, 7 Sep 2023 10:54:01 -0700 Subject: [PATCH 44/52] cert: fix dropped errors (#961) --- cert/cert.go | 3 +++ cert/crypto.go | 3 +++ 2 files changed, 6 insertions(+) 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 { From 280fa026ead8693abb6f04fa687920fda19a57df Mon Sep 17 00:00:00 2001 From: Wade Simmons Date: Thu, 7 Sep 2023 13:57:41 -0400 Subject: [PATCH 45/52] smoke-test: don't assume docker needs sudo (#958) Let the host deal with this detail if necessary --- .github/workflows/smoke/build-relay.sh | 2 +- .github/workflows/smoke/build.sh | 2 +- .github/workflows/smoke/smoke-relay.sh | 50 +++++++------- .github/workflows/smoke/smoke.sh | 92 +++++++++++++------------- 4 files changed, 73 insertions(+), 73 deletions(-) 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)" ] From 282ca4368ee19e0c56f080c0cb536dbea9f2c04a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 09:47:00 -0400 Subject: [PATCH 46/52] Bump golang.org/x/crypto from 0.12.0 to 0.13.0 (#976) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.12.0 to 0.13.0. - [Commits](https://github.com/golang/crypto/compare/v0.12.0...v0.13.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 86e7998f8..1e6a45417 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ 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.13.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/net v0.14.0 golang.org/x/sys v0.12.0 diff --git a/go.sum b/go.sum index 13db6d95a..2a584758e 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,8 @@ 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.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 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= From e3fbfbfd4d2ade5433d683e9c80e4bc0a9bc2253 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 09:47:45 -0400 Subject: [PATCH 47/52] Bump golang.org/x/net from 0.14.0 to 0.15.0 (#977) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.14.0 to 0.15.0. - [Commits](https://github.com/golang/net/compare/v0.14.0...v0.15.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1e6a45417..8514ad386 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.13.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 - golang.org/x/net v0.14.0 + golang.org/x/net v0.15.0 golang.org/x/sys v0.12.0 golang.org/x/term v0.12.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 diff --git a/go.sum b/go.sum index 2a584758e..644990085 100644 --- a/go.sum +++ b/go.sum @@ -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.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 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= From c289c7a7ca36976f7b8056ac2d8cf32272f1d99f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 09:48:26 -0400 Subject: [PATCH 48/52] Bump github.com/miekg/dns from 1.1.55 to 1.1.56 (#979) Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.55 to 1.1.56. - [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release) - [Commits](https://github.com/miekg/dns/compare/v1.1.55...v1.1.56) --- updated-dependencies: - dependency-name: github.com/miekg/dns dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 8514ad386..a44e5ec12 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 @@ -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 644990085..e065fad67 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= @@ -156,8 +156,8 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI 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= @@ -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= @@ -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= From 5fccbb8676b7e0975b13117046ca925ec74e298e Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Mon, 16 Oct 2023 10:06:43 -0500 Subject: [PATCH 49/52] Retry wintun creation (#985) --- overlay/tun_wintun_windows.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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) From e78fe0b9ef3b84ecd4c7f682730a36115bfcc0e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:28:59 -0400 Subject: [PATCH 50/52] Bump golang.org/x/net from 0.15.0 to 0.17.0 (#990) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.17.0. - [Commits](https://github.com/golang/net/compare/v0.15.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index a44e5ec12..ba57aa12f 100644 --- a/go.mod +++ b/go.mod @@ -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.13.0 + golang.org/x/crypto v0.14.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 - golang.org/x/net v0.15.0 - golang.org/x/sys v0.12.0 - golang.org/x/term v0.12.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 diff --git a/go.sum b/go.sum index e065fad67..445f18a65 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,8 @@ 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.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +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= @@ -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.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +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= @@ -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.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.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.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +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= From 50d6a1e8cae1fa60668f62405e2020e990706cf7 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Tue, 17 Oct 2023 15:43:51 -0500 Subject: [PATCH 51/52] QueryServer needs to be done outside of the lock (#996) --- handshake_manager.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/handshake_manager.go b/handshake_manager.go index e2c2cf548..11c0c6f83 100644 --- a/handshake_manager.go +++ b/handshake_manager.go @@ -318,13 +318,13 @@ func (hm *HandshakeManager) GetOrHandshake(vpnIp iputil.VpnIp, cacheCb func(*Hos // StartHandshake will ensure a handshake is currently being attempted for the provided vpn ip func (hm *HandshakeManager) StartHandshake(vpnIp iputil.VpnIp, cacheCb func(*HostInfo)) *HostInfo { hm.Lock() - defer hm.Unlock() if hostinfo, ok := hm.vpnIps[vpnIp]; ok { // We are already trying to handshake with this vpn ip if cacheCb != nil { cacheCb(hostinfo) } + hm.Unlock() return hostinfo } @@ -361,6 +361,7 @@ func (hm *HandshakeManager) StartHandshake(vpnIp iputil.VpnIp, cacheCb func(*Hos } } + hm.Unlock() hm.lightHouse.QueryServer(vpnIp, hm.f) return hostinfo } From 87b628ba241c2d519151496c2870bba68cbaa33c Mon Sep 17 00:00:00 2001 From: John Maguire Date: Fri, 27 Oct 2023 08:39:34 -0400 Subject: [PATCH 52/52] Fix truncated comment in config.yml (#999) --- examples/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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