diff --git a/.appveyor.yml b/.appveyor.yml index 5da83f1258..f05939ff69 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -41,12 +41,30 @@ environment: appveyor_build_worker_image: ubuntu2004 for: - - # Linux and MacOS + - # Linux + skip_tags: true + matrix: + only: + - job_name: Linux + + install: + - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.44.2 + - sudo apt-get install gcc libgtk-3-dev libayatana-appindicator3-dev -y + - make dep + - sh: ci_scripts/create-ip-aliases.sh + + before_build: + - make check + + build_script: + - make build + - make build-systray + + - # MacOS skip_tags: true matrix: only: - job_name: Linux - - job_name: MacOS install: - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.44.2 diff --git a/cmd/apps/vpn-server/vpn-server.go b/cmd/apps/vpn-server/vpn-server.go index 54066cffe5..5fae705be4 100644 --- a/cmd/apps/vpn-server/vpn-server.go +++ b/cmd/apps/vpn-server/vpn-server.go @@ -30,6 +30,7 @@ var ( localPKStr = flag.String("pk", "", "Local PubKey") localSKStr = flag.String("sk", "", "Local SecKey") passcode = flag.String("passcode", "", "Passcode to authenticate connecting users") + networkIfc = flag.String("netifc", "", "Default network interface for multiple available interfaces") secure = flag.Bool("secure", true, "Forbid connections from clients to server local network") ) @@ -73,8 +74,9 @@ func main() { log.Infof("Got app listener, bound to %d", vpnPort) srvCfg := vpn.ServerConfig{ - Passcode: *passcode, - Secure: *secure, + Passcode: *passcode, + Secure: *secure, + NetworkInterface: *networkIfc, } srv, err := vpn.NewServer(srvCfg, log) if err != nil { diff --git a/cmd/skywire-cli/commands/config/update/root.go b/cmd/skywire-cli/commands/config/update/root.go index 834648c38a..64dadc0315 100644 --- a/cmd/skywire-cli/commands/config/update/root.go +++ b/cmd/skywire-cli/commands/config/update/root.go @@ -30,6 +30,7 @@ var ( addVPNServerPasscode string setVPNServerSecure string setVPNServerAutostart string + setVPNServerNetIfc string resetVPNServer bool addSkysocksClientSrv string resetSkysocksClient bool diff --git a/cmd/skywire-cli/commands/config/update/update.go b/cmd/skywire-cli/commands/config/update/update.go index 8146892922..9af44decd4 100644 --- a/cmd/skywire-cli/commands/config/update/update.go +++ b/cmd/skywire-cli/commands/config/update/update.go @@ -43,6 +43,7 @@ func init() { vpnServerUpdateCmd.Flags().StringVarP(&addVPNServerPasscode, "passwd", "s", "", "add passcode to vpn-server") vpnServerUpdateCmd.Flags().StringVar(&setVPNServerSecure, "secure", "", "change secure mode status of vpn-server") vpnServerUpdateCmd.Flags().StringVar(&setVPNServerAutostart, "autostart", "", "change autostart of vpn-server") + vpnServerUpdateCmd.Flags().StringVar(&setVPNServerNetIfc, "netifc", "", "set default network interface") vpnServerUpdateCmd.Flags().BoolVarP(&resetVPNServer, "reset", "r", false, "reset vpn-server configurations") } @@ -200,6 +201,9 @@ var vpnServerUpdateCmd = &cobra.Command{ if addVPNServerPasscode != "" { changeAppsConfig(conf, "vpn-server", "--passcode", addVPNServerPasscode) } + if setVPNServerNetIfc != "" { + changeAppsConfig(conf, "vpn-server", "--netifc", setVPNServerNetIfc) + } switch setVPNServerSecure { case "true": changeAppsConfig(conf, "vpn-server", "--secure", setVPNServerSecure) diff --git a/internal/gui/gui.go b/internal/gui/gui.go index 9f77831533..3a8967d0dd 100644 --- a/internal/gui/gui.go +++ b/internal/gui/gui.go @@ -138,7 +138,7 @@ func initAdvancedButton(conf *visorconfig.V1) { // if it's not installed via package, hide the uninstall button initUninstallBtn() //hide the buttons which could launch the browser if the process is run as root - if checkRoot() { + if isRoot() { mAdvancedButton.Hide() mOpenHypervisor.Hide() return @@ -178,7 +178,7 @@ func initAdvancedButton(conf *visorconfig.V1) { func initOpenVPNLinkBtn(vc *visorconfig.V1) { mVPNLink = systray.AddMenuItem("Open VPN UI", "Open VPN UI in browser") - if checkRoot() { + if isRoot() { mVPNLink.Hide() return } diff --git a/internal/vpn/server.go b/internal/vpn/server.go index 7b994aa017..758a103c45 100644 --- a/internal/vpn/server.go +++ b/internal/vpn/server.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net" + "strings" "sync" "github.com/sirupsen/logrus" @@ -29,16 +30,28 @@ type Server struct { // NewServer creates VPN server instance. func NewServer(cfg ServerConfig, l logrus.FieldLogger) (*Server, error) { + var defaultNetworkIfc string s := &Server{ cfg: cfg, log: l, ipGen: NewIPGenerator(), } - defaultNetworkIfc, err := netutil.DefaultNetworkInterface() + defaultNetworkIfcs, err := netutil.DefaultNetworkInterface() if err != nil { return nil, fmt.Errorf("error getting default network interface: %w", err) } + ifcs, hasMultiple := s.hasMutipleNetworkInterfaces(defaultNetworkIfcs) + if hasMultiple { + if cfg.NetworkInterface == "" { + return nil, fmt.Errorf("multiple default network interfaces detected...set a default one for VPN server or remove one: %v", ifcs) + } else if !s.validateInterface(ifcs, cfg.NetworkInterface) { + return nil, fmt.Errorf("network interface value in config is not in default network interfaces detected: %v", ifcs) + } + defaultNetworkIfc = cfg.NetworkInterface + } else { + defaultNetworkIfc = defaultNetworkIfcs + } l.Infof("Got default network interface: %s", defaultNetworkIfc) @@ -316,3 +329,20 @@ func (s *Server) sendServerErrHello(conn net.Conn, status HandshakeStatus) { s.log.WithError(err).Errorln("Error sending server hello") } } + +func (s *Server) hasMutipleNetworkInterfaces(defaultNetworkInterface string) ([]string, bool) { + networkInterfaces := strings.Split(defaultNetworkInterface, "\n") + if len(networkInterfaces) > 1 { + return networkInterfaces, true + } + return []string{}, false +} + +func (s *Server) validateInterface(ifcs []string, selectedIfc string) bool { + for _, ifc := range ifcs { + if ifc == selectedIfc { + return true + } + } + return false +} diff --git a/internal/vpn/server_config.go b/internal/vpn/server_config.go index b954765984..d855549107 100644 --- a/internal/vpn/server_config.go +++ b/internal/vpn/server_config.go @@ -2,6 +2,7 @@ package vpn // ServerConfig is a configuration for VPN server. type ServerConfig struct { - Passcode string - Secure bool + Passcode string + Secure bool + NetworkInterface string } diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 9164eab4e3..70639408c8 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -47,6 +47,7 @@ type API interface { SetAppPK(appName string, pk cipher.PubKey) error SetAppSecure(appName string, isSecure bool) error SetAppKillswitch(appName string, killswitch bool) error + SetAppNetworkInterface(appName string, netifc string) error LogsSince(timestamp time.Time, appName string) ([]string, error) GetAppStats(appName string) (appserver.AppStats, error) GetAppError(appName string) (string, error) @@ -411,6 +412,27 @@ func (v *Visor) SetAppPassword(appName, password string) error { return nil } +// SetAppNetworkInterface implements API. +func (v *Visor) SetAppNetworkInterface(appName, netifc string) error { + if skyenv.VPNServerName != appName { + return fmt.Errorf("app %s is not allowed to set network interface", appName) + } + + v.log.Infof("Changing %s network interface to %q", appName, netifc) + + const ( + netifcArgName = "--netifc" + ) + + if err := v.conf.UpdateAppArg(v.appL, appName, netifcArgName, netifc); err != nil { + return err + } + + v.log.Infof("Updated %v network interface", appName) + + return nil +} + // SetAppKillswitch implements API. func (v *Visor) SetAppKillswitch(appName string, killswitch bool) error { if appName != skyenv.VPNClientName { diff --git a/pkg/visor/hypervisor.go b/pkg/visor/hypervisor.go index cbc747ceec..057d1c5571 100644 --- a/pkg/visor/hypervisor.go +++ b/pkg/visor/hypervisor.go @@ -604,13 +604,14 @@ func (hv *Hypervisor) putApp() http.HandlerFunc { Secure *bool `json:"secure,omitempty"` Status *int `json:"status,omitempty"` Passcode *string `json:"passcode,omitempty"` + NetIfc *string `json:"netifc,omitempty"` PK *cipher.PubKey `json:"pk,omitempty"` } shouldRestartApp := func(r req) bool { // we restart the app if one of these fields was changed return r.Killswitch != nil || r.Secure != nil || r.Passcode != nil || - r.PK != nil + r.PK != nil || r.NetIfc != nil } var reqBody req @@ -661,6 +662,13 @@ func (hv *Hypervisor) putApp() http.HandlerFunc { } } + if reqBody.NetIfc != nil { + if err := ctx.API.SetAppNetworkInterface(ctx.App.Name, *reqBody.NetIfc); err != nil { + httputil.WriteJSON(w, r, http.StatusInternalServerError, err) + return + } + } + if shouldRestartApp(reqBody) { if err := ctx.API.RestartApp(ctx.App.Name); err != nil { httputil.WriteJSON(w, r, http.StatusInternalServerError, err) diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 5e4f3d363a..f12d6a20bb 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -253,6 +253,19 @@ func (r *RPC) SetAppPassword(in *SetAppPasswordIn, _ *struct{}) (err error) { return r.visor.SetAppPassword(in.AppName, in.Password) } +// SetAppNetworkInterfaceIn is input for SetAppNetworkInterface. +type SetAppNetworkInterfaceIn struct { + AppName string + NetIfc string +} + +// SetAppNetworkInterface sets network interface for the app. +func (r *RPC) SetAppNetworkInterface(in *SetAppNetworkInterfaceIn, _ *struct{}) (err error) { + defer rpcutil.LogCall(r.log, "SetAppNetworkInterface", in)(nil, &err) + + return r.visor.SetAppNetworkInterface(in.AppName, in.NetIfc) +} + // SetAppPKIn is input for SetAppPK. type SetAppPKIn struct { AppName string diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 0534b4dba8..3b85a5b14f 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -190,6 +190,14 @@ func (rc *rpcClient) SetAppKillswitch(appName string, killswitch bool) error { }, &struct{}{}) } +// SetAppKillswitch implements API. +func (rc *rpcClient) SetAppNetworkInterface(appName, netifc string) error { + return rc.Call("SetAppNetworkInterface", &SetAppNetworkInterfaceIn{ + AppName: appName, + NetIfc: netifc, + }, &struct{}{}) +} + // SetAppSecure implements API. func (rc *rpcClient) SetAppSecure(appName string, isSecure bool) error { return rc.Call("SetAppSecure", &SetAppBoolIn{ @@ -743,6 +751,21 @@ func (mc *mockRPCClient) SetAppPassword(string, string) error { }) } +// SetAppPassword implements API. +func (mc *mockRPCClient) SetAppNetworkInterface(string, string) error { + return mc.do(true, func() error { + const vpnServerName = "vpn-server" + + for i := range mc.o.Apps { + if mc.o.Apps[i].Name == vpnServerName { + return nil + } + } + + return fmt.Errorf("app of name '%s' does not exist", vpnServerName) + }) +} + // SetAppPK implements API. func (mc *mockRPCClient) SetAppPK(string, cipher.PubKey) error { return mc.do(true, func() error {