Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile #735

Merged
merged 77 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
8ebb720
Add conn state callback for mgm client
pappz Mar 9, 2023
fbeb2d0
In StateRecorder move mgm address to constructor
pappz Mar 9, 2023
cfaeca8
Fix RWMutex initialization in mgm client
pappz Mar 9, 2023
e5dd903
Fix signal server state indication
pappz Mar 9, 2023
cd2bcb9
Fix tests
pappz Mar 9, 2023
335bdd9
Fix tests
pappz Mar 9, 2023
2809027
Fix tests
pappz Mar 9, 2023
4f49c95
Write comments
pappz Mar 9, 2023
ea9476f
From iface pkg move tun device related codes into
pappz Feb 14, 2023
c3c8811
Add Android related implementation for iface
pappz Feb 14, 2023
270b8cd
Add Android specific system/info
pappz Feb 16, 2023
f58ce8b
Add dummy DNS server for Android
pappz Feb 16, 2023
84cb1f2
Add dummy route manager implementation for Android
pappz Feb 16, 2023
8a98cde
Change the WG configuration to ipc way in Android
pappz Feb 16, 2023
0d74c52
Add exported gomobile code
pappz Feb 16, 2023
1d5aa90
Fix remove flag in ipc communication
pappz Feb 16, 2023
47416b4
Add service state notifier
pappz Feb 17, 2023
7a11ce3
Gracefully close tun interface on Android
pappz Feb 20, 2023
8918771
Remove unused variable
pappz Feb 20, 2023
fdba775
Remove unnecessary function
pappz Feb 21, 2023
ff25947
Add login logic
pappz Feb 23, 2023
97a7f69
Rename state listener to connection listener
pappz Feb 23, 2023
c92c5b1
Simple code cleaning
pappz Feb 23, 2023
4f82203
Fix ctx lock
pappz Feb 23, 2023
9d85a17
Change the host name of Android implementation
pappz Feb 23, 2023
a944979
Fix context cancellation in start/stop
pappz Feb 24, 2023
610670c
Remove unused function
pappz Feb 26, 2023
828e0f4
Rename WGAdpater to TunAdapter
pappz Feb 27, 2023
1299eff
Replace the SDK version to Android version
pappz Feb 27, 2023
87951ba
Eliminate exported fields in Formatter
pappz Feb 27, 2023
23f350f
Add new formatter for Android client
pappz Feb 27, 2023
417df04
Squashed commit of the config-cleaning
pappz Mar 1, 2023
74b9d65
Follow up the changes in config.go
pappz Mar 1, 2023
1baa8e7
Add config reader for gomobile
pappz Mar 1, 2023
55bf9ff
Remove mgm, admin url from Client constructor
pappz Mar 2, 2023
27dcf7c
Revert "Simple code cleaning"
pappz Mar 3, 2023
1270939
Use the conn state of peer on proper way (#717)
pappz Mar 3, 2023
3c34991
Add peer state listener
pappz Mar 5, 2023
e341715
Quick fix for Android issue with ICE lib
pappz Mar 5, 2023
f4a56be
Remove unused function from client runner
pappz Mar 6, 2023
8e5a52f
Fix engine text
pappz Mar 7, 2023
ab77b97
Add todo comment
pappz Mar 7, 2023
5aedda5
Add separated Login struct for React lib
pappz Mar 7, 2023
5f65817
Fix the login flow
pappz Mar 7, 2023
0549b33
Remove url opener from auth flow
pappz Mar 7, 2023
d53e2a6
Remove url opener from auth's constructor
pappz Mar 7, 2023
83c9934
Move out urlOpener from constructor
pappz Mar 8, 2023
de9a54e
Add pion/transport for go.mod
pappz Mar 10, 2023
7471fcf
Fix iface tests
pappz Mar 10, 2023
5c2bfbc
Fix tests
pappz Mar 10, 2023
92c2683
Compile toWgUserspaceString func for Android only
pappz Mar 10, 2023
2a72606
Remove unused moc listener
pappz Mar 10, 2023
097ccdc
Fix lints
pappz Mar 10, 2023
9be21b9
Fix Codacy Static Code Analysis issues
pappz Mar 10, 2023
2cefc21
Add comments to peer_notifier.go
pappz Mar 10, 2023
c34af64
Add comments to server_android.go
pappz Mar 10, 2023
74c3226
Add comment login.go
pappz Mar 10, 2023
2f2c417
Code cleaning in tun_unix.go
pappz Mar 10, 2023
2f7ae5e
Rename Url to URL in login.go
pappz Mar 10, 2023
ad903aa
Add comments for exported functions
pappz Mar 10, 2023
fa4c855
Rename urlOpener to URLOpener
pappz Mar 10, 2023
072e478
Fix build of iface pkg on Windows
pappz Mar 10, 2023
1bd424d
Rollback accidentally removed comment
pappz Mar 14, 2023
16078ec
Deduplicate login code
pappz Mar 14, 2023
5f94326
Followup prometheus version update
pappz Mar 14, 2023
e161b47
Merge branch 'main' into mobile
pappz Mar 14, 2023
145a730
Remove duplicated log line
pappz Mar 14, 2023
b9176bf
Fix flickering logcat test
pappz Mar 14, 2023
6f0f92f
Remove interface definitions from Android code
pappz Mar 14, 2023
9a41e8a
Fix management server address update
pappz Mar 14, 2023
c46ba02
Merge branch 'main' into mobile
pappz Mar 15, 2023
b3c1832
Merge branch 'fix-connstate-indication' into mobile
pappz Mar 15, 2023
8f15cda
Fix version info on Android
pappz Mar 15, 2023
895c884
Fix build flags on dns server
pappz Mar 15, 2023
9e60038
Fix management address initialization on Android
pappz Mar 15, 2023
e98687f
Fix keep alive interval on Android
pappz Mar 15, 2023
32d2979
Merge branch 'main' into mobile
pappz Mar 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions client/android/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package android

import (
"context"
"sync"

log "github.com/sirupsen/logrus"

"github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/system"
"github.com/netbirdio/netbird/formatter"
"github.com/netbirdio/netbird/iface"
)

// ConnectionListener export internal Listener for mobile
type ConnectionListener interface {
peer.Listener
}

// TunAdapter export internal TunAdapter for mobile
type TunAdapter interface {
iface.TunAdapter
}

func init() {
formatter.SetLogcatFormatter(log.StandardLogger())
}

// Client struct manage the life circle of background service
type Client struct {
cfgFile string
tunAdapter iface.TunAdapter
recorder *peer.Status
ctxCancel context.CancelFunc
ctxCancelLock *sync.Mutex
deviceName string
}

// NewClient instantiate a new Client
func NewClient(cfgFile, deviceName string, tunAdapter TunAdapter) *Client {
lvl, _ := log.ParseLevel("trace")
log.SetLevel(lvl)

return &Client{
cfgFile: cfgFile,
deviceName: deviceName,
tunAdapter: tunAdapter,
recorder: peer.NewRecorder(""),
ctxCancelLock: &sync.Mutex{},
}
}

// Run start the internal client. It is a blocker function
func (c *Client) Run(urlOpener URLOpener) error {
cfg, err := internal.UpdateOrCreateConfig(internal.ConfigInput{
ConfigPath: c.cfgFile,
})
if err != nil {
return err
}
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())

var ctx context.Context
//nolint
ctxWithValues := context.WithValue(context.Background(), system.DeviceNameCtxKey, c.deviceName)
c.ctxCancelLock.Lock()
ctx, c.ctxCancel = context.WithCancel(ctxWithValues)
defer c.ctxCancel()
c.ctxCancelLock.Unlock()

auth := NewAuthWithConfig(ctx, cfg)
err = auth.Login(urlOpener)
if err != nil {
return err
}

// todo do not throw error in case of cancelled context
ctx = internal.CtxInitState(ctx)
return internal.RunClient(ctx, cfg, c.recorder, c.tunAdapter)
}

// Stop the internal client and free the resources
func (c *Client) Stop() {
c.ctxCancelLock.Lock()
defer c.ctxCancelLock.Unlock()
if c.ctxCancel == nil {
return
}

c.ctxCancel()
}

// PeersList return with the list of the PeerInfos
func (c *Client) PeersList() *PeerInfoArray {

fullStatus := c.recorder.GetFullStatus()

peerInfos := make([]PeerInfo, len(fullStatus.Peers))
for n, p := range fullStatus.Peers {
pi := PeerInfo{
p.IP,
p.FQDN,
p.ConnStatus.String(),
p.Direct,
}
peerInfos[n] = pi
}

return &PeerInfoArray{items: peerInfos}
}

// AddConnectionListener add new network connection listener
func (c *Client) AddConnectionListener(listener ConnectionListener) {
c.recorder.AddConnectionListener(listener)
}

// RemoveConnectionListener remove connection listener
func (c *Client) RemoveConnectionListener(listener ConnectionListener) {
c.recorder.RemoveConnectionListener(listener)
}
173 changes: 173 additions & 0 deletions client/android/login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package android

import (
"context"
"fmt"
"github.com/cenkalti/backoff/v4"
"github.com/netbirdio/netbird/client/cmd"
"time"

"github.com/netbirdio/netbird/client/internal"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
gstatus "google.golang.org/grpc/status"
)

// URLOpener it is a callback interface. The Open function will be triggered if
// the backend want to show an url for the user
type URLOpener interface {
Open(string)
}

// Auth can register or login new client
type Auth struct {
ctx context.Context
config *internal.Config
cfgPath string
}

// NewAuth instantiate Auth struct and validate the management URL
func NewAuth(cfgPath string, mgmURL string) (*Auth, error) {
inputCfg := internal.ConfigInput{
ManagementURL: mgmURL,
}

cfg, err := internal.CreateInMemoryConfig(inputCfg)
if err != nil {
return nil, err
}

return &Auth{
ctx: context.Background(),
config: cfg,
cfgPath: cfgPath,
}, nil
}

// NewAuthWithConfig instantiate Auth based on existing config
func NewAuthWithConfig(ctx context.Context, config *internal.Config) *Auth {
return &Auth{
ctx: ctx,
config: config,
}
}

// LoginAndSaveConfigIfSSOSupported test the connectivity with the management server.
// If the SSO is supported than save the configuration. Return with the SSO login is supported or not.
func (a *Auth) LoginAndSaveConfigIfSSOSupported() (bool, error) {
var needsLogin bool
err := a.withBackOff(a.ctx, func() (err error) {
needsLogin, err = internal.IsLoginRequired(a.ctx, a.config.PrivateKey, a.config.ManagementURL, a.config.SSHKey)
return
})
if err != nil {
return false, fmt.Errorf("backoff cycle failed: %v", err)
}
if !needsLogin {
return false, nil
}
err = internal.WriteOutConfig(a.cfgPath, a.config)
return needsLogin, err
}

// LoginWithSetupKeyAndSaveConfig test the connectivity with the management server with the setup key.
func (a *Auth) LoginWithSetupKeyAndSaveConfig(setupKey string) error {
err := a.withBackOff(a.ctx, func() error {
err := internal.Login(a.ctx, a.config, setupKey, "")
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.InvalidArgument || s.Code() == codes.PermissionDenied) {
return nil
}
return err
})
if err != nil {
return fmt.Errorf("backoff cycle failed: %v", err)
}

return internal.WriteOutConfig(a.cfgPath, a.config)
}

// Login try register the client on the server
func (a *Auth) Login(urlOpener URLOpener) error {
var needsLogin bool

// check if we need to generate JWT token
err := a.withBackOff(a.ctx, func() (err error) {
needsLogin, err = internal.IsLoginRequired(a.ctx, a.config.PrivateKey, a.config.ManagementURL, a.config.SSHKey)
return
})
if err != nil {
return fmt.Errorf("backoff cycle failed: %v", err)
}

jwtToken := ""
if needsLogin {
tokenInfo, err := a.foregroundGetTokenInfo(urlOpener)
if err != nil {
return fmt.Errorf("interactive sso login failed: %v", err)
}
jwtToken = tokenInfo.AccessToken
}

err = a.withBackOff(a.ctx, func() error {
err := internal.Login(a.ctx, a.config, "", jwtToken)
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.InvalidArgument || s.Code() == codes.PermissionDenied) {
return nil
}
return err
})
if err != nil {
return fmt.Errorf("backoff cycle failed: %v", err)
}

return nil
}

func (a *Auth) foregroundGetTokenInfo(urlOpener URLOpener) (*internal.TokenInfo, error) {
providerConfig, err := internal.GetDeviceAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
if err != nil {
s, ok := gstatus.FromError(err)
if ok && s.Code() == codes.NotFound {
return nil, fmt.Errorf("no SSO provider returned from management. " +
"If you are using hosting Netbird see documentation at " +
"https://github.com/netbirdio/netbird/tree/main/management for details")
} else if ok && s.Code() == codes.Unimplemented {
return nil, fmt.Errorf("the management server, %s, does not support SSO providers, "+
"please update your servver or use Setup Keys to login", a.config.ManagementURL)
} else {
return nil, fmt.Errorf("getting device authorization flow info failed with error: %v", err)
}
}

hostedClient := internal.NewHostedDeviceFlow(
providerConfig.ProviderConfig.Audience,
providerConfig.ProviderConfig.ClientID,
providerConfig.ProviderConfig.TokenEndpoint,
providerConfig.ProviderConfig.DeviceAuthEndpoint,
)

flowInfo, err := hostedClient.RequestDeviceCode(context.TODO())
if err != nil {
return nil, fmt.Errorf("getting a request device code failed: %v", err)
}

go urlOpener.Open(flowInfo.VerificationURIComplete)

waitTimeout := time.Duration(flowInfo.ExpiresIn)
waitCTX, cancel := context.WithTimeout(a.ctx, waitTimeout*time.Second)
defer cancel()
tokenInfo, err := hostedClient.WaitToken(waitCTX, flowInfo)
if err != nil {
return nil, fmt.Errorf("waiting for browser login failed: %v", err)
}

return &tokenInfo, nil
}

func (a *Auth) withBackOff(ctx context.Context, bf func() error) error {
return backoff.RetryNotify(
bf,
backoff.WithContext(cmd.CLIBackOffSettings, ctx),
func(err error, duration time.Duration) {
log.Warnf("retrying Login to the Management service in %v due to error %v", duration, err)
})
}
37 changes: 37 additions & 0 deletions client/android/peer_notifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package android

// PeerInfo describe information about the peers. It designed for the UI usage
type PeerInfo struct {
IP string
FQDN string
ConnStatus string // Todo replace to enum
Direct bool
}

// PeerInfoCollection made for Java layer to get non default types as collection
type PeerInfoCollection interface {
Add(s string) PeerInfoCollection
Get(i int) string
Size() int
}

// PeerInfoArray is the implementation of the PeerInfoCollection
type PeerInfoArray struct {
items []PeerInfo
}

// Add new PeerInfo to the collection
func (array PeerInfoArray) Add(s PeerInfo) PeerInfoArray {
array.items = append(array.items, s)
return array
}

// Get return an element of the collection
func (array PeerInfoArray) Get(i int) *PeerInfo {
return &array.items[i]
}

// Size return with the size of the collection
func (array PeerInfoArray) Size() int {
return len(array.items)
}
Loading