mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-15 10:36:57 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cdb94b2a0 |
@@ -222,14 +222,11 @@ test-cov-html:
|
|||||||
go test -coverprofile=coverage.out
|
go test -coverprofile=coverage.out
|
||||||
go tool cover -html=coverage.out
|
go tool cover -html=coverage.out
|
||||||
|
|
||||||
# The package builds only compile. The final line links an android binary so a linker-only failure,
|
|
||||||
# such as the //go:linkname reference anet makes, cannot pass CI.
|
|
||||||
build-test-mobile:
|
build-test-mobile:
|
||||||
GOARCH=amd64 GOOS=ios go build $(shell go list ./... | grep -v '/cmd/\|/examples/')
|
GOARCH=amd64 GOOS=ios go build $(shell go list ./... | grep -v '/cmd/\|/examples/')
|
||||||
GOARCH=arm64 GOOS=ios go build $(shell go list ./... | grep -v '/cmd/\|/examples/')
|
GOARCH=arm64 GOOS=ios go build $(shell go list ./... | grep -v '/cmd/\|/examples/')
|
||||||
GOARCH=amd64 GOOS=android go build $(shell go list ./... | grep -v '/cmd/\|/examples/')
|
GOARCH=amd64 GOOS=android go build $(shell go list ./... | grep -v '/cmd/\|/examples/')
|
||||||
GOARCH=arm64 GOOS=android go build $(shell go list ./... | grep -v '/cmd/\|/examples/')
|
GOARCH=arm64 GOOS=android go build $(shell go list ./... | grep -v '/cmd/\|/examples/')
|
||||||
GOARCH=arm64 GOOS=android go build -ldflags=-checklinkname=0 -o /dev/null ${NEBULA_CMD_PATH}
|
|
||||||
|
|
||||||
bench:
|
bench:
|
||||||
go test -bench=.
|
go test -bench=.
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ require (
|
|||||||
filippo.io/bigmod v0.1.0
|
filippo.io/bigmod v0.1.0
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||||
github.com/armon/go-radix v1.0.0
|
github.com/armon/go-radix v1.0.0
|
||||||
github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432
|
|
||||||
github.com/flynn/noise v1.1.0
|
github.com/flynn/noise v1.1.0
|
||||||
github.com/gaissmai/bart v0.28.0
|
github.com/gaissmai/bart v0.28.0
|
||||||
github.com/gogo/protobuf v1.3.2
|
github.com/gogo/protobuf v1.3.2
|
||||||
@@ -22,7 +21,6 @@ require (
|
|||||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6
|
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/vishvananda/netlink v1.3.1
|
github.com/vishvananda/netlink v1.3.1
|
||||||
github.com/wlynxg/anet v0.0.5
|
|
||||||
go.uber.org/goleak v1.3.0
|
go.uber.org/goleak v1.3.0
|
||||||
go.yaml.in/yaml/v3 v3.0.4
|
go.yaml.in/yaml/v3 v3.0.4
|
||||||
golang.org/x/crypto v0.54.0
|
golang.org/x/crypto v0.54.0
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432 h1:M5QgkYacWj0Xs8MhpIK/5uwU02icXpEoSo9sM2aRCps=
|
|
||||||
github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432/go.mod h1:xwIwAxMvYnVrGJPe2FKx5prTrnAjGOD8zvDOnxnrrkM=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -149,8 +147,6 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
|||||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
|
||||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
|||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
package nebula
|
||||||
|
|
||||||
|
// This file is a trimmed, inlined copy of the graphite exporter from
|
||||||
|
// github.com/cyberdelia/go-metrics-graphite, retaining only the Config type and
|
||||||
|
// the Once entrypoint that Nebula uses. The upstream package has been
|
||||||
|
// unmaintained for 10+ years, so it was vendored here to drop the dependency.
|
||||||
|
// See https://github.com/slackhq/nebula/issues/1831.
|
||||||
|
//
|
||||||
|
// Copyright 2015 Timothée Peignier. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rcrowley/go-metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
// graphiteConfigExport provides a container with configuration parameters for
|
||||||
|
// the Graphite exporter.
|
||||||
|
type graphiteConfigExport struct {
|
||||||
|
Addr *net.TCPAddr // Network address to connect to
|
||||||
|
Registry metrics.Registry // Registry to be exported
|
||||||
|
FlushInterval time.Duration // Flush interval
|
||||||
|
DurationUnit time.Duration // Time conversion unit for durations
|
||||||
|
Prefix string // Prefix to be prepended to metric names
|
||||||
|
Percentiles []float64 // Percentiles to export from timers and histograms
|
||||||
|
}
|
||||||
|
|
||||||
|
// graphiteOnce performs a single submission to Graphite, returning a non-nil
|
||||||
|
// error on failed connections.
|
||||||
|
func graphiteOnce(c graphiteConfigExport) error {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
du := float64(c.DurationUnit)
|
||||||
|
flushSeconds := float64(c.FlushInterval) / float64(time.Second)
|
||||||
|
conn, err := net.DialTCP("tcp", nil, c.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
w := bufio.NewWriter(conn)
|
||||||
|
c.Registry.Each(func(name string, i any) {
|
||||||
|
switch metric := i.(type) {
|
||||||
|
case metrics.Counter:
|
||||||
|
count := metric.Count()
|
||||||
|
fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, count, now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.count_ps %.2f %d\n", c.Prefix, name, float64(count)/flushSeconds, now)
|
||||||
|
case metrics.Gauge:
|
||||||
|
fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now)
|
||||||
|
case metrics.GaugeFloat64:
|
||||||
|
fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now)
|
||||||
|
case metrics.Histogram:
|
||||||
|
h := metric.Snapshot()
|
||||||
|
ps := h.Percentiles(c.Percentiles)
|
||||||
|
fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now)
|
||||||
|
for psIdx, psKey := range c.Percentiles {
|
||||||
|
key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
|
||||||
|
fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
|
||||||
|
}
|
||||||
|
case metrics.Meter:
|
||||||
|
m := metric.Snapshot()
|
||||||
|
fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now)
|
||||||
|
case metrics.Timer:
|
||||||
|
t := metric.Snapshot()
|
||||||
|
ps := t.Percentiles(c.Percentiles)
|
||||||
|
count := t.Count()
|
||||||
|
fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, count, now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.count_ps %.2f %d\n", c.Prefix, name, float64(count)/flushSeconds, now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now)
|
||||||
|
for psIdx, psKey := range c.Percentiles {
|
||||||
|
key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
|
||||||
|
fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx]/du, now)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now)
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+4
-27
@@ -868,28 +868,10 @@ func (i *HostInfo) logger(l *slog.Logger) *slog.Logger {
|
|||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
|
|
||||||
func localAddrs(l *slog.Logger, allowList *LocalAllowList) ([]netip.Addr, error) {
|
func localAddrs(l *slog.Logger, allowList *LocalAllowList) []netip.Addr {
|
||||||
return collectLocalAddrs(l, allowList, localInterfaces, localInterfaceAddrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// collectLocalAddrs takes its enumerators as arguments so tests can drive the filtering and the
|
|
||||||
// failure branches without depending on the addresses of whatever host they run on. It reports
|
|
||||||
// failures to the caller rather than logging them, because it runs on every lighthouse update and
|
|
||||||
// only the caller can tell a new failure from a repeat of the same one.
|
|
||||||
func collectLocalAddrs(
|
|
||||||
l *slog.Logger,
|
|
||||||
allowList *LocalAllowList,
|
|
||||||
interfaces func() ([]net.Interface, error),
|
|
||||||
interfaceAddrs func(*net.Interface) ([]net.Addr, error),
|
|
||||||
) ([]netip.Addr, error) {
|
|
||||||
//FIXME: This function is pretty garbage
|
//FIXME: This function is pretty garbage
|
||||||
var finalAddrs []netip.Addr
|
var finalAddrs []netip.Addr
|
||||||
var errs []error
|
ifaces, _ := net.Interfaces()
|
||||||
ifaces, err := interfaces()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to enumerate local interfaces: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range ifaces {
|
for _, i := range ifaces {
|
||||||
allow := allowList.AllowName(i.Name)
|
allow := allowList.AllowName(i.Name)
|
||||||
if l.Enabled(context.Background(), logging.LevelTrace) {
|
if l.Enabled(context.Background(), logging.LevelTrace) {
|
||||||
@@ -902,12 +884,7 @@ func collectLocalAddrs(
|
|||||||
if !allow {
|
if !allow {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
addrs, err := interfaceAddrs(&i)
|
addrs, _ := i.Addrs()
|
||||||
if err != nil {
|
|
||||||
errs = append(errs, fmt.Errorf("failed to get addresses for %s: %w", i.Name, err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rawAddr := range addrs {
|
for _, rawAddr := range addrs {
|
||||||
var addr netip.Addr
|
var addr netip.Addr
|
||||||
switch v := rawAddr.(type) {
|
switch v := rawAddr.(type) {
|
||||||
@@ -942,5 +919,5 @@ func collectLocalAddrs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return finalAddrs, errors.Join(errs...)
|
return finalAddrs
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package nebula
|
package nebula
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -403,83 +401,3 @@ func TestHostMap_RelayState(t *testing.T) {
|
|||||||
assert.Equal(t, []netip.Addr{}, h1.relayState.relays)
|
assert.Equal(t, []netip.Addr{}, h1.relayState.relays)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCollectLocalAddrs(t *testing.T) {
|
|
||||||
ifaces := []net.Interface{
|
|
||||||
{Index: 1, Name: "lo"},
|
|
||||||
{Index: 2, Name: "eth0"},
|
|
||||||
{Index: 3, Name: "docker0"},
|
|
||||||
}
|
|
||||||
addrs := map[string][]net.Addr{
|
|
||||||
"lo": {
|
|
||||||
&net.IPNet{IP: net.ParseIP("127.0.0.1"), Mask: net.CIDRMask(8, 32)},
|
|
||||||
&net.IPNet{IP: net.ParseIP("::1"), Mask: net.CIDRMask(128, 128)},
|
|
||||||
},
|
|
||||||
"eth0": {
|
|
||||||
&net.IPNet{IP: net.ParseIP("10.0.0.5"), Mask: net.CIDRMask(24, 32)},
|
|
||||||
&net.IPNet{IP: net.ParseIP("fe80::1"), Mask: net.CIDRMask(64, 128)},
|
|
||||||
&net.IPAddr{IP: net.ParseIP("fd00::5")},
|
|
||||||
},
|
|
||||||
"docker0": {
|
|
||||||
&net.IPNet{IP: net.ParseIP("172.17.0.1"), Mask: net.CIDRMask(16, 32)},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
enumerate := func() ([]net.Interface, error) { return ifaces, nil }
|
|
||||||
addrsFor := func(i *net.Interface) ([]net.Addr, error) { return addrs[i.Name], nil }
|
|
||||||
|
|
||||||
// Loopback and link local are dropped, everything else on every interface is kept.
|
|
||||||
out, err := collectLocalAddrs(test.NewLogger(), nil, enumerate, addrsFor)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, []netip.Addr{
|
|
||||||
netip.MustParseAddr("10.0.0.5"),
|
|
||||||
netip.MustParseAddr("fd00::5"),
|
|
||||||
netip.MustParseAddr("172.17.0.1"),
|
|
||||||
}, out)
|
|
||||||
|
|
||||||
// An interface the allow list rejects by name is never asked for its addresses.
|
|
||||||
c := config.NewC(test.NewLogger())
|
|
||||||
c.Settings["allowlist"] = map[string]any{
|
|
||||||
"interfaces": map[string]any{`docker.*`: false},
|
|
||||||
}
|
|
||||||
al, err := NewLocalAllowListFromConfig(c, "allowlist")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
asked := make(map[string]struct{})
|
|
||||||
countingAddrsFor := func(i *net.Interface) ([]net.Addr, error) {
|
|
||||||
asked[i.Name] = struct{}{}
|
|
||||||
return addrs[i.Name], nil
|
|
||||||
}
|
|
||||||
out, err = collectLocalAddrs(test.NewLogger(), al, enumerate, countingAddrsFor)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, []netip.Addr{
|
|
||||||
netip.MustParseAddr("10.0.0.5"),
|
|
||||||
netip.MustParseAddr("fd00::5"),
|
|
||||||
}, out)
|
|
||||||
assert.NotContains(t, asked, "docker0")
|
|
||||||
|
|
||||||
// A failure to enumerate interfaces at all is reported rather than silently advertising nothing.
|
|
||||||
out, err = collectLocalAddrs(
|
|
||||||
test.NewLogger(),
|
|
||||||
nil,
|
|
||||||
func() ([]net.Interface, error) { return nil, errors.New("netlinkrib: permission denied") },
|
|
||||||
addrsFor,
|
|
||||||
)
|
|
||||||
assert.Nil(t, out)
|
|
||||||
require.EqualError(t, err, "failed to enumerate local interfaces: netlinkrib: permission denied")
|
|
||||||
|
|
||||||
// One interface failing is reported and skipped, the rest are still collected.
|
|
||||||
out, err = collectLocalAddrs(
|
|
||||||
test.NewLogger(),
|
|
||||||
nil,
|
|
||||||
enumerate,
|
|
||||||
func(i *net.Interface) ([]net.Addr, error) {
|
|
||||||
if i.Name == "eth0" {
|
|
||||||
return nil, errors.New("nope")
|
|
||||||
}
|
|
||||||
return addrs[i.Name], nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert.Equal(t, []netip.Addr{netip.MustParseAddr("172.17.0.1")}, out)
|
|
||||||
require.EqualError(t, err, "failed to get addresses for eth0: nope")
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-27
@@ -40,10 +40,6 @@ type LightHouse struct {
|
|||||||
// addresses rather than whatever this machine's NICs happen to be. Set it before Start.
|
// addresses rather than whatever this machine's NICs happen to be. Set it before Start.
|
||||||
localAddrsFn func(*LocalAllowList) []netip.Addr
|
localAddrsFn func(*LocalAllowList) []netip.Addr
|
||||||
|
|
||||||
// lastLocalAddrsErr is the previous localAddrsFn failure. Enumeration runs on every update, so an
|
|
||||||
// unchanged failure is demoted to Debug rather than warning every lighthouse.interval forever.
|
|
||||||
lastLocalAddrsErr atomic.Pointer[string]
|
|
||||||
|
|
||||||
// Local cache of answers from light houses
|
// Local cache of answers from light houses
|
||||||
// map of vpn addr to answers
|
// map of vpn addr to answers
|
||||||
addrMap map[netip.Addr]*RemoteList
|
addrMap map[netip.Addr]*RemoteList
|
||||||
@@ -116,9 +112,7 @@ func NewLightHouseFromConfig(ctx context.Context, l *slog.Logger, c *config.C, c
|
|||||||
l: l,
|
l: l,
|
||||||
}
|
}
|
||||||
h.localAddrsFn = func(al *LocalAllowList) []netip.Addr {
|
h.localAddrsFn = func(al *LocalAllowList) []netip.Addr {
|
||||||
addrs, err := localAddrs(h.l, al)
|
return localAddrs(h.l, al)
|
||||||
h.logLocalAddrsErr(err)
|
|
||||||
return addrs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lighthouses := make([]netip.Addr, 0)
|
lighthouses := make([]netip.Addr, 0)
|
||||||
@@ -919,26 +913,6 @@ func (lh *LightHouse) TriggerUpdate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// logLocalAddrsErr reports a localAddrs failure at Warn the first time it is seen and at Debug while
|
|
||||||
// it persists unchanged, so a permanent failure does not warn on every update forever.
|
|
||||||
func (lh *LightHouse) logLocalAddrsErr(err error) {
|
|
||||||
if err == nil {
|
|
||||||
lh.lastLocalAddrsErr.Store(nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := err.Error()
|
|
||||||
prev := lh.lastLocalAddrsErr.Swap(&msg)
|
|
||||||
if prev != nil && *prev == msg {
|
|
||||||
if lh.l.Enabled(context.Background(), slog.LevelDebug) {
|
|
||||||
lh.l.Debug("Failed to collect local addresses to advertise", "error", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
lh.l.Warn("Failed to collect local addresses to advertise", "error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lh *LightHouse) SendUpdate() {
|
func (lh *LightHouse) SendUpdate() {
|
||||||
var v4 []*V4AddrPort
|
var v4 []*V4AddrPort
|
||||||
var v6 []*V6AddrPort
|
var v6 []*V6AddrPort
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package nebula
|
package nebula
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -740,32 +738,3 @@ func TestLighthouse_DeletesWork(t *testing.T) {
|
|||||||
out = lh.Query(testHost)
|
out = lh.Query(testHost)
|
||||||
assert.Nil(t, out)
|
assert.Nil(t, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLightHouse_logLocalAddrsErr(t *testing.T) {
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
lh := &LightHouse{l: test.NewLoggerWithOutput(out)}
|
|
||||||
|
|
||||||
// The first sighting of a failure warns.
|
|
||||||
lh.logLocalAddrsErr(errors.New("permission denied"))
|
|
||||||
assert.Contains(t, out.String(), "level=WARN")
|
|
||||||
assert.Contains(t, out.String(), "permission denied")
|
|
||||||
|
|
||||||
// Repeating unchanged does not warn again, which is what keeps a permanent failure from warning
|
|
||||||
// on every lighthouse.interval for the life of the process.
|
|
||||||
out.Reset()
|
|
||||||
lh.logLocalAddrsErr(errors.New("permission denied"))
|
|
||||||
assert.NotContains(t, out.String(), "level=WARN")
|
|
||||||
|
|
||||||
// A different failure is a new event and warns.
|
|
||||||
out.Reset()
|
|
||||||
lh.logLocalAddrsErr(errors.New("something else"))
|
|
||||||
assert.Contains(t, out.String(), "level=WARN")
|
|
||||||
assert.Contains(t, out.String(), "something else")
|
|
||||||
|
|
||||||
// Recovering resets, so the same failure returning later warns again.
|
|
||||||
out.Reset()
|
|
||||||
lh.logLocalAddrsErr(nil)
|
|
||||||
assert.Empty(t, out.String())
|
|
||||||
lh.logLocalAddrsErr(errors.New("something else"))
|
|
||||||
assert.Contains(t, out.String(), "level=WARN")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
//go:build !android
|
|
||||||
|
|
||||||
package nebula
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
func localInterfaces() ([]net.Interface, error) {
|
|
||||||
return net.Interfaces()
|
|
||||||
}
|
|
||||||
|
|
||||||
func localInterfaceAddrs(i *net.Interface) ([]net.Addr, error) {
|
|
||||||
return i.Addrs()
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
//go:build android
|
|
||||||
|
|
||||||
package nebula
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/wlynxg/anet"
|
|
||||||
)
|
|
||||||
|
|
||||||
// anet relies on //go:linkname and so needs -ldflags=-checklinkname=0 on Go 1.23+. Nebula ships no
|
|
||||||
// Android binaries of its own, so that burden falls on consumers linking Android artifacts.
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// anet only takes its bind-free path when it believes it is on API 30+, and detecting the running
|
|
||||||
// device's level requires cgo. Pin it so a CGO_ENABLED=0 build cannot quietly fall back to the
|
|
||||||
// denied path. The bind-free path is correct on older releases too, just unnecessary there.
|
|
||||||
anet.SetAndroidVersion(11)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The app sandbox denies bind() on netlink_route_socket, so the stdlib's RTM_GETLINK enumeration
|
|
||||||
// fails with EACCES and we advertise no underlay addresses at all. anet reads RTM_GETADDR from an
|
|
||||||
// unbound socket instead, so this must not be collapsed back into net.Interfaces.
|
|
||||||
func localInterfaces() ([]net.Interface, error) {
|
|
||||||
return anet.Interfaces()
|
|
||||||
}
|
|
||||||
|
|
||||||
// net.Interface.Addrs goes back through the denied netlink path, so addresses have to come from anet
|
|
||||||
// as well. anet cannot report HardwareAddr, which localAddrs does not read.
|
|
||||||
func localInterfaceAddrs(i *net.Interface) ([]net.Addr, error) {
|
|
||||||
return anet.InterfaceAddrsByInterface(i)
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
graphite "github.com/cyberdelia/go-metrics-graphite"
|
|
||||||
mp "github.com/nbrownus/go-metrics-prometheus"
|
mp "github.com/nbrownus/go-metrics-prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
@@ -253,7 +252,7 @@ func (s *statsServer) buildRuntime(cfg statsConfig) ([]func(), *http.Server) {
|
|||||||
// loadStatsConfig already resolved and validated the address; re-parse
|
// loadStatsConfig already resolved and validated the address; re-parse
|
||||||
// the resolved form (no DNS lookup) to get a *net.TCPAddr.
|
// the resolved form (no DNS lookup) to get a *net.TCPAddr.
|
||||||
addr, _ := net.ResolveTCPAddr(cfg.graphite.protocol, cfg.graphite.resolvedAddr)
|
addr, _ := net.ResolveTCPAddr(cfg.graphite.protocol, cfg.graphite.resolvedAddr)
|
||||||
gcfg := graphite.Config{
|
gcfg := graphiteConfigExport{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Registry: metrics.DefaultRegistry,
|
Registry: metrics.DefaultRegistry,
|
||||||
FlushInterval: cfg.interval,
|
FlushInterval: cfg.interval,
|
||||||
@@ -262,7 +261,7 @@ func (s *statsServer) buildRuntime(cfg statsConfig) ([]func(), *http.Server) {
|
|||||||
Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999},
|
Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999},
|
||||||
}
|
}
|
||||||
captureFns = append(captureFns, func() {
|
captureFns = append(captureFns, func() {
|
||||||
if err := graphite.Once(gcfg); err != nil {
|
if err := graphiteOnce(gcfg); err != nil {
|
||||||
s.l.Error("Graphite export failed", "error", err)
|
s.l.Error("Graphite export failed", "error", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -371,7 +371,7 @@ func waitForListening(t *testing.T, addr string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// graphiteSink is a minimal TCP accept-and-discard server so graphite.Once
|
// graphiteSink is a minimal TCP accept-and-discard server so graphiteOnce
|
||||||
// calls in tests don't spam error logs or wedge on connection refused.
|
// calls in tests don't spam error logs or wedge on connection refused.
|
||||||
type graphiteSink struct {
|
type graphiteSink struct {
|
||||||
ln net.Listener
|
ln net.Listener
|
||||||
|
|||||||
Reference in New Issue
Block a user