diff --git a/android/app/src/main/kotlin/net/defined/mobile_nebula/NebulaVpnService.kt b/android/app/src/main/kotlin/net/defined/mobile_nebula/NebulaVpnService.kt index 5cc7c368..870d7c3d 100644 --- a/android/app/src/main/kotlin/net/defined/mobile_nebula/NebulaVpnService.kt +++ b/android/app/src/main/kotlin/net/defined/mobile_nebula/NebulaVpnService.kt @@ -213,11 +213,17 @@ class NebulaVpnService : VpnService() { try { vpnInterface = builder.establish() nebula = mobileNebula.MobileNebula.newNebula(site!!.config, site!!.getKey(this), site!!.logFile, vpnInterface!!.detachFd().toLong()) + nebula!!.start(exitCallbackFor(nebula!!)) } catch (e: Exception) { Log.e(TAG, "Got an error $e") + // Go owns the detached tun fd from the moment newNebula is called and closes + // it on failure, never close it here, a second close can hit an unrelated + // recycled fd. The network callback and reload receiver below were never + // registered, so there is nothing else for stopVpn to clean up. + nebula = null vpnInterface?.close() - announceExit(site!!.id, e.message) + announceExit(site!!.id, e.message ?: e.toString()) return stopSelf() } @@ -226,7 +232,6 @@ class NebulaVpnService : VpnService() { //TODO: There is an open discussion around sleep killing tunnels or just changing mobile to tear down stale tunnels //registerSleep() - nebula!!.start() running = true sendSimple(MSG_IS_RUNNING, 1) } @@ -301,29 +306,44 @@ class NebulaVpnService : VpnService() { nebula?.reload(site!!.config, site!!.getKey(this)) } - private fun stopVpn() { + private fun stopVpn(error: String? = null) { if (nebula == null) { return stopSelf() } unregisterNetworkCallback() unregisterReloadReceiver() + // stop() blocks until the packet readers have drained and nebula has fully stopped nebula?.stop() nebula = null running = false - announceExit(site?.id, null) + announceExit(site?.id, error) stopSelf() } + // Called from a Go thread when nebula dies on its own, e.g. a fatal packet + // reader error, so the tunnel comes down instead of blackholing traffic. + // Bound to its session's nebula instance, a stale callback posted from a + // dying session must not tear down a new session on this same service. + private fun exitCallbackFor(sessionNebula: mobileNebula.Nebula): mobileNebula.ExitCallback { + return object : mobileNebula.ExitCallback { + override fun onExit(message: String?) { + Handler(Looper.getMainLooper()).post { + if (running && nebula === sessionNebula) { + stopVpn(message ?: "Nebula exited unexpectedly") + } + } + } + } + } + override fun onRevoke() { stopVpn() - //TODO: wait for the thread to exit super.onRevoke() } override fun onDestroy() { stopVpn() - //TODO: wait for the thread to exit super.onDestroy() } diff --git a/ios/NebulaNetworkExtension/PacketTunnelProvider.swift b/ios/NebulaNetworkExtension/PacketTunnelProvider.swift index c14f157c..89d972ae 100644 --- a/ios/NebulaNetworkExtension/PacketTunnelProvider.swift +++ b/ios/NebulaNetworkExtension/PacketTunnelProvider.swift @@ -33,6 +33,41 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private var didSleep = false private var cachedRouteDescription: String? + // A stopTunnel can race an in-flight start() before self.nebula exists, in + // which case its nebula?.stop() is a silent no-op. Latch the stop here so + // start() can apply it to the instance once it has been built. Never reset, + // a provider instance serves one session, and if the system ever reused one + // a stale latch refuses to start, which beats two nebulas on one tun fd. + private var stopped = false + private let stoppedLock = NSLock() + + private func isStopped() -> Bool { + stoppedLock.lock() + defer { stoppedLock.unlock() } + return stopped + } + + // Latch the stop and halt the network monitor in one critical section so a + // stop can't slip between start()'s latch check and its monitor startup + private func markStoppedAndHaltMonitor() { + stoppedLock.lock() + defer { stoppedLock.unlock() } + stopped = true + stopNetworkMonitor() + } + + // Start the post startup work only if a stop has not raced us, atomic with + // the stopped latch for the same reason as markStoppedAndHaltMonitor + private func startPostStartWork() { + stoppedLock.lock() + defer { stoppedLock.unlock() } + if stopped { + return + } + startNetworkMonitor() + dnUpdater.updateSingleLoop(site: site!, onUpdate: handleDNUpdate) + } + override func startTunnel(options: [String: NSObject]? = nil) async throws { // There is currently no way to get initialization errors back to the UI via completionHandler here // `expectStart` is sent only via the UI which means we should wait for the real start command which has another completion handler the UI can intercept @@ -112,15 +147,23 @@ class PacketTunnelProvider: NEPacketTunnelProvider { var nebulaErr: NSError? self.nebula = MobileNebulaNewNebula( String(data: config, encoding: .utf8), key, self.site!.logFile, tunFD, &nebulaErr) - self.startNetworkMonitor() if nebulaErr != nil { self.log.error("We had an error starting up: \(nebulaErr, privacy: .public)") throw nebulaErr! } - self.nebula!.start() - self.dnUpdater.updateSingleLoop(site: self.site!, onUpdate: self.handleDNUpdate) + // A stopTunnel that raced us before self.nebula existed had nothing to + // stop, apply it to this instance so start(self) tears it back down + if isStopped() { + self.nebula?.stop() + } + + try self.nebula!.start(self) + + // Skips the monitor and DN updater if a stopTunnel raced us, the tunnel + // is coming down and nothing would ever cancel them + startPostStartWork() } private func getNetworkAddressesAndRoutes(networks: [String], unsafeRoutes: [UnsafeRoute]) throws @@ -250,8 +293,10 @@ class PacketTunnelProvider: NEPacketTunnelProvider { override func stopTunnel( with reason: NEProviderStopReason, completionHandler: @escaping () -> Void ) { + markStoppedAndHaltMonitor() + + // stop() blocks until the packet readers have drained and nebula has fully stopped nebula?.stop() - stopNetworkMonitor() completionHandler() } @@ -299,6 +344,15 @@ class PacketTunnelProvider: NEPacketTunnelProvider { // No response data, this is expected on a clean start return try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil)) } catch { + // A stop that raced this start already tore the tunnel down, either + // cleanly or by yanking the session out from under one of the startup + // steps. Report success so the user's own disconnect doesn't pop an + // error in the UI, the site status will settle to disconnected via the + // status stream. Log it in case the failure predated the stop. + if isStopped() { + log.error("Start failed while stopping: \(error.localizedDescription, privacy: .public)") + return try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil)) + } defer { self.cancelTunnelWithError(error) } @@ -398,3 +452,22 @@ class PacketTunnelProvider: NEPacketTunnelProvider { return nil } } + +extension PacketTunnelProvider: MobileNebulaExitCallbackProtocol { + // Called from a Go thread when nebula dies on its own, e.g. a fatal packet + // reader error, so the tunnel comes down instead of blackholing traffic + func onExit(_ message: String?) { + // A reader can die from the tun being torn out from under it during a + // requested stop, don't report that as a tunnel failure + if isStopped() { + return + } + + let error = message ?? "Nebula exited unexpectedly" + log.error("\(error, privacy: .public)") + cancelTunnelWithError( + NSError( + domain: "net.defined.mobileNebula", code: 1, + userInfo: [NSLocalizedDescriptionKey: error])) + } +} diff --git a/nebula/control.go b/nebula/control.go index 1eec1aa7..90d4c4ec 100644 --- a/nebula/control.go +++ b/nebula/control.go @@ -4,22 +4,44 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "net/netip" "os" "runtime" "runtime/debug" + "strings" + "sync" - "github.com/sirupsen/logrus" "github.com/slackhq/nebula" nc "github.com/slackhq/nebula/config" + "github.com/slackhq/nebula/logging" "github.com/slackhq/nebula/overlay" "github.com/slackhq/nebula/util" + "golang.org/x/sys/unix" ) +// ExitCallback is implemented by the platform side (Kotlin/Swift) to learn about +// nebula dying on its own, a fatal packet reader error stops nebula internally +// and without this the OS still thinks the tunnel is up while traffic blackholes. +// It only fires for deaths the platform did not ask for, never for a Stop. +// The callback fires from a Go thread, hop to the platform main thread before +// touching anything that needs it. +type ExitCallback interface { + OnExit(message string) +} + type Nebula struct { c *nebula.Control - l *logrus.Logger + l *slog.Logger config *nc.C + + // startStopLock serializes Start and Stop, which arrive on different + // platform threads. Holding it across the whole of both bodies means a Stop + // that races an in-flight Start blocks until the start has landed and been + // torn back down, so Stop returning always means the readers have drained. + startStopLock sync.Mutex + wait func() error + stopRequested bool } func init() { @@ -27,21 +49,54 @@ func init() { runtime.MemProfileRate = 0 } -func NewNebula(configData string, key string, logFile string, tunFd int) (*Nebula, error) { +func NewNebula(configData string, key string, logFile string, tunFd int) (_ *Nebula, reterr error) { // GC more often, largely for iOS due to extension 15mb limit debug.SetGCPercent(20) + // Go owns tunFd from here on where ownership is really ours to take: + // Android detaches the fd to us, so if we fail before the overlay device + // wraps it we must close it ourselves, and the platform side must never + // close it (a double close can hit an unrelated recycled fd). On iOS the + // fd is scanned out of the process and the NE framework still owns it, so + // we leave a pre handoff failure for the session teardown to clean up. + // Once the device factory has run, the fd belongs to the os.File it + // created, which nebula.Main closes on failures after a successful create. + fdHandedOff := false + defer func() { + if reterr != nil && !fdHandedOff && runtime.GOOS == "android" { + _ = unix.Close(tunFd) + } + }() + + // Go only registers the tun fd with its runtime poller when the fd is + // already non-blocking, and a blocking fd leaves shutdown hanging in a + // parked read until a packet happens to arrive. Android hands us a + // non-blocking fd, iOS does not, setting it here covers both. + // TODO: this belongs upstream in tun_ios.go next to its siblings + // (tun_darwin.go does the same thing), move it there and drop this. + if err := unix.SetNonblock(tunFd, true); err != nil { + return nil, fmt.Errorf("failed to set the tun fd to non-blocking mode: %s", err) + } + yamlConfig, err := RenderConfig(configData, key) if err != nil { return nil, err } - l := logrus.New() f, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return nil, err } - l.SetOutput(f) + + // The logger owns f on success, close it ourselves on any error return so + // repeated failed starts in the long lived app process don't pile up fds + defer func() { + if reterr != nil { + f.Close() + } + }() + + l := logging.NewLogger(f) c := nc.NewC(l) err = c.LoadString(yamlConfig) @@ -49,50 +104,162 @@ func NewNebula(configData string, key string, logFile string, tunFd int) (*Nebul return nil, fmt.Errorf("failed to load config: %s", err) } + // nebula.Main does not configure the logger from config, so apply + // logging.level/format ourselves and keep it in sync on reload. + if err := applyLogConfig(l, c); err != nil { + return nil, fmt.Errorf("failed to apply logging config: %s", err) + } + c.RegisterReloadCallback(func(c *nc.C) { + if err := applyLogConfig(l, c); err != nil { + l.Error("Failed to reconfigure logger on reload", "error", err) + } + }) + + devFactory := overlay.NewFdDeviceFromConfig(&tunFd) + factoryFailed := false + wrappedFactory := func(c *nc.C, l *slog.Logger, vpnNetworks []netip.Prefix, routines int) (overlay.Device, error) { + // Even a failed newTunFromFd has wrapped the fd in an os.File by the + // time it can fail, so the fd is out of our hands once this runs + fdHandedOff = true + device, err := devFactory(c, l, vpnNetworks, routines) + if err != nil { + factoryFailed = true + } + return device, err + } + //TODO: inject our version - ctrl, err := nebula.Main(c, false, "", l, overlay.NewFdDeviceFromConfig(&tunFd)) + ctrl, err := nebula.Main(c, false, "", l, wrappedFactory) if err != nil { - switch v := err.(type) { - case *util.ContextualError: - v.Log(l) - return nil, v.Unwrap() - default: - l.WithError(err).Error("Failed to start") - return nil, err + if factoryFailed { + // A failed newTunFromFd leaves the fd wrapped in an unreachable + // os.File that only a GC finalizer will close. Nudge the GC so it + // happens now rather than at some arbitrary later point, on Android + // the system may keep VPN routes captured until the fd closes. + // TODO: upstream newTunFromFd should close the file on error. + runtime.GC() } + return nil, logAndUnwrap("Failed to start", err, l) } - return &Nebula{ctrl, l, c}, nil + return &Nebula{c: ctrl, l: l, config: c}, nil } -func (n *Nebula) Log(v string) { - n.l.Println(v) +// logAndUnwrap logs err with its context fields attached and returns the inner +// error, a ContextualError's string includes a raw fields map that we don't +// want surfaced in the platform UI +func logAndUnwrap(msg string, err error, l *slog.Logger) error { + util.LogWithContextIfNeeded(msg, err, l) + var ce *util.ContextualError + if errors.As(err, &ce) { + return ce.Unwrap() + } + return err } -func (n *Nebula) Start() { - n.c.Start() +// applyLogConfig is logging.ApplyConfig plus compatibility with the logrus level +// names, logrus treated fatal/panic as quieter than error but slog stops at +// error, so push the handler level above error to keep those configs quiet. +// logging.timestamp_format is not honored, the slog migration upstream dropped +// it and restoring it belongs there, not here. +func applyLogConfig(l *slog.Logger, c *nc.C) error { + if err := logging.ApplyConfig(l, c); err != nil { + return err + } + + if lvl := strings.ToLower(c.GetString("logging.level", "info")); lvl == "fatal" || lvl == "panic" { + if ls, ok := l.Handler().(interface{ SetLevel(slog.Level) }); ok { + ls.SetLevel(slog.LevelError + 4) + } + } + return nil } -func (n *Nebula) ShutdownBlock() { - n.c.ShutdownBlock() +// Start brings the tunnel up. A Nebula is single use, once Stop has been +// called on an instance any Start on it will tear itself back down and return +// an error, both platforms build a fresh instance per connect. +func (n *Nebula) Start(cb ExitCallback) error { + n.startStopLock.Lock() + defer n.startStopLock.Unlock() + + wait, err := n.c.Start() + if err != nil { + return logAndUnwrap("Failed to start nebula", err, n.l) + } + n.wait = wait + + // A Stop already came and went before we ran, it found nothing to tear + // down so do it for them now that the readers are actually running + if n.stopRequested { + n.c.Stop() + _ = wait() + return errors.New("nebula was stopped during startup") + } + + // A fatal packet reader error stops nebula internally, tell the platform + // side so it can tear the tunnel down instead of blackholing traffic. + // A nil error from wait means the stop came from Stop, upstream only stops + // itself through onFatal which always carries an error. + go func() { + err := wait() + if err == nil { + return + } + + n.l.Error("Nebula stopped due to fatal error", "error", err) + + // Don't tell the platform about a death it caused, a reader can hit a + // real error in the small window before Stop marks the tun closed + n.startStopLock.Lock() + requested := n.stopRequested + n.startStopLock.Unlock() + + if !requested && cb != nil { + cb.OnExit(err.Error()) + } + }() + + return nil } +// Stop tears nebula down and blocks until the packet readers have drained, so +// when it returns the tunnel is fully stopped. A Stop that races an in-flight +// Start on this instance waits for the start to land and tears it down before +// returning. A Stop that arrives before Start was ever called returns +// immediately and the eventual Start tears itself down and returns an error +// instead. func (n *Nebula) Stop() { + n.startStopLock.Lock() + defer n.startStopLock.Unlock() + + n.stopRequested = true n.c.Stop() + if n.wait != nil { + _ = n.wait() + } } func (n *Nebula) Rebind(reason string) { - n.l.Debugf("Rebinding UDP listener and updating lighthouses due to %s", reason) + n.l.Debug("Rebinding UDP listener and updating lighthouses", "reason", reason) n.c.RebindUDPServer() } func (n *Nebula) Reload(configData string, key string) error { - n.l.Info("Reloading Nebula") yamlConfig, err := RenderConfig(configData, key) if err != nil { return err } + n.startStopLock.Lock() + defer n.startStopLock.Unlock() + + // Don't fire reload callbacks into an interface that is coming down, the + // iOS DN updater timer can land a reload mid Stop + if n.stopRequested { + return nil + } + + n.l.Info("Reloading Nebula") return n.config.ReloadConfigString(yamlConfig) } @@ -160,6 +327,6 @@ func (n *Nebula) SetRemoteForTunnel(vpnIp string, addr string) (string, error) { func (n *Nebula) Sleep() { if closed := n.c.CloseAllTunnels(true); closed > 0 { - n.l.WithField("tunnels", closed).Info("Sleep called, closed non lighthouse tunnels") + n.l.Info("Sleep called, closed non lighthouse tunnels", "tunnels", closed) } } diff --git a/nebula/go.mod b/nebula/go.mod index e305569e..ffe85ee1 100644 --- a/nebula/go.mod +++ b/nebula/go.mod @@ -7,9 +7,10 @@ go 1.25.0 require ( github.com/DefinedNet/dnapi v0.0.0-20260313005402-c66f625d8dfd github.com/sirupsen/logrus v1.9.4 - github.com/slackhq/nebula v1.10.3 + github.com/slackhq/nebula v1.10.4-0.20260708014326-c1eea118f4e8 github.com/stretchr/testify v1.11.1 - golang.org/x/crypto v0.49.0 + golang.org/x/crypto v0.53.0 + golang.org/x/sys v0.46.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -23,11 +24,11 @@ require ( github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/flynn/noise v1.1.0 // indirect - github.com/gaissmai/bart v0.26.0 // indirect + github.com/gaissmai/bart v0.28.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/miekg/dns v1.1.70 // indirect - github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b // indirect + github.com/miekg/dns v1.1.72 // indirect + github.com/miekg/pkcs11 v1.1.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -41,16 +42,15 @@ require ( github.com/vishvananda/netns v0.0.5 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mobile v0.0.0-20260312152759-81488f6aeb60 // indirect - golang.org/x/mod v0.34.0 // indirect - golang.org/x/net v0.52.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/term v0.41.0 // indirect - golang.org/x/tools v0.43.0 // indirect + golang.org/x/mobile v0.0.0-20260611195102-4dd8f1dbf5d2 // indirect + golang.org/x/mod v0.37.0 // indirect + golang.org/x/net v0.56.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/term v0.44.0 // indirect + golang.org/x/tools v0.46.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect - golang.zx2c4.com/wireguard/windows v0.5.3 // indirect + golang.zx2c4.com/wireguard/windows v1.0.1 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/nebula/go.sum b/nebula/go.sum index ea3b66f6..103face2 100644 --- a/nebula/go.sum +++ b/nebula/go.sum @@ -28,8 +28,8 @@ 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/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/gaissmai/bart v0.26.0 h1:xOZ57E9hJLBiQaSyeZa9wgWhGuzfGACgqp4BE77OkO0= -github.com/gaissmai/bart v0.26.0/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c= +github.com/gaissmai/bart v0.28.0 h1:89yZLo8NmyqD0RYgJ3QO9HhqqGGw+oWhf90cZm69Lko= +github.com/gaissmai/bart v0.28.0/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -83,10 +83,10 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.70 h1:DZ4u2AV35VJxdD9Fo9fIWm119BsQL5cZU1cQ9s0LkqA= -github.com/miekg/dns v1.1.70/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= -github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b h1:J/AzCvg5z0Hn1rqZUJjpbzALUmkKX0Zwbc/i4fw7Sfk= -github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= +github.com/miekg/pkcs11 v1.1.2 h1:/VxmeAX5qU6Q3EwafypogwWbYryHFmF2RpkJmw3m4MQ= +github.com/miekg/pkcs11 v1.1.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= 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= @@ -134,8 +134,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -github.com/slackhq/nebula v1.10.3 h1:EstYj8ODEcv6T0R9X5BVq1zgWZnyU5gtPzk99QF1PMU= -github.com/slackhq/nebula v1.10.3/go.mod h1:IL5TUQm4x9IFx2kCKPYm1gP47pwd5b8QGnnBH2RHnvs= +github.com/slackhq/nebula v1.10.4-0.20260708014326-c1eea118f4e8 h1:3C3O5awM8m55bYE50p/FlgRgXPjFbnzVFu+bYTXbPOk= +github.com/slackhq/nebula v1.10.4-0.20260708014326-c1eea118f4e8/go.mod h1:LZBUu47iiQQCwsQJhMHQcG7Vigj9IXQE70CO+MmrOXk= github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw= github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -163,18 +163,16 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20260312152759-81488f6aeb60 h1:MOzyaj0wu2xneBkzkg9LHNYjDBB4W5vP043A2SYQRPA= -golang.org/x/mobile v0.0.0-20260312152759-81488f6aeb60/go.mod h1:th6VJvzjMbrYF8SduQY5rpD0HG0GleGxjadkqSxFs3k= +golang.org/x/mobile v0.0.0-20260611195102-4dd8f1dbf5d2 h1:zoM1gIKhVkcQNm43kad8OHLgPNoJ12xIqmxHtKr8Mug= +golang.org/x/mobile v0.0.0-20260611195102-4dd8f1dbf5d2/go.mod h1:QGMqsqLn6orFQ/ksqYMf+Fa33Soa1vPoHEd0Pj7N+lQ= 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.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= 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= @@ -185,10 +183,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.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= 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= @@ -196,10 +192,8 @@ 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.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= 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= @@ -215,11 +209,11 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= 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= @@ -228,10 +222,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.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk= +golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys= 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= @@ -240,8 +232,8 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A= golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= -golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= -golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= +golang.zx2c4.com/wireguard/windows v1.0.1 h1:eOxiDVbywPC+ZQqvdCK7x+ZwWXKbYv50TtH8ysFIbw8= +golang.zx2c4.com/wireguard/windows v1.0.1/go.mod h1:+fbT3FFdX4zzYDLwJh5+HPEcNN/3HyNdzhNSVsQM+zs= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/nebula/mobileNebula.go b/nebula/mobileNebula.go index a3e974bb..755ef359 100644 --- a/nebula/mobileNebula.go +++ b/nebula/mobileNebula.go @@ -1,18 +1,16 @@ package mobileNebula import ( - "bytes" "crypto/rand" "encoding/json" "fmt" "io" - "io/ioutil" + "log/slog" "net" "net/netip" "strings" "time" - "github.com/sirupsen/logrus" "github.com/slackhq/nebula" "github.com/slackhq/nebula/cert" nc "github.com/slackhq/nebula/config" @@ -284,8 +282,7 @@ func TestConfig(configData string, key string) error { } // We don't want to leak the config into the system logs - l := logrus.New() - l.SetOutput(bytes.NewBuffer([]byte{})) + l := slog.New(slog.DiscardHandler) c := nc.NewC(l) err = c.LoadString(yamlConfig) @@ -307,8 +304,7 @@ func TestConfig(configData string, key string) error { func GetConfigSetting(configData string, setting string) string { // We don't want to leak the config into the system logs - l := logrus.New() - l.SetOutput(ioutil.Discard) + l := slog.New(slog.DiscardHandler) c := nc.NewC(l) c.LoadString(configData) diff --git a/nebula/mobileNebula_test.go b/nebula/mobileNebula_test.go index f021e408..ff592bee 100644 --- a/nebula/mobileNebula_test.go +++ b/nebula/mobileNebula_test.go @@ -2,9 +2,9 @@ package mobileNebula import ( "encoding/json" + "log/slog" "testing" - "github.com/sirupsen/logrus" nebcfg "github.com/slackhq/nebula/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -60,7 +60,7 @@ func TestRenderConfig(t *testing.T) { s, err := RenderConfig(string(configData), "") require.NoError(t, err, "RenderConfig failed") - config := nebcfg.NewC(logrus.New()) + config := nebcfg.NewC(slog.New(slog.DiscardHandler)) err = config.LoadString(s) require.NoError(t, err, "LoadString failed") @@ -106,7 +106,7 @@ func TestRenderConfigLegacy(t *testing.T) { s, err := RenderConfig(jsonConfig, "") require.NoError(t, err, "RenderConfig (legacy) failed") - config := nebcfg.NewC(logrus.New()) + config := nebcfg.NewC(slog.New(slog.DiscardHandler)) err = config.LoadString(s) require.NoError(t, err, "LoadString (legacy) failed") }