From 4504d1b3910dcc22aa7d691814c499abedb02ecd Mon Sep 17 00:00:00 2001 From: "jan.hajek@zerops.io" Date: Wed, 3 Apr 2024 19:50:07 +0200 Subject: [PATCH 1/4] fix: (windows) paths --- Makefile | 11 ++++++++++- src/cmd/serviceDeploy.go | 10 +++++----- src/cmd/servicePush.go | 4 ++-- src/cmd/vpnDown.go | 2 +- src/cmd/vpnUp.go | 20 +++++++++++++------- src/constants/darwin.go | 10 +++++----- src/constants/linux.go | 10 +++++----- src/constants/zerops.go | 6 +++--- src/i18n/en.go | 12 ++++++++---- src/i18n/i18n.go | 12 ++++++++---- src/storage/exists.go | 16 ---------------- src/storage/handler.go | 30 ++++++++++-------------------- src/wg/darwin.go | 2 +- src/wg/linux.go | 2 +- src/wg/windows.go | 4 ++-- 15 files changed, 74 insertions(+), 77 deletions(-) delete mode 100644 src/storage/exists.go diff --git a/Makefile b/Makefile index 9c52e7e5..f15fda0c 100644 --- a/Makefile +++ b/Makefile @@ -10,4 +10,13 @@ test: lint: GOOS=darwin GOARCH=arm64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose GOOS=linux GOARCH=amd64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose - GOOS=windows GOARCH=amd64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose \ No newline at end of file + GOOS=windows GOARCH=amd64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose + +build-for-windows-amd: + GOOS=windows GOARCH=amd64 go build -o bin/zcli.exe cmd/zcli/main.go + +build-for-linux-amd: + GOOS=linux GOARCH=amd64 go build -o bin/zcli cmd/zcli/main.go + +build-for-darwin-arm: + GOOS=darwin GOARCH=arm64 go build -o bin/zcli cmd/zcli/main.go diff --git a/src/cmd/serviceDeploy.go b/src/cmd/serviceDeploy.go index 6c4a8240..92651df4 100644 --- a/src/cmd/serviceDeploy.go +++ b/src/cmd/serviceDeploy.go @@ -4,7 +4,7 @@ import ( "context" "io" "os" - "path" + "path/filepath" "time" "github.com/zeropsio/zcli/src/archiveClient" @@ -93,7 +93,7 @@ func serviceDeployCmd() *cmdBuilder.Cmd { size = s.Size() reader = packageFile } else { - tempFile := path.Join(os.TempDir(), appVersion.Id.Native()) + tempFile := filepath.Join(os.TempDir(), appVersion.Id.Native()) f, err := os.Create(tempFile) if err != nil { return err @@ -178,9 +178,9 @@ func serviceDeployCmd() *cmdBuilder.Cmd { cmdData.UxBlocks, []uxHelpers.Process{{ F: uxHelpers.CheckZeropsProcess(deployProcess.Id, cmdData.RestApiClient), - RunningMessage: i18n.T(i18n.PushRunning), - ErrorMessageMessage: i18n.T(i18n.PushFailed), - SuccessMessage: i18n.T(i18n.PushFinished), + RunningMessage: i18n.T(i18n.DeployRunning), + ErrorMessageMessage: i18n.T(i18n.DeployFailed), + SuccessMessage: i18n.T(i18n.DeployFinished), }}, ) diff --git a/src/cmd/servicePush.go b/src/cmd/servicePush.go index 804fef35..43548928 100644 --- a/src/cmd/servicePush.go +++ b/src/cmd/servicePush.go @@ -4,7 +4,7 @@ import ( "context" "io" "os" - "path" + "path/filepath" "time" "github.com/zeropsio/zcli/src/archiveClient" @@ -92,7 +92,7 @@ func servicePushCmd() *cmdBuilder.Cmd { size = s.Size() reader = packageFile } else { - tempFile := path.Join(os.TempDir(), appVersion.Id.Native()) + tempFile := filepath.Join(os.TempDir(), appVersion.Id.Native()) f, err := os.Create(tempFile) if err != nil { return err diff --git a/src/cmd/vpnDown.go b/src/cmd/vpnDown.go index 6541892f..744cdacb 100644 --- a/src/cmd/vpnDown.go +++ b/src/cmd/vpnDown.go @@ -42,7 +42,7 @@ func disconnectVpn(ctx context.Context, uxBlocks uxBlock.UxBlocks) error { } defer f.Close() - c := wg.DownCmd(ctx, filePath) + c := wg.DownCmd(ctx, filePath, constants.WgInterfaceName) _, err = cmdRunner.Run(c) if err != nil { return err diff --git a/src/cmd/vpnUp.go b/src/cmd/vpnUp.go index 04aa61aa..31d710b6 100644 --- a/src/cmd/vpnUp.go +++ b/src/cmd/vpnUp.go @@ -6,7 +6,6 @@ import ( "time" "github.com/pkg/errors" - "github.com/zeropsio/zcli/src/uxBlock" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/zeropsio/zcli/src/cliStorage" @@ -18,6 +17,7 @@ import ( "github.com/zeropsio/zcli/src/file" "github.com/zeropsio/zcli/src/i18n" "github.com/zeropsio/zcli/src/nettools" + "github.com/zeropsio/zcli/src/uxBlock" "github.com/zeropsio/zcli/src/uxBlock/styles" "github.com/zeropsio/zcli/src/uxHelpers" "github.com/zeropsio/zcli/src/wg" @@ -92,13 +92,19 @@ func vpnUpCmd() *cmdBuilder.Cmd { return err } - f, err := file.Open(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileMode) - if err != nil { - return err - } + if err := func() error { + f, err := file.Open(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileMode) + if err != nil { + return err + } + defer f.Close() - err = wg.GenerateConfig(f, privateKey, vpnSettings) - if err != nil { + err = wg.GenerateConfig(f, privateKey, vpnSettings) + if err != nil { + return err + } + return nil + }(); err != nil { return err } diff --git a/src/constants/darwin.go b/src/constants/darwin.go index 6e043cb9..0dde89dc 100644 --- a/src/constants/darwin.go +++ b/src/constants/darwin.go @@ -5,7 +5,7 @@ package constants import ( "os" - "path" + "path/filepath" ) func getDataFilePathsReceivers() []pathReceiver { @@ -21,7 +21,7 @@ func getDataFilePathsReceivers() []pathReceiver { func getLogFilePathReceivers() []pathReceiver { return []pathReceiver{ receiverFromEnv(CliLogFilePathEnvVar), - receiverFromPath(path.Join("/usr/local/var/log/", ZeropsLogFile)), + receiverFromPath(filepath.Join("/usr/local/var/log/", ZeropsLogFile)), receiverFromOsFunc(os.UserConfigDir, ZeropsDir, ZeropsLogFile), receiverFromOsFunc(os.UserHomeDir, ZeropsDir, ZeropsLogFile), receiverFromOsFunc(os.UserHomeDir, "zerops."+ZeropsLogFile), @@ -32,9 +32,9 @@ func getLogFilePathReceivers() []pathReceiver { func getWgConfigFilePathReceivers() []pathReceiver { return []pathReceiver{ receiverFromEnv(CliWgConfigPathEnvVar), - receiverFromPath(path.Join("/etc/wireguard/", WgConfigFile)), - receiverFromPath(path.Join("/usr/local/etc/wireguard/", WgConfigFile)), - receiverFromPath(path.Join("/opt/homebrew/etc/wireguard/", WgConfigFile)), + receiverFromPath(filepath.Join("/etc/wireguard/", WgConfigFile)), + receiverFromPath(filepath.Join("/usr/local/etc/wireguard/", WgConfigFile)), + receiverFromPath(filepath.Join("/opt/homebrew/etc/wireguard/", WgConfigFile)), receiverFromOsFunc(os.UserConfigDir, ZeropsDir, WgConfigFile), receiverFromOsFunc(os.UserHomeDir, ZeropsDir, WgConfigFile), receiverFromOsFunc(os.UserHomeDir, WgConfigFile), diff --git a/src/constants/linux.go b/src/constants/linux.go index 26fc412f..dc448eb2 100644 --- a/src/constants/linux.go +++ b/src/constants/linux.go @@ -5,7 +5,7 @@ package constants import ( "os" - "path" + "path/filepath" ) func getDataFilePathsReceivers() []pathReceiver { @@ -21,7 +21,7 @@ func getDataFilePathsReceivers() []pathReceiver { func getLogFilePathReceivers() []pathReceiver { return []pathReceiver{ receiverFromEnv(CliLogFilePathEnvVar), - receiverFromPath(path.Join("/var/log/", ZeropsLogFile)), + receiverFromPath(filepath.Join("/var/log/", ZeropsLogFile)), receiverFromOsFunc(os.UserConfigDir, ZeropsDir, ZeropsLogFile), receiverFromOsFunc(os.UserHomeDir, ZeropsDir, ZeropsLogFile), receiverFromOsFunc(os.UserHomeDir, "zerops."+ZeropsLogFile), @@ -32,9 +32,9 @@ func getLogFilePathReceivers() []pathReceiver { func getWgConfigFilePathReceivers() []pathReceiver { return []pathReceiver{ receiverFromEnv(CliWgConfigPathEnvVar), - receiverFromPath(path.Join("/etc/wireguard/", WgConfigFile)), - receiverFromPath(path.Join("/usr/local/etc/wireguard/", WgConfigFile)), - receiverFromPath(path.Join("/opt/homebrew/etc/wireguard/", WgConfigFile)), + receiverFromPath(filepath.Join("/etc/wireguard/", WgConfigFile)), + receiverFromPath(filepath.Join("/usr/local/etc/wireguard/", WgConfigFile)), + receiverFromPath(filepath.Join("/opt/homebrew/etc/wireguard/", WgConfigFile)), receiverFromOsFunc(os.UserConfigDir, ZeropsDir, WgConfigFile), receiverFromOsFunc(os.UserHomeDir, ZeropsDir, WgConfigFile), receiverFromOsFunc(os.UserHomeDir, WgConfigFile), diff --git a/src/constants/zerops.go b/src/constants/zerops.go index 44cc94dd..ea706412 100644 --- a/src/constants/zerops.go +++ b/src/constants/zerops.go @@ -2,7 +2,6 @@ package constants import ( "os" - "path" "path/filepath" "strings" @@ -17,6 +16,7 @@ const ( ZeropsDir = "zerops" ZeropsLogFile = "zerops.log" WgConfigFile = "zerops.conf" + WgInterfaceName = "zerops" CliDataFileName = "cli.data" CliDataFilePathEnvVar = "ZEROPS_CLI_DATA_FILE_PATH" CliLogFilePathEnvVar = "ZEROPS_CLI_LOG_FILE_PATH" @@ -35,7 +35,7 @@ func LogFilePath() (string, os.FileMode, error) { } func WgConfigFilePath() (string, os.FileMode, error) { - return checkReceivers(getWgConfigFilePathReceivers(), 0600, i18n.UnableToWriteLogFile) + return checkReceivers(getWgConfigFilePathReceivers(), 0600, i18n.UnableToWriteWgConfigFile) } func checkReceivers(pathReceivers []pathReceiver, fileMode os.FileMode, errorText string) (string, os.FileMode, error) { @@ -96,7 +96,7 @@ func findFirstWritablePath(paths []pathReceiver, fileMode os.FileMode) string { } func checkPath(filePath string, fileMode os.FileMode) (string, error) { - dir := path.Dir(filePath) + dir := filepath.Dir(filePath) if err := os.MkdirAll(dir, 0755); err != nil { return "", err diff --git a/src/i18n/en.go b/src/i18n/en.go index f8791231..ba447a15 100644 --- a/src/i18n/en.go +++ b/src/i18n/en.go @@ -106,6 +106,9 @@ var en = map[string]string{ "directory. The working directory is by default the current directory and can be changed\n" + "using the --workingDir flag. zCLI deploys selected directories and/or files to Zerops. \n\n" + "To build your application in Zerops, use the zcli push command instead.", + DeployRunning: "Deploy is running", + DeployFailed: "Deploy failed", + DeployFinished: "Deploy finished", // push CmdHelpPush: "the service push command.", @@ -242,10 +245,11 @@ var en = map[string]string{ CliLogFilePathEnvVar: "Path to a log file.", CliDataFilePathEnvVar: "Path to data file.", - UnknownTerminalMode: "Unknown terminal mode: %s. Falling back to auto-discovery. Possible values: auto, enabled, disabled.", - UnableToDecodeJsonFile: "Unable to decode json file: %s", - UnableToWriteCliData: "Unable to write zcli data, paths tested: %s", - UnableToWriteLogFile: "Unable to write zcli debug log file, paths tested: %s", + UnknownTerminalMode: "Unknown terminal mode: %s. Falling back to auto-discovery. Possible values: auto, enabled, disabled.", + UnableToDecodeJsonFile: "Unable to decode json file: %s", + UnableToWriteCliData: "Unable to write zcli data, paths tested: %s", + UnableToWriteLogFile: "Unable to write zcli debug log file, paths tested: %s", + UnableToWriteWgConfigFile: "Unable to write zcli wireguard config file, paths tested: %s", // args ArgsOnlyOneOptionalAllowed: "optional arg %s can be only the last one", diff --git a/src/i18n/i18n.go b/src/i18n/i18n.go index f982f2ac..d36b404a 100644 --- a/src/i18n/i18n.go +++ b/src/i18n/i18n.go @@ -111,6 +111,9 @@ const ( CmdHelpServiceDeploy = "CmdHelpServiceDeploy" CmdDescDeploy = "CmdDescDeploy" CmdDescDeployLong = "CmdDescDeployLong" + DeployRunning = "DeployRunning" + DeployFailed = "DeployFailed" + DeployFinished = "DeployFinished" // push CmdHelpPush = "CmdHelpPush" @@ -236,10 +239,11 @@ const ( CliLogFilePathEnvVar = "CliLogFilePathEnvVar" CliDataFilePathEnvVar = "CliDataFilePathEnvVar" - UnknownTerminalMode = "UnknownTerminalMode" - UnableToDecodeJsonFile = "UnableToDecodeJsonFile" - UnableToWriteCliData = "UnableToWriteCliData" - UnableToWriteLogFile = "UnableToWriteLogFile" + UnknownTerminalMode = "UnknownTerminalMode" + UnableToDecodeJsonFile = "UnableToDecodeJsonFile" + UnableToWriteCliData = "UnableToWriteCliData" + UnableToWriteLogFile = "UnableToWriteLogFile" + UnableToWriteWgConfigFile = "UnableToWriteWgConfigFile" // args ArgsOnlyOneOptionalAllowed = "ArgsOnlyOneOptionalAllowed" diff --git a/src/storage/exists.go b/src/storage/exists.go deleted file mode 100644 index ca85b772..00000000 --- a/src/storage/exists.go +++ /dev/null @@ -1,16 +0,0 @@ -package storage - -import ( - "os" -) - -func FileExists(path string) (bool, error) { - f, err := os.Stat(path) - if err == nil && !f.IsDir() { - return true, nil - } - if os.IsNotExist(err) { - return false, nil - } - return false, err -} diff --git a/src/storage/handler.go b/src/storage/handler.go index 2208a1d3..2110ceac 100644 --- a/src/storage/handler.go +++ b/src/storage/handler.go @@ -34,11 +34,17 @@ func New[T any](config Config) (*Handler[T], error) { } func (h *Handler[T]) load() error { - storageFileExists, err := FileExists(h.config.FilePath) + fileInfo, err := os.Stat(h.config.FilePath) + if os.IsNotExist(err) { + return nil + } if err != nil { return errors.WithStack(err) } - if !storageFileExists { + if fileInfo.Size() == 0 { + if err := os.Remove(h.config.FilePath); err != nil { + return errors.WithStack(err) + } return nil } @@ -48,29 +54,12 @@ func (h *Handler[T]) load() error { } defer f.Close() - // If the file is empty, set the default value and save it. - fi, err := f.Stat() - if err != nil { - return errors.WithStack(err) - } - if fi.Size() == 0 { - return h.Clear() - } - if err := json.NewDecoder(f).Decode(&h.data); err != nil { return errors.WithMessagef(err, i18n.T(i18n.UnableToDecodeJsonFile, h.config.FilePath)) } - return nil } -func (h *Handler[T]) Clear() error { - h.lock.Lock() - defer h.lock.Unlock() - var data T - return h.save(data) -} - func (h *Handler[T]) Update(callback func(T) T) (T, error) { h.lock.Lock() defer h.lock.Unlock() @@ -95,10 +84,11 @@ func (h *Handler[T]) save(data T) error { }(); err != nil { return err } + os.Remove(h.config.FilePath) + defer os.Remove(h.config.FilePath + ".new") if err := os.Rename(h.config.FilePath+".new", h.config.FilePath); err != nil { return errors.WithStack(err) } - os.Remove(h.config.FilePath + ".new") return nil } diff --git a/src/wg/darwin.go b/src/wg/darwin.go index 03ceb58f..68c72573 100644 --- a/src/wg/darwin.go +++ b/src/wg/darwin.go @@ -38,7 +38,7 @@ func UpCmd(ctx context.Context, filePath string) (err *exec.Cmd) { return exec.CommandContext(ctx, "wg-quick", "up", filePath) } -func DownCmd(ctx context.Context, filePath string) (err *exec.Cmd) { +func DownCmd(ctx context.Context, filePath, _ string) (err *exec.Cmd) { return exec.CommandContext(ctx, "wg-quick", "down", filePath) } diff --git a/src/wg/linux.go b/src/wg/linux.go index b7629a95..ffe9d157 100644 --- a/src/wg/linux.go +++ b/src/wg/linux.go @@ -37,7 +37,7 @@ func UpCmd(ctx context.Context, filePath string) (err *exec.Cmd) { return exec.CommandContext(ctx, "wg-quick", "up", filePath) } -func DownCmd(ctx context.Context, filePath string) (err *exec.Cmd) { +func DownCmd(ctx context.Context, filePath, _ string) (err *exec.Cmd) { return exec.CommandContext(ctx, "wg-quick", "down", filePath) } diff --git a/src/wg/windows.go b/src/wg/windows.go index 4c7a3516..efc6b72c 100644 --- a/src/wg/windows.go +++ b/src/wg/windows.go @@ -37,8 +37,8 @@ func UpCmd(ctx context.Context, filePath string) (err *exec.Cmd) { return exec.CommandContext(ctx, "wireguard", "/installtunnelservice", filePath) } -func DownCmd(ctx context.Context, filePath string) (err *exec.Cmd) { - return exec.CommandContext(ctx, "wireguard", "/uninstalltunnelservice", filePath) +func DownCmd(ctx context.Context, _, interfaceName string) (err *exec.Cmd) { + return exec.CommandContext(ctx, "wireguard", "/uninstalltunnelservice", interfaceName) } var vpnTmpl = ` From db8b19620ec3c47a73683f5e8a17993c189dbf78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Tue, 30 Apr 2024 15:40:34 +0200 Subject: [PATCH 2/4] fix: (windows) wireguard command permission elevation --- .gitignore | 1 + src/archiveClient/handler_findGitFiles.go | 10 +-- src/cmd/servicePush.go | 2 +- src/cmd/vpnUp.go | 19 ++--- src/cmdRunner/execCmd.go | 46 ++++++++++++ src/cmdRunner/run.go | 17 +++-- src/wg/darwin.go | 9 +-- src/wg/linux.go | 9 +-- src/wg/windows.go | 88 +++++++++++++++++++++-- 9 files changed, 164 insertions(+), 37 deletions(-) create mode 100644 src/cmdRunner/execCmd.go diff --git a/.gitignore b/.gitignore index 495c7004..329c2449 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea +.sandbox include/ .sandbox/ .tool-versions diff --git a/src/archiveClient/handler_findGitFiles.go b/src/archiveClient/handler_findGitFiles.go index c208449b..f43cdb71 100644 --- a/src/archiveClient/handler_findGitFiles.go +++ b/src/archiveClient/handler_findGitFiles.go @@ -3,9 +3,9 @@ package archiveClient import ( "bufio" "bytes" + "context" "io" "os" - "os/exec" "path/filepath" "strings" @@ -13,7 +13,7 @@ import ( "github.com/zeropsio/zcli/src/cmdRunner" ) -func (h *Handler) FindGitFiles(workingDir string) (res []File, _ error) { +func (h *Handler) FindGitFiles(ctx context.Context, workingDir string) (res []File, _ error) { workingDir, err := filepath.Abs(workingDir) if err != nil { return nil, err @@ -28,8 +28,8 @@ func (h *Handler) FindGitFiles(workingDir string) (res []File, _ error) { } } - createCmd := func(name string, arg ...string) *exec.Cmd { - cmd := exec.Command(name, arg...) + createCmd := func(name string, arg ...string) *cmdRunner.ExecCmd { + cmd := cmdRunner.CommandContext(ctx, name, arg...) cmd.Dir = workingDir return cmd } @@ -102,7 +102,7 @@ func (h *Handler) FindGitFiles(workingDir string) (res []File, _ error) { return res, nil } -func (h *Handler) listFiles(cmd *exec.Cmd, fn func(path string) error) error { +func (h *Handler) listFiles(cmd *cmdRunner.ExecCmd, fn func(path string) error) error { output, err := cmdRunner.Run(cmd) if err != nil { return err diff --git a/src/cmd/servicePush.go b/src/cmd/servicePush.go index 43548928..a2fa157b 100644 --- a/src/cmd/servicePush.go +++ b/src/cmd/servicePush.go @@ -98,7 +98,7 @@ func servicePushCmd() *cmdBuilder.Cmd { return err } defer os.Remove(tempFile) - files, err := arch.FindGitFiles(cmdData.Params.GetString("workingDir")) + files, err := arch.FindGitFiles(ctx, cmdData.Params.GetString("workingDir")) if err != nil { return err } diff --git a/src/cmd/vpnUp.go b/src/cmd/vpnUp.go index 31d710b6..1bc2fce8 100644 --- a/src/cmd/vpnUp.go +++ b/src/cmd/vpnUp.go @@ -92,19 +92,14 @@ func vpnUpCmd() *cmdBuilder.Cmd { return err } - if err := func() error { - f, err := file.Open(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileMode) - if err != nil { - return err - } - defer f.Close() + f, err := file.Open(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileMode) + if err != nil { + return err + } + defer f.Close() - err = wg.GenerateConfig(f, privateKey, vpnSettings) - if err != nil { - return err - } - return nil - }(); err != nil { + err = wg.GenerateConfig(f, privateKey, vpnSettings) + if err != nil { return err } diff --git a/src/cmdRunner/execCmd.go b/src/cmdRunner/execCmd.go new file mode 100644 index 00000000..0db04086 --- /dev/null +++ b/src/cmdRunner/execCmd.go @@ -0,0 +1,46 @@ +package cmdRunner + +import ( + "context" + "os/exec" +) + +type Func func(ctx context.Context) error + +func CommandContext(ctx context.Context, cmd string, args ...string) *ExecCmd { + return &ExecCmd{ + Cmd: exec.CommandContext(ctx, cmd, args...), + ctx: ctx, + } +} + +type ExecCmd struct { + *exec.Cmd + ctx context.Context + before Func + after Func +} + +func (e *ExecCmd) SetBefore(f Func) *ExecCmd { + e.before = f + return e +} + +func (e *ExecCmd) execBefore() error { + if e.before == nil { + return nil + } + return e.before(e.ctx) +} + +func (e *ExecCmd) SetAfter(f Func) *ExecCmd { + e.after = f + return e +} + +func (e *ExecCmd) execAfter() error { + if e.after == nil { + return nil + } + return e.after(e.ctx) +} diff --git a/src/cmdRunner/run.go b/src/cmdRunner/run.go index e88dcd8f..69290dcc 100644 --- a/src/cmdRunner/run.go +++ b/src/cmdRunner/run.go @@ -12,13 +12,8 @@ var ErrIpAlreadySet = errors.New("RTNETLINK answers: File exists") var ErrCannotFindDevice = errors.New(`Cannot find device "wg0"`) var ErrOperationNotPermitted = errors.New(`Operation not permitted`) -type ExecErrInterface interface { - error - ExitCode() int -} - type execError struct { - cmd *exec.Cmd + cmd *ExecCmd prev error exitCode int } @@ -39,13 +34,17 @@ func (e execError) Is(target error) bool { return errors.Is(e.prev, target) } -func Run(cmd *exec.Cmd) ([]byte, ExecErrInterface) { +func Run(cmd *ExecCmd) ([]byte, error) { output := &bytes.Buffer{} errOutput := &bytes.Buffer{} cmd.Stdout = output cmd.Stderr = errOutput cmd.Env = append(os.Environ(), cmd.Env...) + if err := cmd.execBefore(); err != nil { + return nil, err + } + if err := cmd.Run(); err != nil { exitCode := 0 var exitError *exec.ExitError @@ -81,5 +80,9 @@ func Run(cmd *exec.Cmd) ([]byte, ExecErrInterface) { return nil, execError } + if err := cmd.execAfter(); err != nil { + return nil, err + } + return output.Bytes(), nil } diff --git a/src/wg/darwin.go b/src/wg/darwin.go index 68c72573..2ee9cf09 100644 --- a/src/wg/darwin.go +++ b/src/wg/darwin.go @@ -10,6 +10,7 @@ import ( "text/template" "github.com/pkg/errors" + "github.com/zeropsio/zcli/src/cmdRunner" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/zeropsio/zcli/src/i18n" @@ -34,12 +35,12 @@ func GenerateConfig(f io.Writer, privateKey wgtypes.Key, vpnSettings output.Proj return template.Must(template.New("wg template").Parse(vpnTmpl)).Execute(f, data) } -func UpCmd(ctx context.Context, filePath string) (err *exec.Cmd) { - return exec.CommandContext(ctx, "wg-quick", "up", filePath) +func UpCmd(ctx context.Context, filePath string) (err *cmdRunner.ExecCmd) { + return cmdRunner.CommandContext(ctx, "wg-quick", "up", filePath) } -func DownCmd(ctx context.Context, filePath, _ string) (err *exec.Cmd) { - return exec.CommandContext(ctx, "wg-quick", "down", filePath) +func DownCmd(ctx context.Context, filePath, _ string) (err *cmdRunner.ExecCmd) { + return cmdRunner.CommandContext(ctx, "wg-quick", "down", filePath) } var vpnTmpl = ` diff --git a/src/wg/linux.go b/src/wg/linux.go index ffe9d157..f2c61aa9 100644 --- a/src/wg/linux.go +++ b/src/wg/linux.go @@ -10,6 +10,7 @@ import ( "text/template" "github.com/pkg/errors" + "github.com/zeropsio/zcli/src/cmdRunner" "github.com/zeropsio/zcli/src/i18n" "github.com/zeropsio/zerops-go/dto/output" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" @@ -33,12 +34,12 @@ func GenerateConfig(f io.Writer, privateKey wgtypes.Key, vpnSettings output.Proj return template.Must(template.New("wg template").Parse(vpnTmpl)).Execute(f, data) } -func UpCmd(ctx context.Context, filePath string) (err *exec.Cmd) { - return exec.CommandContext(ctx, "wg-quick", "up", filePath) +func UpCmd(ctx context.Context, filePath string) (err *cmdRunner.ExecCmd) { + return cmdRunner.CommandContext(ctx, "wg-quick", "up", filePath) } -func DownCmd(ctx context.Context, filePath, _ string) (err *exec.Cmd) { - return exec.CommandContext(ctx, "wg-quick", "down", filePath) +func DownCmd(ctx context.Context, filePath, _ string) (err *cmdRunner.ExecCmd) { + return cmdRunner.CommandContext(ctx, "wg-quick", "down", filePath) } var vpnTmpl = ` diff --git a/src/wg/windows.go b/src/wg/windows.go index efc6b72c..325bf105 100644 --- a/src/wg/windows.go +++ b/src/wg/windows.go @@ -6,15 +6,24 @@ package wg import ( "context" "io" + "os" "os/exec" + "path/filepath" + "strings" "text/template" "github.com/pkg/errors" + "github.com/zeropsio/zcli/src/cmdRunner" + "github.com/zeropsio/zcli/src/constants" "github.com/zeropsio/zcli/src/i18n" "github.com/zeropsio/zerops-go/dto/output" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) +// To install wireguard tunnel in windows wireguard.exe has to be run with elevated permissions. +// Only (simple) way I found to achieve this is to run Start-Process cmdlet with param '-Verb RunAS' +// https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.4 + func CheckWgInstallation() error { _, err := exec.LookPath("wireguard") if err != nil { @@ -33,12 +42,72 @@ func GenerateConfig(f io.Writer, privateKey wgtypes.Key, vpnSettings output.Proj return template.Must(template.New("wg template").Parse(vpnTmpl)).Execute(f, data) } -func UpCmd(ctx context.Context, filePath string) (err *exec.Cmd) { - return exec.CommandContext(ctx, "wireguard", "/installtunnelservice", filePath) +func UpCmd(ctx context.Context, filePath string) (err *cmdRunner.ExecCmd) { + return cmdRunner.CommandContext(ctx, + "powershell", + "-Command", + "Start-Process", "wireguard", + "-Verb", "RunAs", + "-ArgumentList "+formatArgumentList("/installtunnelservice", filePath), + ). + SetBefore(beforeUp(filePath)) } -func DownCmd(ctx context.Context, _, interfaceName string) (err *exec.Cmd) { - return exec.CommandContext(ctx, "wireguard", "/uninstalltunnelservice", interfaceName) +// beforeUp this function tries to remove previous zerops.conf from usual wireguard configuration +// dir (at %ProgramFiles%\WireGuard\Data\Configurations) and copy a newly generated one. +// It fails with error = nil because it's only for windows wireguard GUI. +func beforeUp(zeropsConfPath string) cmdRunner.Func { + return func(_ context.Context) error { + programFiles, set := os.LookupEnv("ProgramFiles") + if !set { + return nil + } + + wgConfigDir := filepath.Join(programFiles, "WireGuard", "Data", "Configurations") + stat, err := os.Stat(wgConfigDir) + if err != nil { + return nil + } + if !stat.IsDir() { + return nil + } + + wgConfFile := filepath.Join(wgConfigDir, constants.WgConfigFile) + // remove previous zerops.conf encrypted by wireguard.exe thus ending with .dpapi + // https://git.zx2c4.com/wireguard-windows/about/docs/enterprise.md + _ = os.Remove(wgConfFile + ".dpapi") + + wgConf, err := os.OpenFile(filepath.Join(wgConfigDir, constants.WgConfigFile), os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + return nil + } + defer wgConf.Close() + + zeropsConf, err := os.OpenFile(zeropsConfPath, os.O_RDONLY, 0666) + if err != nil { + _ = os.Remove(wgConfFile) + return nil + } + defer zeropsConf.Close() + + _, err = io.Copy(wgConf, zeropsConf) + if err != nil { + _ = os.Remove(wgConfFile) + return nil + } + + return nil + } +} + +func DownCmd(ctx context.Context, _, interfaceName string) (err *cmdRunner.ExecCmd) { + return cmdRunner.CommandContext(ctx, + "powershell", + "-Command", + "Start-Process", "wireguard", + "-Verb", "RunAs", + "-ArgumentList "+formatArgumentList("/uninstalltunnelservice", interfaceName), + ) } var vpnTmpl = ` @@ -60,3 +129,14 @@ Endpoint = {{.ProjectIpv4SharedEndpoint}} PersistentKeepalive = 5 ` + +func formatArgumentList(args ...string) string { + for i, a := range args { + args[i] = quote(a) + } + return strings.Join(args, ", ") +} + +func quote(in string) string { + return `"` + in + `"` +} From f08568e65d41de1258bbdd042a2bc68214c769ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Sun, 5 May 2024 12:24:31 +0200 Subject: [PATCH 3/4] ci: remove duplicate and unused linters and fix .golangci.yaml schema --- .golangci.yaml | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 75c0664a..bc9112fb 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,11 +1,12 @@ -concurrency: 16 run: - deadline: 10m + timeout: 10m + concurrency: 16 issues-exit-code: 1 tests: true output: - format: colored-line-number + formats: + - format: colored-line-number linters-settings: godox: @@ -17,25 +18,12 @@ issues: linters: enable: - - scopelint - - govet - - ineffassign - - varcheck - - unused - - deadcode - - bodyclose - - structcheck - - staticcheck - - godox - - rowserrcheck - - prealloc - asasalint - asciicheck - bidichk - bodyclose - containedctx - contextcheck - - deadcode - decorder - dogsled - dupword @@ -86,11 +74,9 @@ linters: - protogetter - reassign - rowserrcheck - - scopelint - sloglint - sqlclosecheck - staticcheck - - structcheck - tagalign - tagliatelle - tenv @@ -103,7 +89,6 @@ linters: - unparam - unused - usestdlibvars - - varcheck - wastedassign - whitespace - zerologlint From f5142e8c8c80d51d38e5212ab9ea7758dd4ff7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Sun, 5 May 2024 12:25:01 +0200 Subject: [PATCH 4/4] fix: add nolint directive where necessary --- src/cmdRunner/execCmd.go | 1 + src/wg/windows.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/cmdRunner/execCmd.go b/src/cmdRunner/execCmd.go index 0db04086..598273a6 100644 --- a/src/cmdRunner/execCmd.go +++ b/src/cmdRunner/execCmd.go @@ -16,6 +16,7 @@ func CommandContext(ctx context.Context, cmd string, args ...string) *ExecCmd { type ExecCmd struct { *exec.Cmd + //nolint:containedctx ctx context.Context before Func after Func diff --git a/src/wg/windows.go b/src/wg/windows.go index 325bf105..a7ae1c19 100644 --- a/src/wg/windows.go +++ b/src/wg/windows.go @@ -66,6 +66,7 @@ func beforeUp(zeropsConfPath string) cmdRunner.Func { wgConfigDir := filepath.Join(programFiles, "WireGuard", "Data", "Configurations") stat, err := os.Stat(wgConfigDir) if err != nil { + //nolint:nilerr return nil } if !stat.IsDir() { @@ -79,6 +80,7 @@ func beforeUp(zeropsConfPath string) cmdRunner.Func { wgConf, err := os.OpenFile(filepath.Join(wgConfigDir, constants.WgConfigFile), os.O_WRONLY|os.O_CREATE, 0666) if err != nil { + //nolint:nilerr return nil } defer wgConf.Close() @@ -86,6 +88,7 @@ func beforeUp(zeropsConfPath string) cmdRunner.Func { zeropsConf, err := os.OpenFile(zeropsConfPath, os.O_RDONLY, 0666) if err != nil { _ = os.Remove(wgConfFile) + //nolint:nilerr return nil } defer zeropsConf.Close() @@ -93,6 +96,7 @@ func beforeUp(zeropsConfPath string) cmdRunner.Func { _, err = io.Copy(wgConf, zeropsConf) if err != nil { _ = os.Remove(wgConfFile) + //nolint:nilerr return nil }