mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-02 13:51:28 +02:00
Compare commits
2 Commits
agent-netw
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2318a8fef | ||
|
|
77f7e9fc91 |
15
client/internal/dns/interface_index.go
Normal file
15
client/internal/dns/interface_index.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func getInterfaceIndex(interfaceName string) (int, error) {
|
||||
iface, err := net.InterfaceByName(interfaceName)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("lookup interface %q: %w", interfaceName, err)
|
||||
}
|
||||
|
||||
return iface.Index, nil
|
||||
}
|
||||
35
client/internal/dns/interface_index_test.go
Normal file
35
client/internal/dns/interface_index_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetInterfaceIndexExisting(t *testing.T) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
t.Fatalf("list network interfaces: %v", err)
|
||||
}
|
||||
if len(interfaces) == 0 {
|
||||
t.Fatal("expected at least one network interface")
|
||||
}
|
||||
|
||||
iface := interfaces[0]
|
||||
index, err := getInterfaceIndex(iface.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("look up existing interface %q: %v", iface.Name, err)
|
||||
}
|
||||
if index != iface.Index {
|
||||
t.Fatalf("expected interface index %d, got %d", iface.Index, index)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInterfaceIndexMissing(t *testing.T) {
|
||||
index, err := getInterfaceIndex("netbird-interface-that-does-not-exist")
|
||||
if index != 0 {
|
||||
t.Fatalf("expected missing interface index to be 0, got %d", index)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("expected missing interface lookup to return an error")
|
||||
}
|
||||
}
|
||||
@@ -130,8 +130,3 @@ func GetClientPrivate(iface privateClientIface, upstreamIP netip.Addr, dialTimeo
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func getInterfaceIndex(interfaceName string) (int, error) {
|
||||
iface, err := net.InterfaceByName(interfaceName)
|
||||
return iface.Index, err
|
||||
}
|
||||
|
||||
@@ -158,13 +158,19 @@ func (c *Client) Run(fd int32, interfaceName string, envList *EnvList) error {
|
||||
defer c.ctxCancel()
|
||||
c.ctxCancelLock.Unlock()
|
||||
|
||||
auth := NewAuthWithConfig(ctx, cfg)
|
||||
err = auth.LoginSync()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Auth successful")
|
||||
// No login pre-flight here. The engine's own loginToManagement (connect.go) performs
|
||||
// the authoritative Login immediately before the first Sync, so a LoginSync() call at
|
||||
// this point only duplicated it — costing two extra Login RPCs (IsLoginRequired +
|
||||
// Login) on every engine start, since IsLoginRequired is itself a full Login RPC.
|
||||
//
|
||||
// Auth failures still reach the caller through the engine path: loginToManagement
|
||||
// returns PermissionDenied, which marks the shared status recorder
|
||||
// (MarkManagementDisconnected) and fires ClientStop → onDisconnected, where
|
||||
// IsLoginRequiredCached() reports login-required. The error is also returned out of Run().
|
||||
//
|
||||
// A pre-flight was also actively harmful when the server is unreachable: its 2-minute
|
||||
// backoff blocked the start and then reported "login required" for what was really a
|
||||
// timeout. The engine instead keeps retrying and recovers when the server returns.
|
||||
// todo do not throw error in case of cancelled context
|
||||
ctx = internal.CtxInitState(ctx)
|
||||
c.onHostDnsFn = func([]string) {}
|
||||
|
||||
@@ -222,17 +222,36 @@ func (a *Auth) Login(resultListener ErrListener, urlOpener URLOpener, forceDevic
|
||||
// LoginWithDeviceName performs interactive login with device authentication support
|
||||
// The deviceName parameter allows specifying a custom device name (required for tvOS)
|
||||
func (a *Auth) LoginWithDeviceName(resultListener ErrListener, urlOpener URLOpener, forceDeviceAuth bool, deviceName string) {
|
||||
a.startLogin(resultListener, urlOpener, forceDeviceAuth, deviceName, false)
|
||||
}
|
||||
|
||||
// LoginInteractive performs the same interactive login as LoginWithDeviceName but skips the
|
||||
// IsLoginRequired() pre-flight and goes straight to the browser / device-code flow.
|
||||
//
|
||||
// IsLoginRequired() is itself a full Login RPC against the management server, so when the
|
||||
// caller has ALREADY established that login is required it is a pure duplicate. On iOS the
|
||||
// main app decides to show the browser based on its own isLoginRequired() check and then
|
||||
// calls straight into this method, so re-asking the server would add another Login RPC to
|
||||
// every interactive login.
|
||||
//
|
||||
// Use LoginWithDeviceName when the auth state is unknown and a silent (browser-less) login
|
||||
// must still be possible; use this when the browser is going to be shown regardless.
|
||||
func (a *Auth) LoginInteractive(resultListener ErrListener, urlOpener URLOpener, forceDeviceAuth bool, deviceName string) {
|
||||
a.startLogin(resultListener, urlOpener, forceDeviceAuth, deviceName, true)
|
||||
}
|
||||
|
||||
func (a *Auth) startLogin(resultListener ErrListener, urlOpener URLOpener, forceDeviceAuth bool, deviceName string, skipLoginCheck bool) {
|
||||
if resultListener == nil {
|
||||
log.Errorf("LoginWithDeviceName: resultListener is nil")
|
||||
log.Errorf("startLogin: resultListener is nil")
|
||||
return
|
||||
}
|
||||
if urlOpener == nil {
|
||||
log.Errorf("LoginWithDeviceName: urlOpener is nil")
|
||||
log.Errorf("startLogin: urlOpener is nil")
|
||||
resultListener.OnError(fmt.Errorf("urlOpener is nil"))
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
err := a.login(urlOpener, forceDeviceAuth, deviceName)
|
||||
err := a.login(urlOpener, forceDeviceAuth, deviceName, skipLoginCheck)
|
||||
if err != nil {
|
||||
resultListener.OnError(err)
|
||||
} else {
|
||||
@@ -241,7 +260,7 @@ func (a *Auth) LoginWithDeviceName(resultListener ErrListener, urlOpener URLOpen
|
||||
}()
|
||||
}
|
||||
|
||||
func (a *Auth) login(urlOpener URLOpener, forceDeviceAuth bool, deviceName string) error {
|
||||
func (a *Auth) login(urlOpener URLOpener, forceDeviceAuth bool, deviceName string, skipLoginCheck bool) error {
|
||||
// Create context with device name if provided
|
||||
ctx := a.ctx
|
||||
if deviceName != "" {
|
||||
@@ -255,10 +274,13 @@ func (a *Auth) login(urlOpener URLOpener, forceDeviceAuth bool, deviceName strin
|
||||
}
|
||||
defer authClient.Close()
|
||||
|
||||
// check if we need to generate JWT token
|
||||
needsLogin, err := authClient.IsLoginRequired(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check login requirement: %v", err)
|
||||
// check if we need to generate JWT token (skipped when the caller already knows)
|
||||
needsLogin := true
|
||||
if !skipLoginCheck {
|
||||
needsLogin, err = authClient.IsLoginRequired(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check login requirement: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
jwtToken := ""
|
||||
|
||||
Reference in New Issue
Block a user