From ed4b4d81148eaf471dc6c068422ef8c71cb7af26 Mon Sep 17 00:00:00 2001 From: Jan Hajek Date: Fri, 10 Jul 2020 12:46:14 +0200 Subject: [PATCH] #17216 - zCli for macos --- .gitignore | 3 +- cmd/di.go | 7 +- go.mod | 3 - go.sum | 7 +- src/command/startVpn/handler.go | 6 +- ...andler_clean.go => handler_clean_linux.go} | 2 + .../startVpn/handler_clean_linux_macos.go | 19 ++++ src/command/startVpn/handler_isVpnAlive.go | 2 +- ...dler_setVpn.go => handler_setVpn_linux.go} | 4 +- src/command/startVpn/handler_setVpn_macos.go | 98 +++++++++++++++++++ src/command/stopVpn/handler.go | 13 +-- src/command/stopVpn/handler_clean_linux.go | 24 +++++ .../stopVpn/handler_clean_linux_macos.go | 19 ++++ src/helpers/cmdRunner/run.go | 40 ++++++++ src/service/certReader/handler.go | 2 +- src/service/sudoers/handler.go | 47 +++------ 16 files changed, 240 insertions(+), 56 deletions(-) rename src/command/startVpn/{handler_clean.go => handler_clean_linux.go} (95%) create mode 100644 src/command/startVpn/handler_clean_linux_macos.go rename src/command/startVpn/{handler_setVpn.go => handler_setVpn_linux.go} (99%) create mode 100644 src/command/startVpn/handler_setVpn_macos.go create mode 100644 src/command/stopVpn/handler_clean_linux.go create mode 100644 src/command/stopVpn/handler_clean_linux_macos.go create mode 100644 src/helpers/cmdRunner/run.go diff --git a/.gitignore b/.gitignore index e5f8f24c..2c9f5d3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -zcli.config.yml \ No newline at end of file +zcli.config.yml +zcli.data \ No newline at end of file diff --git a/cmd/di.go b/cmd/di.go index 504ff301..6a112749 100644 --- a/cmd/di.go +++ b/cmd/di.go @@ -33,11 +33,14 @@ func createSudoers() *sudoers.Handler { } func createStorage() (*storage.Handler, error) { - tempDir := os.TempDir() + currentDir, err := os.Getwd() + if err != nil { + return nil, err + } return storage.New( storage.Config{ - FilePath: path.Join(tempDir, "zcli.data"), + FilePath: path.Join(currentDir, "zcli.data"), }, ) } diff --git a/go.mod b/go.mod index 6de458f0..3ea714d2 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/zerops-io/zcli go 1.14 require ( - github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/golang/protobuf v1.4.2 github.com/google/uuid v1.1.1 @@ -15,10 +14,8 @@ require ( github.com/spf13/cobra v1.0.0 github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.6.3 - golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 // indirect golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect golang.org/x/text v0.3.2 // indirect google.golang.org/grpc v1.21.0 gopkg.in/ini.v1 v1.55.0 // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect ) diff --git a/go.sum b/go.sum index 00bec223..9f833ef6 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,4 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -git.vsh-labs.cz/cml/nest v0.0.30 h1:/D9n9dVd3bAbOU+4wJecOc7wBHBnOAc2JY9yp60rVN8= -git.vsh-labs.cz/cml/nest v0.0.30/go.mod h1:thiWZ8/Nx6YndGNrdgx7ReczCGiKI4buH1EM3hQ6ll4= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -23,8 +21,6 @@ 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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -95,9 +91,11 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= @@ -219,6 +217,7 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ= gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/src/command/startVpn/handler.go b/src/command/startVpn/handler.go index dc2cc9a8..a6d7fdd7 100644 --- a/src/command/startVpn/handler.go +++ b/src/command/startVpn/handler.go @@ -134,7 +134,11 @@ func (h *Handler) Run(ctx context.Context, config RunConfig) error { return err } - h.logger.Info("\nclient is connected \n") + if h.isVpnAlive() { + h.logger.Info("\nvpn connection is established\n") + } else { + return errors.New("vpn is not connected") + } h.storage.Data.ProjectId = project.GetId() err = h.storage.Save() diff --git a/src/command/startVpn/handler_clean.go b/src/command/startVpn/handler_clean_linux.go similarity index 95% rename from src/command/startVpn/handler_clean.go rename to src/command/startVpn/handler_clean_linux.go index d69d21e5..f66cc32d 100644 --- a/src/command/startVpn/handler_clean.go +++ b/src/command/startVpn/handler_clean_linux.go @@ -1,3 +1,5 @@ +// +build linux + package startVpn import ( diff --git a/src/command/startVpn/handler_clean_linux_macos.go b/src/command/startVpn/handler_clean_linux_macos.go new file mode 100644 index 00000000..1af070d6 --- /dev/null +++ b/src/command/startVpn/handler_clean_linux_macos.go @@ -0,0 +1,19 @@ +// +build darwin + +package startVpn + +import "os/exec" + +func (h *Handler) cleanVpn() error { + + var err error + + cmd := "ps aux | grep wireguard | grep -v grep | awk '{print $2}' | xargs sudo kill" + + _, err = h.sudoers.RunCommand(exec.Command("bash", "-c", cmd)) + if err != nil { + return err + } + + return nil +} diff --git a/src/command/startVpn/handler_isVpnAlive.go b/src/command/startVpn/handler_isVpnAlive.go index a01c7594..8d5ad940 100644 --- a/src/command/startVpn/handler_isVpnAlive.go +++ b/src/command/startVpn/handler_isVpnAlive.go @@ -11,7 +11,7 @@ func (h *Handler) isVpnAlive() bool { } for i := 0; i < 3; i++ { - _, err := exec.Command("ping", h.storage.Data.ServerIp, "-c", "1", "-W", "1").Output() + _, err := exec.Command("ping6", h.storage.Data.ServerIp, "-c", "1").Output() if err != nil { continue } diff --git a/src/command/startVpn/handler_setVpn.go b/src/command/startVpn/handler_setVpn_linux.go similarity index 99% rename from src/command/startVpn/handler_setVpn.go rename to src/command/startVpn/handler_setVpn_linux.go index 83eb8a8e..9395c7ce 100644 --- a/src/command/startVpn/handler_setVpn.go +++ b/src/command/startVpn/handler_setVpn_linux.go @@ -1,3 +1,5 @@ +// +build linux + package startVpn import ( @@ -9,9 +11,7 @@ import ( "strconv" "github.com/google/uuid" - "github.com/zerops-io/zcli/src/service/sudoers" - "github.com/zerops-io/zcli/src/zeropsVpnProtocol" ) diff --git a/src/command/startVpn/handler_setVpn_macos.go b/src/command/startVpn/handler_setVpn_macos.go new file mode 100644 index 00000000..009c47e4 --- /dev/null +++ b/src/command/startVpn/handler_setVpn_macos.go @@ -0,0 +1,98 @@ +// +build darwin + +package startVpn + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" + "regexp" + "strconv" + + "github.com/zerops-io/zcli/src/service/sudoers" + + "github.com/google/uuid" + "github.com/zerops-io/zcli/src/zeropsVpnProtocol" +) + +func (h *Handler) setVpn(selectedVpnAddress, privateKey string, response *zeropsVpnProtocol.StartVpnResponse) error { + var err error + + output, err := h.sudoers.RunCommand(exec.Command("wireguard-go", "utun")) + if err != nil { + return err + } + + re := regexp.MustCompile(`INFO: \((.*)\)`) + submatches := re.FindSubmatch(output) + if len(submatches) != 2 { + return errors.New("vpn interface not found") + } + + interfaceName := string(submatches[1]) + + { + privateKeyName := uuid.New().String() + tempPrivateKeyFile := path.Join(os.TempDir(), privateKeyName) + + fmt.Println(tempPrivateKeyFile) + err = ioutil.WriteFile(tempPrivateKeyFile, []byte(privateKey), 0755) + if err != nil { + return err + } + _, err = h.sudoers.RunCommand(exec.Command("wg", "set", interfaceName, "private-key", tempPrivateKeyFile)) + if err != nil { + return err + } + err = os.Remove(tempPrivateKeyFile) + if err != nil { + return err + } + } + + _, err = h.sudoers.RunCommand(exec.Command("wg", "set", interfaceName, "listen-port", wireguardPort)) + if err != nil { + return err + } + + clientIp := zeropsVpnProtocol.FromProtoIP(response.GetVpn().GetAssignedClientIp()) + serverIp := zeropsVpnProtocol.FromProtoIP(response.GetVpn().GetServerIp()) + vpnRange := zeropsVpnProtocol.FromProtoIPRange(response.GetVpn().GetVpnIpRange()) + + args := []string{ + "set", interfaceName, + "peer", response.GetVpn().GetServerPublicKey(), + "allowed-ips", vpnRange.String(), + "endpoint", selectedVpnAddress + ":" + strconv.Itoa(int(response.GetVpn().GetPort())), + "persistent-keepalive", "25", + } + _, err = h.sudoers.RunCommand(exec.Command("wg", args...)) + if err != nil { + if !errors.Is(err, sudoers.IpAlreadySetErr) { + panic(err) + } + } + + _, err = h.sudoers.RunCommand(exec.Command("ifconfig", interfaceName, "inet6", clientIp.String(), "mtu", "1420")) + if err != nil { + return err + } + + _, err = h.sudoers.RunCommand(exec.Command("route", "add", "-inet6", vpnRange.String(), serverIp.String())) + if err != nil { + return err + } + + h.logger.Debug("assigned client address: " + clientIp.String()) + h.logger.Debug("assigned vpn server: " + selectedVpnAddress + ":" + strconv.Itoa(int(response.GetVpn().GetPort()))) + h.logger.Debug("server public key: " + response.GetVpn().GetServerPublicKey()) + h.logger.Debug("serverIp address: " + serverIp.String()) + h.logger.Debug("vpnRange: " + vpnRange.String()) + + h.storage.Data.ServerIp = serverIp.String() + + return nil +} diff --git a/src/command/stopVpn/handler.go b/src/command/stopVpn/handler.go index c0e8d53a..8470edc4 100644 --- a/src/command/stopVpn/handler.go +++ b/src/command/stopVpn/handler.go @@ -2,12 +2,9 @@ package stopVpn import ( "context" - "errors" - "os/exec" - - "github.com/zerops-io/zcli/src/service/storage" "github.com/zerops-io/zcli/src/service/logger" + "github.com/zerops-io/zcli/src/service/storage" "github.com/zerops-io/zcli/src/service/sudoers" ) @@ -40,11 +37,9 @@ func New( func (h *Handler) Run(_ context.Context, _ RunConfig) error { - _, err := h.sudoers.RunCommand(exec.Command("ip", "link", "del", "dev", "wg0")) + err := h.cleanVpn() if err != nil { - if !errors.Is(err, sudoers.CannotFindDeviceErr) { - return err - } + return err } h.storage.Data.ProjectId = "" @@ -54,5 +49,7 @@ func (h *Handler) Run(_ context.Context, _ RunConfig) error { return err } + h.logger.Info("\nvpn connection was closed\n") + return nil } diff --git a/src/command/stopVpn/handler_clean_linux.go b/src/command/stopVpn/handler_clean_linux.go new file mode 100644 index 00000000..15a7d0bf --- /dev/null +++ b/src/command/stopVpn/handler_clean_linux.go @@ -0,0 +1,24 @@ +// +build linux + +package stopVpn + +import ( + "errors" + "os/exec" + + "github.com/zerops-io/zcli/src/service/sudoers" +) + +func (h *Handler) cleanVpn() error { + + var err error + + _, err = h.sudoers.RunCommand(exec.Command("ip", "link", "del", "dev", "wg0")) + if err != nil { + if !errors.Is(err, sudoers.CannotFindDeviceErr) { + return err + } + } + + return nil +} diff --git a/src/command/stopVpn/handler_clean_linux_macos.go b/src/command/stopVpn/handler_clean_linux_macos.go new file mode 100644 index 00000000..b5b0b221 --- /dev/null +++ b/src/command/stopVpn/handler_clean_linux_macos.go @@ -0,0 +1,19 @@ +// +build darwin + +package stopVpn + +import "os/exec" + +func (h *Handler) cleanVpn() error { + + var err error + + cmd := "ps aux | grep wireguard | grep -v grep | awk '{print $2}' | xargs sudo kill" + + _, err = h.sudoers.RunCommand(exec.Command("bash", "-c", cmd)) + if err != nil { + return err + } + + return nil +} diff --git a/src/helpers/cmdRunner/run.go b/src/helpers/cmdRunner/run.go new file mode 100644 index 00000000..17512857 --- /dev/null +++ b/src/helpers/cmdRunner/run.go @@ -0,0 +1,40 @@ +package cmdRunner + +import ( + "bytes" + "errors" + "os/exec" + "strings" +) + +var IpAlreadySetErr = errors.New("RTNETLINK answers: File exists") +var CannotFindDeviceErr = errors.New(`Cannot find device "wg0"`) +var OperationNotPermitted = errors.New(`Operation not permitted`) + +func Run(cmd *exec.Cmd) ([]byte, error) { + output := &bytes.Buffer{} + errOutput := &bytes.Buffer{} + cmd.Stdout = output + cmd.Stderr = errOutput + + if err := cmd.Run(); err != nil { + + + if errOutput.Len() > 0 { + errOutputString := string(errOutput.Bytes()[0 : errOutput.Len()-1]) + + if strings.Contains(errOutputString, OperationNotPermitted.Error()) { + return nil, OperationNotPermitted + } + + for _, e := range []error{IpAlreadySetErr, CannotFindDeviceErr} { + if errOutputString == e.Error() { + return nil, e + } + } + } + return nil, err + } + + return output.Bytes(), nil +} diff --git a/src/service/certReader/handler.go b/src/service/certReader/handler.go index 81f67fec..ac25ca36 100644 --- a/src/service/certReader/handler.go +++ b/src/service/certReader/handler.go @@ -32,7 +32,7 @@ func New(config Config) (h *Handler, err error) { tokens := strings.Split(config.Token, ";") if len(tokens) != 3 { - return h, errors.New("wrong token") + return h, errors.New("invalid credentials, try `login` command") } if h.CaData, err = readData(tokens[0]); err != nil { diff --git a/src/service/sudoers/handler.go b/src/service/sudoers/handler.go index 683d5378..25520f7e 100644 --- a/src/service/sudoers/handler.go +++ b/src/service/sudoers/handler.go @@ -1,15 +1,15 @@ package sudoers import ( - "bytes" "errors" "os/exec" - "strings" + + "github.com/zerops-io/zcli/src/helpers/cmdRunner" ) -var IpAlreadySetErr = errors.New("RTNETLINK answers: File exists") -var CannotFindDeviceErr = errors.New(`Cannot find device "wg0"`) -var OperationNotPermitted = errors.New(`Operation not permitted`) +var IpAlreadySetErr = cmdRunner.IpAlreadySetErr +var CannotFindDeviceErr = cmdRunner.CannotFindDeviceErr +var OperationNotPermitted = cmdRunner.OperationNotPermitted type Config struct { } @@ -27,42 +27,23 @@ func New(config Config) *Handler { // command with installation if operation is not permitted func (h *Handler) RunCommand(cmd *exec.Cmd) ([]byte, error) { - output, err := runCommand(cmd) + sudoCmd := exec.Command("sudo", cmd.Args...) + sudoCmd.Env = cmd.Env + sudoCmd.Stdin = cmd.Stdin + sudoCmd.Stderr = cmd.Stderr + sudoCmd.Stdout = cmd.Stdout + sudoCmd.Dir = cmd.Dir + + output, err := cmdRunner.Run(sudoCmd) if err != nil { if errors.Is(err, OperationNotPermitted) { newCmd := exec.Command("sudo", cmd.Args...) - output, err = runCommand(newCmd) + output, err = cmdRunner.Run(newCmd) } } return output, err } - -func runCommand(cmd *exec.Cmd) ([]byte, error) { - output := &bytes.Buffer{} - errOutput := &bytes.Buffer{} - cmd.Stdout = output - cmd.Stderr = errOutput - - if err := cmd.Run(); err != nil { - if errOutput.Len() > 0 { - errOutputString := string(errOutput.Bytes()[0 : errOutput.Len()-1]) - - if strings.Contains(errOutputString, OperationNotPermitted.Error()) { - return nil, OperationNotPermitted - } - - for _, e := range []error{IpAlreadySetErr, CannotFindDeviceErr} { - if errOutputString == e.Error() { - return nil, e - } - } - } - return nil, err - } - - return output.Bytes(), nil -}