diff --git a/Makefile b/Makefile index c51d52e48c..f390b726c9 100644 --- a/Makefile +++ b/Makefile @@ -152,6 +152,7 @@ snapshot-clean: ## Cleans snapshot / release rm -rf ./dist host-apps: ## Build app + test -d apps && rm -r apps || true mkdir -p ./apps ${OPTS} go build ${BUILD_OPTS} -o ./apps/ ./cmd/apps/skychat ${OPTS} go build ${BUILD_OPTS} -o ./apps/ ./cmd/apps/skysocks @@ -180,6 +181,7 @@ host-apps-systray-windows: # Static Apps host-apps-static: ## Build app + test -d apps && rm -r apps || true mkdir -p ./apps ${STATIC_OPTS} go build -trimpath --ldflags '-linkmode external -extldflags "-static" -buildid=' -o ./apps/ ./cmd/apps/skychat ${STATIC_OPTS} go build -trimpath --ldflags '-linkmode external -extldflags "-static" -buildid=' -o ./apps/ ./cmd/apps/skysocks @@ -233,68 +235,61 @@ install-deps-ui: ## Install the UI dependencies run: ## Run skywire visor with skywire-config.json, and start a browser if running a hypervisor ./skywire-visor -bc ./skywire-config.json -## Run skywire from source, without compiling binaries - requires skywire cloned -run-source: +## Prepare to run skywire from source, without compiling binaries +prepare: test -d apps && rm -r apps || true - ln -s scripts/_apps apps - chmod +x apps/* + mkdir -p apps + ln ./scripts/_apps/skychat ./apps/ + ln ./scripts/_apps/skysocks ./apps/ + ln ./scripts/_apps/skysocks-client ./apps/ + ln ./scripts/_apps/vpn-server ./apps/ + ln ./scripts/_apps/vpn-client ./apps/ + chmod +x ./apps/* sudo echo "sudo cache" + +prepare-systray: prepare + rm apps/vpn* + ln -f ./scripts/_apps/vpn-server-systray ./apps/vpn-server + ln -f ./scripts/_apps/vpn-client-systray ./apps/vpn-client + +## Run skywire from source, without compiling binaries - requires skywire cloned +run-source: prepare go run ./cmd/skywire-cli/skywire-cli.go config gen -in | go run ./cmd/skywire-visor/skywire-visor.go -nb || true +## Run skywire from source, with vpn server enabled +run-systray: prepare-systray + go run -tags systray ./cmd/skywire-cli/skywire-cli.go config gen -ni | go run -tags systray ./cmd/skywire-visor/skywire-visor.go -nb || true + ## Run skywire from source, without compiling binaries - requires skywire cloned -run-vpnsrv: - test -d apps && rm -r apps || true - ln -s scripts/_apps apps - chmod +x apps/* - sudo echo "sudo cache" +run-vpnsrv: prepare go run ./cmd/skywire-cli/skywire-cli.go config gen -in --servevpn | go run ./cmd/skywire-visor/skywire-visor.go -nb || true ## Run skywire from source with test endpoints -run-source-test: - test -d apps && rm -r apps || true - ln -s scripts/_apps apps - chmod +x apps/* - sudo echo "sudo cache" +run-source-test: prepare go run ./cmd/skywire-cli/skywire-cli.go config gen -nit | go run ./cmd/skywire-visor/skywire-visor.go -nb || true ## Run skywire from source, with vpn server enabled -run-vpnsrv-test: - test -d apps && rm -r apps || true - ln -s scripts/_apps apps - chmod +x apps/* - sudo echo "sudo cache" +run-vpnsrv-test: prepare go run ./cmd/skywire-cli/skywire-cli.go config gen -nit --servevpn | go run ./cmd/skywire-visor/skywire-visor.go -nb || true +## Run skywire from source, with vpn server enabled +run-systray-test: prepare-systray + go run -tags systray ./cmd/skywire-cli/skywire-cli.go config gen -nit | go run -tags systray ./cmd/skywire-visor/skywire-visor.go -nb || true + ## Run skywire from source with dmsghttp config -run-source-dmsghttp: - test -d apps && rm -r apps || true - ln -s scripts/_apps apps - chmod +x apps/* - sudo echo "sudo cache" +run-source-dmsghttp: prepare go run ./cmd/skywire-cli/skywire-cli.go config gen -din | go run ./cmd/skywire-visor/skywire-visor.go -nb || true ## Run skywire from source with dmsghttp config and vpn server -run-vpnsrv-dmsghttp: - test -d apps && rm -r apps || true - ln -s scripts/_apps apps - chmod +x apps/* - sudo echo "sudo cache" +run-vpnsrv-dmsghttp: prepare go run ./cmd/skywire-cli/skywire-cli.go config gen -din --servevpn | go run ./cmd/skywire-visor/skywire-visor.go -nb || true ## Run skywire from source with dmsghttp config and test endpoints -run-source-dmsghttp-test: - test -d apps && rm -r apps || true - ln -s scripts/_apps apps - chmod +x apps/* - sudo echo "sudo cache" +run-source-dmsghttp-test: prepare go run ./cmd/skywire-cli/skywire-cli.go config gen -dint | go run ./cmd/skywire-visor/skywire-visor.go -nb || true ## Run skywire from source with dmsghttp config, vpn server, and test endpoints -run-vpnsrv-dmsghttp-test: - test -d apps && rm -r apps || true - ln -s scripts/_apps apps - chmod +x apps/* - sudo echo "sudo cache" +run-vpnsrv-dmsghttp-test: prepare go run ./cmd/skywire-cli/skywire-cli.go config gen -dint --servevpn | go run ./cmd/skywire-visor/skywire-visor.go -nb || true lint-ui: ## Lint the UI code diff --git a/cmd/apps/skychat/chat.go b/cmd/apps/skychat/skychat.go similarity index 100% rename from cmd/apps/skychat/chat.go rename to cmd/apps/skychat/skychat.go diff --git a/cmd/skywire-cli/commands/config/gen.go b/cmd/skywire-cli/commands/config/gen.go index 1a6fb1296e..b545ff20dd 100644 --- a/cmd/skywire-cli/commands/config/gen.go +++ b/cmd/skywire-cli/commands/config/gen.go @@ -4,9 +4,12 @@ import ( "encoding/json" "fmt" "os" + "os/exec" + "os/user" "path/filepath" "strings" + "github.com/bitfield/script" "github.com/sirupsen/logrus" "github.com/skycoin/skycoin/src/util/logging" "github.com/spf13/cobra" @@ -29,6 +32,7 @@ var ( retainHypervisors bool testEnv bool pkgEnv bool + usrEnv bool hypervisor bool hypervisorPKs string dmsgHTTP bool @@ -47,6 +51,7 @@ var ( all bool outunset bool ver string + root bool svcconf = strings.ReplaceAll(utilenv.ServiceConfAddr, "http://", "") //skyenv.DefaultServiceConfAddr testconf = strings.ReplaceAll(utilenv.TestServiceConfAddr, "http://", "") //skyenv.DefaultServiceConfAddr hiddenflags []string @@ -58,30 +63,49 @@ func init() { RootCmd.AddCommand(genConfigCmd) genConfigCmd.Flags().StringVarP(&serviceConfURL, "url", "a", svcconf, "services conf") + hiddenflags = append(hiddenflags, "url") genConfigCmd.Flags().BoolVarP(&bestProtocol, "bestproto", "b", false, "best protocol (dmsg | direct) based on location") genConfigCmd.Flags().BoolVarP(&disableauth, "noauth", "c", false, "disable authentication for hypervisor UI") + hiddenflags = append(hiddenflags, "noauth") genConfigCmd.Flags().BoolVarP(&dmsgHTTP, "dmsghttp", "d", false, "use dmsg connection to skywire services") + hiddenflags = append(hiddenflags, "dmsghttp") genConfigCmd.Flags().BoolVarP(&enableauth, "auth", "e", false, "enable auth on hypervisor UI") + hiddenflags = append(hiddenflags, "auth") genConfigCmd.Flags().BoolVarP(&force, "force", "f", false, "remove pre-existing config") + hiddenflags = append(hiddenflags, "force") genConfigCmd.Flags().StringVarP(&disableApps, "disableapps", "g", "", "comma separated list of apps to disable") + hiddenflags = append(hiddenflags, "disableapps") genConfigCmd.Flags().BoolVarP(&hypervisor, "ishv", "i", false, "local hypervisor configuration") genConfigCmd.Flags().StringVarP(&hypervisorPKs, "hvpks", "j", "", "list of public keys to use as hypervisor") genConfigCmd.Flags().StringVarP(&selectedOS, "os", "k", skyenv.OS, "(linux / macos / windows) paths") + hiddenflags = append(hiddenflags, "os") genConfigCmd.Flags().BoolVarP(&stdout, "stdout", "n", false, "write config to stdout") - genConfigCmd.Flags().StringVarP(&output, "out", "o", skyenv.ConfigName, "output config") - genConfigCmd.Flags().BoolVarP(&pkgEnv, "package", "p", false, "use paths for package "+skyenv.SkywirePath) + hiddenflags = append(hiddenflags, "stdout") + genConfigCmd.Flags().StringVarP(&output, "out", "o", "", "output config: "+skyenv.ConfigName) + genConfigCmd.Flags().BoolVarP(&pkgEnv, "pkg", "p", false, "use paths for package: "+skyenv.SkywirePath) + homepath := skyenv.HomePath() + if homepath != "" { + genConfigCmd.Flags().BoolVarP(&usrEnv, "user", "u", false, "use paths for user space: "+homepath) + } genConfigCmd.Flags().BoolVarP(&publicRPC, "publicrpc", "q", false, "allow rpc requests from LAN") + hiddenflags = append(hiddenflags, "publicrpc") genConfigCmd.Flags().BoolVarP(®en, "regen", "r", false, "re-generate existing config & retain keys") genConfigCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") + hiddenflags = append(hiddenflags, "sk") genConfigCmd.Flags().BoolVarP(&testEnv, "testenv", "t", false, "use test deployment "+testconf) + hiddenflags = append(hiddenflags, "testenv") genConfigCmd.Flags().BoolVarP(&vpnServerEnable, "servevpn", "v", false, "enable vpn server") + hiddenflags = append(hiddenflags, "servevpn") genConfigCmd.Flags().BoolVarP(&hide, "hide", "w", false, "dont print the config to the terminal") + hiddenflags = append(hiddenflags, "hide") genConfigCmd.Flags().BoolVarP(&retainHypervisors, "retainhv", "x", false, "retain existing hypervisors with regen") + hiddenflags = append(hiddenflags, "retainhv") genConfigCmd.Flags().StringVar(&ver, "version", "", "custom version testing override") + hiddenflags = append(hiddenflags, "version") genConfigCmd.Flags().BoolVar(&all, "all", false, "show all flags") genConfigCmd.Flags().StringVar(&print, "print", "", "parse test ; read config from file & print") + hiddenflags = append(hiddenflags, "print") - hiddenflags = []string{"url", "print", "noauth", "dmsghttp", "auth", "force", "disableapps", "os", "stdout", "publicrpc", "sk", "testenv", "servevpn", "hide", "retainhv", "print", "version"} for _, j := range hiddenflags { genConfigCmd.Flags().MarkHidden(j) //nolint } @@ -104,6 +128,7 @@ var genConfigCmd = &cobra.Command{ //set default output filename if output == "" { outunset = true + confPath = skyenv.ConfigName } else { confPath = output } @@ -113,15 +138,23 @@ var genConfigCmd = &cobra.Command{ force = false regen = false } + //hide defeats the purpose of stdout. + if (stdout) && (hide) { + logger.Fatal("Use of mutually exclusive flags: -w --hide and -n --stdout") + } //--force will delete a config, which excludes --regen if (force) && (regen) { logger.Fatal("Use of mutually exclusive flags: -f --force cannot override -r --regen") } - var err error - //hide defeats the purpose of stdout. - if (stdout) && (hide) { - logger.Fatal("Use of mutually exclusive flags: -w --hide and -n --stdout") + // these flags overwrite each other + if (usrEnv) && (pkgEnv) { + logger.Fatal("Use of mutually exclusive flags: -u --user and -p --pkg") } + //enable local hypervisor by default for user + if usrEnv { + hypervisor = true + } + var err error if dmsgHTTP { dmsgHTTPPath := skyenv.DMSGHTTPName if pkgEnv { @@ -172,16 +205,20 @@ var genConfigCmd = &cobra.Command{ //fetch the service endpoints services = visorconfig.Fetch(mLog, serviceConfURL, stdout) // skywire-cli config gen -ip || skywire-cli config gen -p - if !stdout && outunset && pkgEnv && (selectedOS == "linux") { - if hypervisor { - //default config hypervisor - configName = "skywire.json" - } else { - configName = "skywire-visor.json" + if !stdout && outunset && (selectedOS == "linux") { + if pkgEnv { + if hypervisor { + //default config hypervisor + configName = skyenv.Skywirejson + } else { + configName = skyenv.Skywirevisorjson + } + confPath = skyenv.SkywirePath + "/" + configName + } + if usrEnv { + confPath = skyenv.HomePath() + "/" + skyenv.ConfigName } - confPath = skyenv.SkywirePath + "/" + configName } - // Read in old config and obtain old secret key or generate a new random secret key // and obtain old hypervisors (if any) var sk cipher.SecKey @@ -204,7 +241,7 @@ var genConfigCmd = &cobra.Command{ } //create the conf - conf, err := visorconfig.MakeDefaultConfig(mLog, &sk, pkgEnv, testEnv, dmsgHTTP, hypervisor, output, hypervisorPKs, services) + conf, err := visorconfig.MakeDefaultConfig(mLog, &sk, usrEnv, pkgEnv, testEnv, dmsgHTTP, hypervisor, confPath, hypervisorPKs, services) if err != nil { logger.WithError(err).Fatal("Failed to create config.") } @@ -221,6 +258,46 @@ var genConfigCmd = &cobra.Command{ } } } + skywire := os.Args[0] + match := strings.Contains("/tmp/", skywire) + if (!stdout) || (!match) { + // Disable apps not found at bin_path with above exceptions for go run and stdout + if _, err := os.Stat(conf.Launcher.BinPath + "/" + "skychat"); err != nil { + if disableApps == "" { + disableApps = "skychat" + } else { + disableApps = disableApps + ",skychat" + } + } + if _, err := os.Stat(conf.Launcher.BinPath + "/" + "skysocks"); err != nil { + if disableApps == "" { + disableApps = "skysocks" + } else { + disableApps = disableApps + ",skysocks" + } + } + if _, err := os.Stat(conf.Launcher.BinPath + "/" + "skysocks-client"); err != nil { + if disableApps == "" { + disableApps = "skysocks-client" + } else { + disableApps = disableApps + ",skysocks-client" + } + } + if _, err := os.Stat(conf.Launcher.BinPath + "/" + "vpn-client"); err != nil { + if disableApps == "" { + disableApps = "vpn-client" + } else { + disableApps = disableApps + ",vpn-client" + } + } + if _, err := os.Stat(conf.Launcher.BinPath + "/" + "vpn-server"); err != nil { + if disableApps == "" { + disableApps = "vpn-server" + } else { + disableApps = disableApps + ",vpn-server" + } + } + } // Disable apps listed on --disable-apps flag if disableApps != "" { apps := strings.Split(disableApps, ",") @@ -251,7 +328,7 @@ var genConfigCmd = &cobra.Command{ } } // Check OS and enable auth windows or macos - if selectedOS == "windows" || selectedOS == "macos" { + if (selectedOS == "windows") || (selectedOS == "macos") { if hypervisor { conf.Hypervisor.EnableAuth = true } @@ -261,6 +338,33 @@ var genConfigCmd = &cobra.Command{ } //don't write file with stdout if !stdout { + userLvl, err := user.Current() + if err != nil { + logger.WithError(err).Error("Failed to detect user.") + } else { + if userLvl.Username == "root" { + root = true + } + } + //dont write config as root to non root owned dir & vice versa + if _, err = exec.LookPath("stat"); err == nil { + + confPath1, _ := filepath.Split(confPath) + if confPath1 == "" { + confPath1 = "./" + } + owner, err := script.Exec(`stat -c '%U' ` + confPath1).String() + if err != nil { + logger.Error("cannot stat: " + confPath1) + } + if ((owner != "root") || (owner != "root\n")) && root { + logger.Fatal("declined writing config as root to directory not owned by root") + } + if !root && ((owner == "root") || (owner == "root\n")) { + logger.Fatal("Insufficient permissions to write to the specified path") + } + } + // Save config to file. if err := conf.Flush(); err != nil { logger.WithError(err).Fatal("Failed to flush config to file.") diff --git a/cmd/skywire-visor/commands/nosystray.go b/cmd/skywire-visor/commands/nosystray.go index 86a050fa76..0a61b983ab 100644 --- a/cmd/skywire-visor/commands/nosystray.go +++ b/cmd/skywire-visor/commands/nosystray.go @@ -10,7 +10,7 @@ import ( ) func runApp() { - runVisor() + runVisor(nil) } // setStopFunction sets the stop function diff --git a/cmd/skywire-visor/commands/root.go b/cmd/skywire-visor/commands/root.go index 18c2ec5064..aea706c9b6 100644 --- a/cmd/skywire-visor/commands/root.go +++ b/cmd/skywire-visor/commands/root.go @@ -14,6 +14,7 @@ import ( "os/exec" "os/user" "path/filepath" + "regexp" "strings" "sync" "time" @@ -62,7 +63,7 @@ var ( all bool pkg bool pkg1 bool - + usr bool // skywire is the path to the running visor binary skywire string // workDir is the working directory where skywire-visor was executed @@ -74,26 +75,52 @@ var ( ) func init() { + thisUser, err := user.Current() + if err != nil { + panic(err) + } + if thisUser.Username == "root" { + root = true + } + rootCmd.Flags().SortFlags = false rootCmd.Flags().StringVarP(&confPath, "config", "c", skyenv.ConfigName, "config file to use") rootCmd.Flags().BoolVarP(&hypervisorUI, "hvui", "i", false, "run as hypervisor") - rootCmd.Flags().BoolVarP(&launchBrowser, "browser", "b", false, "open hypervisor ui in default web browser") + if ((skyenv.OS == "linux") && !root) || ((skyenv.OS == "mac") && !root) || (skyenv.OS == "win") { + rootCmd.Flags().BoolVarP(&launchBrowser, "browser", "b", false, "open hypervisor ui in default web browser") + } rootCmd.Flags().StringVarP(&remoteHypervisorPKs, "hv", "j", "", "add remote hypervisor PKs at runtime") + hiddenflags = append(hiddenflags, "hv") rootCmd.Flags().BoolVarP(&disableHypervisorPKs, "xhv", "k", false, "disable remote hypervisors set in config file") + hiddenflags = append(hiddenflags, "xhv") rootCmd.Flags().BoolVarP(&stdin, "stdin", "n", false, "read config from stdin") + hiddenflags = append(hiddenflags, "stdin") if skyenv.OS == "linux" { - rootCmd.Flags().BoolVar(&pkg, "ph", false, "use package config "+skyenv.SkywirePath+"/"+skyenv.Skywirejson) - rootCmd.Flags().BoolVar(&pkg1, "pv", false, "use package config "+skyenv.SkywirePath+"/"+skyenv.Skywirevisorjson) + if _, err := os.Stat(skyenv.SkywirePath + "/" + skyenv.Skywirejson); err == nil { + rootCmd.Flags().BoolVar(&pkg, "ph", false, "use package config "+skyenv.SkywirePath+"/"+skyenv.Skywirejson) + hiddenflags = append(hiddenflags, "ph") + } + if _, err := os.Stat(skyenv.SkywirePath + "/" + skyenv.Skywirevisorjson); err == nil { + rootCmd.Flags().BoolVar(&pkg1, "pv", false, "use package config "+skyenv.SkywirePath+"/"+skyenv.Skywirevisorjson) + hiddenflags = append(hiddenflags, "pv") + } + } + if _, err := os.Stat(skyenv.HomePath() + "/" + skyenv.ConfigName); err == nil { + rootCmd.Flags().BoolVarP(&usr, "user", "u", false, "use config at: $HOME/"+skyenv.ConfigName) } rootCmd.Flags().StringVarP(&pprofMode, "pprofmode", "p", "", "pprof mode: cpu, mem, mutex, block, trace, http") + hiddenflags = append(hiddenflags, "pprofmode") rootCmd.Flags().StringVarP(&pprofAddr, "pprofaddr", "q", "localhost:6060", "pprof http port") + hiddenflags = append(hiddenflags, "pprofaddr") rootCmd.Flags().StringVarP(&tag, "tag", "t", "skywire", "logging tag") + hiddenflags = append(hiddenflags, "tag") rootCmd.Flags().StringVarP(&syslogAddr, "syslog", "y", "", "syslog server address. E.g. localhost:514") + hiddenflags = append(hiddenflags, "syslog") rootCmd.Flags().StringVarP(&completion, "completion", "z", "", "[ bash | zsh | fish | powershell ]") + hiddenflags = append(hiddenflags, "completion") rootCmd.Flags().BoolVar(&all, "all", false, "show all flags") - hiddenflags = []string{"hv", "xhv", "stdin", "pprofmode", "pprofaddr", "tag", "syslog", "completion", "ph", "pv"} for _, j := range hiddenflags { rootCmd.Flags().MarkHidden(j) //nolint } @@ -150,8 +177,8 @@ var rootCmd = &cobra.Command{ _, hook := logstore.MakeStore(runtimeLogMaxEntries) log.AddHook(hook) if !stdin { - //multiple configs from flags - if (pkg && pkg1) || ((pkg || pkg1) && (confPath != "")) { + //error on multiple configs from flags + if (pkg && pkg1) || (pkg && usr) || (pkg1 && usr) || ((pkg || pkg1) && (confPath != "")) { fmt.Println("Error: multiple configs specified") os.Exit(1) } @@ -163,7 +190,9 @@ var rootCmd = &cobra.Command{ if pkg1 { confPath = skyenv.SkywirePath + "/" + skyenv.Skywirevisorjson } - + if usr { + confPath = skyenv.HomePath() + "/" + skyenv.ConfigName + } //enforce .json extension if !strings.HasSuffix(confPath, ".json") { //append .json @@ -180,6 +209,7 @@ var rootCmd = &cobra.Command{ } var fork string var branch string + var nocommit string //indicates how skywire was started skywire = os.Args[0] //indicates where skywire was started @@ -188,16 +218,6 @@ var rootCmd = &cobra.Command{ log.WithError(err).Fatal() } workDir = path - //determine if process has root permissions - thisUser, err := user.Current() - if err != nil { - log.Error("Unable to get current user: %s", err) - } - if (skyenv.OS == "linux") || (skyenv.OS == "mac") { - if thisUser.Username == "root" { - root = true - } - } //retrieve build info visorBuildInfo = buildinfo.Get() if visorBuildInfo.Version == "unknown" { @@ -214,8 +234,10 @@ var rootCmd = &cobra.Command{ if version, err := script.Exec(`git describe`).String(); err == nil { visorBuildInfo.Version = strings.ReplaceAll(version, "\n", "") if visorBuildInfo.Commit == "unknown" { - if commit, err := script.Exec(`git rev-list -1 HEAD`).String(); err == nil { - visorBuildInfo.Commit = strings.ReplaceAll(commit, "\n", "") + if nocommit, err = script.Exec(`git diff-index HEAD --`).String(); err == nil { + if commit, err := script.Exec(`git rev-list -1 HEAD`).String(); err == nil { + visorBuildInfo.Commit = strings.ReplaceAll(commit, "\n", "") + } } } if fork, err = script.Exec(`git config --get remote.origin.url`).String(); err == nil { @@ -226,7 +248,11 @@ var rootCmd = &cobra.Command{ fork = strings.ReplaceAll(fork, "github.com/", "") fork = strings.ReplaceAll(fork, ":/", "") fork = strings.ReplaceAll(fork, "\n", "") - if nofork := strings.Contains(fork, "skycoin/skywire"); err == nil { + nofork, err := regexp.MatchString("skycoin/skywire", fork) + if err != nil { + log.Error(err) + } else { + log.Info(nofork) if !nofork { fork = "" } @@ -246,18 +272,23 @@ var rootCmd = &cobra.Command{ } } } - log.WithField("version: ", visorBuildInfo.Version).Info() + log.WithField("version", visorBuildInfo.Version).Info() if visorBuildInfo.Date != "unknown" && visorBuildInfo.Date != "" { - log.WithField("built on: ", visorBuildInfo.Date).Info() + log.WithField("built on", visorBuildInfo.Date).Info() } if visorBuildInfo.Commit != "unknown" && visorBuildInfo.Commit != "" { - log.WithField("against commit: ", visorBuildInfo.Commit).Info() + if (nocommit != "") && (nocommit != "\n") { + log.Info("with changes since commit") + log.WithField("commit", visorBuildInfo.Commit).Info() + } else { + log.WithField("against commit", visorBuildInfo.Commit).Info() + } if fork != "" { - log.WithField("fork: ", fork).Info() + log.WithField("fork", fork).Info() } } if branch != "unknown" && branch != "" { - log.WithField("branch: ", branch).Info() + log.WithField("branch", branch).Info() } }, Run: func(_ *cobra.Command, _ []string) { @@ -266,7 +297,7 @@ var rootCmd = &cobra.Command{ Version: buildinfo.Version(), } -func runVisor() { +func runVisor(conf *visorconfig.V1) { var ok bool log := initLogger(tag, syslogAddr) store, hook := logstore.MakeStore(runtimeLogMaxEntries) @@ -275,7 +306,28 @@ func runVisor() { stopPProf := initPProf(log, tag, pprofMode, pprofAddr) defer stopPProf() - conf := initConfig(log, confPath) + if conf == nil { + conf = initConfig(log, confPath) + } + + //warn about creating files & directories as root in non root-owned dir + if _, err := exec.LookPath("stat"); err == nil { + pathtolocalpath := strings.ReplaceAll(conf.LocalPath, "local", "") + if owner, err := script.Exec(`stat -c '%U' ` + pathtolocalpath).String(); err == nil { + if ((owner != "root") || (owner != "root\n")) && root { + log.WithField("local path", conf.LocalPath).Warn() + log.Warn("writing as root to directory not owned by root!") + } + } + // fail on the reverse instance + if owner, err := script.Exec(`stat -c '%U' ` + conf.LocalPath).String(); err == nil { + if ((owner == "root") || (owner == "root\n")) && !root { + log.WithField("local path", conf.LocalPath).WithField("owner", "root").Error("folder belongs to root") + log.WithField("visor is root", root).Error("visor not started as root") + log.Fatal("Insufficient permissions to write to the local path: " + conf.LocalPath) + } + } + } if disableHypervisorPKs { conf.Hypervisors = []cipher.PubKey{} diff --git a/cmd/skywire-visor/commands/systray.go b/cmd/skywire-visor/commands/systray.go index cee11b4298..5acf3a9dbe 100644 --- a/cmd/skywire-visor/commands/systray.go +++ b/cmd/skywire-visor/commands/systray.go @@ -19,12 +19,13 @@ func runApp(args ...string) { l.WithError(err).Fatalln("Failed to read system tray icon") } + conf := initConfig(l, confPath) + go func() { - runVisor() + runVisor(conf) systray.Quit() }() - conf := initConfig(l, confPath) systray.Run(gui.GetOnGUIReady(sysTrayIcon, conf), gui.OnGUIQuit) } diff --git a/internal/gui/gui.go b/internal/gui/gui.go index 99b2f2d4df..9f77831533 100644 --- a/internal/gui/gui.go +++ b/internal/gui/gui.go @@ -64,24 +64,36 @@ var ( ) // GetOnGUIReady creates func to run on GUI startup. -func GetOnGUIReady(icon []byte, conf *visorconfig.V1) func() { +func GetOnGUIReady(icon []byte, conf *visorconfig.V1) (ret func()) { doneCh := make(chan bool, 1) logger := logging.NewMasterLogger() logger.SetLevel(logrus.InfoLevel) httpC := getHTTPClient(conf, context.Background(), logger) - return func() { - systray.SetTemplateIcon(icon, icon) - systray.SetTooltip("Skywire") - - initOpenVPNLinkBtn(conf) - initAdvancedButton(conf) - initVpnClientBtn(conf, httpC, logger) - initQuitBtn() - - go handleUserInteraction(conf, doneCh) + if isRoot() { + ret = func() { + systray.SetTemplateIcon(icon, icon) + systray.SetTooltip("Skywire") + initOpenVPNLinkBtn(conf) + initAdvancedButton(conf) + initVpnClientBtn(conf, httpC, logger) + initQuitBtn() + go handleRootInteraction(conf, doneCh) + } } + if !isRoot() { + ret = func() { + systray.SetTemplateIcon(icon, icon) + systray.SetTooltip("Skywire") + initOpenVPNLinkBtn(conf) + initAdvancedButton(conf) + initVpnClientBtn(conf, httpC, logger) + initQuitBtn() + go handleUserInteraction(conf, doneCh) + } + } + return ret } // OnGUIQuit is executed on GUI exit. @@ -116,6 +128,7 @@ func Stop() { } func initAdvancedButton(conf *visorconfig.V1) { + hvAddr := getHVAddr(conf) mAdvancedButton = systray.AddMenuItem("Advanced", "Advanced Menu") @@ -124,7 +137,12 @@ 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() { + mAdvancedButton.Hide() + mOpenHypervisor.Hide() + return + } // if visor's not running or hypervisor config is absent, // there won't be any way to open the hypervisor, so disable button if hvAddr == "" { @@ -160,7 +178,10 @@ func initAdvancedButton(conf *visorconfig.V1) { func initOpenVPNLinkBtn(vc *visorconfig.V1) { mVPNLink = systray.AddMenuItem("Open VPN UI", "Open VPN UI in browser") - + if checkRoot() { + mVPNLink.Hide() + return + } mVPNLink.Disable() // wait for the vpn client to start in the background @@ -417,6 +438,24 @@ func handleUserInteraction(conf *visorconfig.V1, doneCh chan<- bool) { } } +func handleRootInteraction(conf *visorconfig.V1, doneCh chan<- bool) { + for { + select { + // case <-mOpenHypervisor.ClickedCh: + // handleOpenHypervisor(conf) + case <-mVPNButton.ClickedCh: + handleVPNButton(conf, rpcC) + // case <-mVPNLink.ClickedCh: + // handleVPNLinkButton(conf) + case <-mUninstall.ClickedCh: + handleUninstall() + case <-mQuit.ClickedCh: + doneCh <- true + Stop() + } + } +} + func handleOpenHypervisor(conf *visorconfig.V1) { if err := openHypervisor(conf); err != nil { log.WithError(err).Errorln("Failed to open hypervisor") diff --git a/internal/gui/gui_unix.go b/internal/gui/gui_unix.go index 1e00cc9991..3f3b2cfbef 100644 --- a/internal/gui/gui_unix.go +++ b/internal/gui/gui_unix.go @@ -3,8 +3,22 @@ package gui -import "github.com/skycoin/skywire/pkg/util/osutil" +import ( + "os/user" + + "github.com/skycoin/skywire/pkg/util/osutil" +) func platformExecUninstall() error { return osutil.Run("/bin/bash", "-c", deinstallerPath) } + +func isRoot() bool { + userLvl, err := user.Current() + if err == nil { + if userLvl.Username == "root" { + return true + } + } + return false +} diff --git a/pkg/skyenv/values.go b/pkg/skyenv/values.go index 736cfd0d5e..03f1e2ae08 100644 --- a/pkg/skyenv/values.go +++ b/pkg/skyenv/values.go @@ -169,3 +169,9 @@ func Version() string { } return v } + +// HomePath gets the current user's home folder +func HomePath() string { + dir, _ := os.UserHomeDir() //nolint + return dir +} diff --git a/pkg/skyenv/values_darwin.go b/pkg/skyenv/values_darwin.go index 1471532ffb..9c42b076ea 100644 --- a/pkg/skyenv/values_darwin.go +++ b/pkg/skyenv/values_darwin.go @@ -6,13 +6,10 @@ package skyenv const ( //OS detection at runtime OS = "mac" - // SkywirePath is the path to the installation folder. SkywirePath = "/Library/Application Support/Skywire" ) -//TODO implement this similarly for macOS - // PackageConfig contains installation paths (for mac) func PackageConfig() PkgConfig { var pkgconfig PkgConfig @@ -22,3 +19,13 @@ func PackageConfig() PkgConfig { pkgconfig.Hypervisor.EnableAuth = true return pkgconfig } + +// UserConfig contains installation paths (for mac) +func UserConfig() PkgConfig { + var usrconfig PkgConfig + usrconfig.Launcher.BinPath = "/Library/Application Support/Skywire/apps" + usrconfig.LocalPath = HomePath() + "/.skywire/local" + usrconfig.Hypervisor.DbPath = HomePath() + "/.skywire/users.db" + usrconfig.Hypervisor.EnableAuth = true + return usrconfig +} diff --git a/pkg/skyenv/values_linux.go b/pkg/skyenv/values_linux.go index c3eefcdd55..b30a0febf8 100644 --- a/pkg/skyenv/values_linux.go +++ b/pkg/skyenv/values_linux.go @@ -6,7 +6,6 @@ package skyenv const ( //OS detection at runtime OS = "linux" - // SkywirePath is the path to the installation folder for the linux packages. SkywirePath = "/opt/skywire" ) @@ -16,7 +15,17 @@ func PackageConfig() PkgConfig { var pkgconfig PkgConfig pkgconfig.Launcher.BinPath = "/opt/skywire/apps" pkgconfig.LocalPath = "/opt/skywire/local" - pkgconfig.Hypervisor.DbPath = "/opt/skywire/users.db" //permissions errors if the process is not run as root. + pkgconfig.Hypervisor.DbPath = "/opt/skywire/users.db" pkgconfig.Hypervisor.EnableAuth = true return pkgconfig } + +// UserConfig contains installation paths (for linux) +func UserConfig() PkgConfig { + var usrconfig PkgConfig + usrconfig.Launcher.BinPath = "/opt/skywire/apps" + usrconfig.LocalPath = HomePath() + "/.skywire/local" + usrconfig.Hypervisor.DbPath = HomePath() + "/.skywire/users.db" + usrconfig.Hypervisor.EnableAuth = true + return usrconfig +} diff --git a/pkg/skyenv/values_windows.go b/pkg/skyenv/values_windows.go index 8cb4d60a8f..7329fa49c6 100644 --- a/pkg/skyenv/values_windows.go +++ b/pkg/skyenv/values_windows.go @@ -3,15 +3,13 @@ package skyenv -//OS detection at runtime const ( + //OS detection at runtime OS = "win" - // SkywirePath is the path to the installation folder + // SkywirePath is the path to the installation folder for the .msi SkywirePath = "C:/Program Files/Skywire" ) -//TODO implement this similarly for windows - // PackageConfig contains installation paths (for windows) func PackageConfig() PkgConfig { var pkgconfig PkgConfig @@ -21,3 +19,13 @@ func PackageConfig() PkgConfig { pkgconfig.Hypervisor.EnableAuth = true return pkgconfig } + +// UserConfig contains installation paths (for windows) +func UserConfig() PkgConfig { + var usrconfig PkgConfig + usrconfig.Launcher.BinPath = "C:/Program Files/Skywire/apps" + usrconfig.LocalPath = HomePath() + "/.skywire/local" + usrconfig.Hypervisor.DbPath = HomePath() + "/.skywire/users.db" + usrconfig.Hypervisor.EnableAuth = true + return usrconfig +} diff --git a/pkg/visor/visorconfig/config.go b/pkg/visor/visorconfig/config.go index c2f95e3d76..b8855d218d 100644 --- a/pkg/visor/visorconfig/config.go +++ b/pkg/visor/visorconfig/config.go @@ -109,12 +109,14 @@ func MakeBaseConfig(common *Common, testEnv bool, dmsgHTTP bool, services *Servi // The config's 'sk' field will be nil if not specified. // Generated config will be saved to 'confPath'. // This function always returns the latest config version. -func MakeDefaultConfig(log *logging.MasterLogger, sk *cipher.SecKey, pkgEnv bool, testEnv bool, dmsgHTTP bool, hypervisor bool, confPath, hypervisorPKs string, services *Services) (*V1, error) { +func MakeDefaultConfig(log *logging.MasterLogger, sk *cipher.SecKey, usrEnv bool, pkgEnv bool, testEnv bool, dmsgHTTP bool, hypervisor bool, confPath, hypervisorPKs string, services *Services) (*V1, error) { + if usrEnv && pkgEnv { + log.Fatal("usrEnv and pkgEnv are mutually exclusive") + } cc, err := NewCommon(log, confPath, sk) if err != nil { return nil, err } - var dmsgHTTPServersList *DmsgHTTPServers if dmsgHTTP { @@ -155,12 +157,10 @@ func MakeDefaultConfig(log *logging.MasterLogger, sk *cipher.SecKey, pkgEnv bool } } } - if hypervisor { config := hypervisorconfig.GenerateWorkDirConfig(false) conf.Hypervisor = &config } - if pkgEnv { pkgconfig := skyenv.PackageConfig() conf.LocalPath = pkgconfig.LocalPath @@ -170,9 +170,16 @@ func MakeDefaultConfig(log *logging.MasterLogger, sk *cipher.SecKey, pkgEnv bool conf.Hypervisor.DBPath = pkgconfig.Hypervisor.DbPath } } - + if usrEnv { + usrconfig := skyenv.UserConfig() + conf.LocalPath = usrconfig.LocalPath + conf.Launcher.BinPath = usrconfig.Launcher.BinPath + if conf.Hypervisor != nil { + conf.Hypervisor.EnableAuth = usrconfig.Hypervisor.EnableAuth + conf.Hypervisor.DBPath = usrconfig.Hypervisor.DbPath + } + } return conf, nil - } // SetDefaultTestingValues mutates configuration to use testing values diff --git a/scripts/_apps/skychat b/scripts/_apps/skychat index fdf50661b9..ba846709dc 100755 --- a/scripts/_apps/skychat +++ b/scripts/_apps/skychat @@ -1,2 +1,2 @@ #!/bin/bash -go run ../../cmd/apps/skychat/chat.go +go run ../../cmd/apps/skychat/skychat.go diff --git a/scripts/_apps/vpn-client-systray b/scripts/_apps/vpn-client-systray new file mode 100755 index 0000000000..93eaacc2d3 --- /dev/null +++ b/scripts/_apps/vpn-client-systray @@ -0,0 +1,2 @@ +#!/bin/bash +sudo go run -tags systray ../../cmd/apps/vpn-client/vpn-client.go diff --git a/scripts/_apps/vpn-server-systray b/scripts/_apps/vpn-server-systray new file mode 100755 index 0000000000..e94192e126 --- /dev/null +++ b/scripts/_apps/vpn-server-systray @@ -0,0 +1,2 @@ +#!/bin/bash +sudo go run -tags systray ../../cmd/apps/vpn-server/vpn-server.go