From 6ba3272dc0126bf4a5c4b0e8b17597de4446858c Mon Sep 17 00:00:00 2001 From: David Randall Date: Sun, 12 Nov 2023 13:24:24 -0500 Subject: [PATCH 01/19] [Feature] Add cross site scripting --- agent/agent_unix.go | 43 ++++++++++++++++++++++++++++++++++++++++-- agent/agent_windows.go | 8 ++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/agent/agent_unix.go b/agent/agent_unix.go index 80d81c1..c77c52e 100644 --- a/agent/agent_unix.go +++ b/agent/agent_unix.go @@ -194,10 +194,49 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, opts := a.NewCMDOpts() opts.IsScript = true - opts.Shell = f.Name() - opts.Args = args + switch shell { + case "nushell": + // FIXME: Make this dynamic and use /opt/tacticalagent/bin/nu + opts.Shell = "/usr/local/bin/nu" + opts.Args = append([]string{ + "--no-config-file", + f.Name(), + }, + args...) + + case "deno": + // FIXME: Make this dynamic and use /opt/tacticalagent/bin/nu + opts.Shell = "/usr/local/bin/deno" + opts.Args = []string{ + "run", + "--no-prompt", + } + + // Search the environment variables for DENO_PERMISSIONS and use that to set permissions for the script. + // https://docs.deno.com/runtime/manual/basics/permissions#permissions-list + // DENO_PERMISSIONS is not an official environment variable. + // https://docs.deno.com/runtime/manual/basics/env_variables + // TODO: Remove DENO_PERMISSIONS from the environment variables. + for _, v := range envVars { + if strings.HasPrefix(v, "DENO_PERMISSIONS=") { + permissions := strings.Split(v, "=")[1] + opts.Args = append(opts.Args, strings.Split(permissions, " ")...) + } + } + + // Can't append a variadic slice after a string arg. + // https://pkg.go.dev/builtin#append + opts.Args = append(opts.Args, f.Name()) + opts.Args = append(opts.Args, args...) + + default: + opts.Shell = f.Name() + opts.Args = args + } + opts.EnvVars = envVars opts.Timeout = time.Duration(timeout) + a.Logger.Debugln("RunScript(): ", opts.Shell, opts.Args) out := a.CmdV2(opts) retError := "" if out.Status.Error != nil { diff --git a/agent/agent_windows.go b/agent/agent_windows.go index 140c403..d9f6678 100644 --- a/agent/agent_windows.go +++ b/agent/agent_windows.go @@ -118,6 +118,10 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, ext = "*.py" case "cmd": ext = "*.bat" + case "nushell": + ext = "*.nu" + case "deno": + ext = "*.ts" } tmpDir := a.WinTmpDir @@ -151,6 +155,10 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, cmdArgs = []string{tmpfn.Name()} case "cmd": exe = tmpfn.Name() + case "nushell": + exe = tmpfn.Name() + case "deno": + exe = tmpfn.Name() } if len(args) > 0 { From 87e1b29ef6e203d5e72f72f4203b5941e2eb79b7 Mon Sep 17 00:00:00 2001 From: David Randall Date: Sat, 18 Nov 2023 20:03:29 -0500 Subject: [PATCH 02/19] Add: Download the nu and deno binaries from GitHub Signed-off-by: David Randall --- agent/agent.go | 23 ++++ agent/agent_unix.go | 255 +++++++++++++++++++++++++++++++++++++++-- agent/agent_windows.go | 235 ++++++++++++++++++++++++++++++++++++- agent/install.go | 12 +- agent/rpc.go | 4 + agent/svc.go | 3 + agent/utils.go | 67 +++++++++++ main.go | 4 + 8 files changed, 592 insertions(+), 11 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 1cc74cf..cd3c895 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -58,6 +58,8 @@ type Agent struct { MeshSystemEXE string MeshSVC string PyBin string + NuBin string + DenoBin string Headers map[string]string Logger *logrus.Logger Version string @@ -86,10 +88,13 @@ const ( nixAgentDir = "/opt/tacticalagent" nixMeshDir = "/opt/tacticalmesh" nixAgentBin = nixAgentDir + "/tacticalagent" + nixAgentBinDir = nixAgentDir + "/bin" nixMeshAgentBin = nixMeshDir + "/meshagent" macPlistPath = "/Library/LaunchDaemons/tacticalagent.plist" macPlistName = "tacticalagent" defaultMacMeshSvcDir = "/usr/local/mesh_services" + nuVersion = "0.87.0" + denoVersion = "v1.38.2" ) var defaultWinTmpDir = filepath.Join(os.Getenv("PROGRAMDATA"), "TacticalRMM") @@ -119,6 +124,22 @@ func New(logger *logrus.Logger, version string) *Agent { pybin = filepath.Join(pd, "py38-x32", "python.exe") } + var nuBin string + switch runtime.GOOS { + case "windows": + nuBin = filepath.Join(pd, "bin", "nu.exe") + default: + nuBin = filepath.Join(nixAgentBinDir, "nu") + } + + var denoBin string + switch runtime.GOOS { + case "windows": + denoBin = filepath.Join(pd, "bin", "deno.exe") + default: + denoBin = filepath.Join(nixAgentBinDir, "deno") + } + ac := NewAgentConfig() headers := make(map[string]string) @@ -232,6 +253,8 @@ func New(logger *logrus.Logger, version string) *Agent { MeshSystemEXE: MeshSysExe, MeshSVC: meshSvcName, PyBin: pybin, + NuBin: nuBin, + DenoBin: denoBin, Headers: headers, Logger: logger, Version: version, diff --git a/agent/agent_unix.go b/agent/agent_unix.go index c77c52e..aebd808 100644 --- a/agent/agent_unix.go +++ b/agent/agent_unix.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "os" + "path" "path/filepath" "runtime" "strconv" @@ -196,31 +197,42 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, opts.IsScript = true switch shell { case "nushell": - // FIXME: Make this dynamic and use /opt/tacticalagent/bin/nu - opts.Shell = "/usr/local/bin/nu" + opts.Shell = a.NuBin opts.Args = append([]string{ "--no-config-file", f.Name(), }, args...) + if !trmm.FileExists(a.NuBin) { + a.Logger.Errorln("RunScript(): Executable does not exist. Install Nu and try again:", a.NuBin) + err := errors.New("File Not Found: " + a.NuBin) + return "", err.Error(), 85, err + } case "deno": - // FIXME: Make this dynamic and use /opt/tacticalagent/bin/nu - opts.Shell = "/usr/local/bin/deno" + opts.Shell = a.DenoBin opts.Args = []string{ "run", "--no-prompt", } + if !trmm.FileExists(a.DenoBin) { + a.Logger.Errorln("RunScript(): Executable does not exist. Install deno and try again:", a.DenoBin) + err := errors.New("File Not Found: " + a.DenoBin) + return "", err.Error(), 85, err + } // Search the environment variables for DENO_PERMISSIONS and use that to set permissions for the script. // https://docs.deno.com/runtime/manual/basics/permissions#permissions-list // DENO_PERMISSIONS is not an official environment variable. // https://docs.deno.com/runtime/manual/basics/env_variables - // TODO: Remove DENO_PERMISSIONS from the environment variables. - for _, v := range envVars { + for i, v := range envVars { if strings.HasPrefix(v, "DENO_PERMISSIONS=") { permissions := strings.Split(v, "=")[1] opts.Args = append(opts.Args, strings.Split(permissions, " ")...) + // Remove the DENO_PERMISSIONS variable from the environment variables slice. + // It's possible more variables may exist with the same prefix. + envVars = append(envVars[:i], envVars[i+1:]...) + break } } @@ -236,7 +248,7 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, opts.EnvVars = envVars opts.Timeout = time.Duration(timeout) - a.Logger.Debugln("RunScript(): ", opts.Shell, opts.Args) + a.Logger.Debugln("RunScript():", opts.Shell, opts.Args) out := a.CmdV2(opts) retError := "" if out.Status.Error != nil { @@ -541,6 +553,235 @@ func GetServiceStatus(name string) (string, error) { return "", nil } func (a *Agent) GetPython(force bool) {} +// GetNushell will download nushell from GitHub and install (copy) it to nixAgentBinDir +func (a *Agent) GetNushell(force bool) { + if trmm.FileExists(a.NuBin) { + if force { + err := os.Remove(a.NuBin) + if err != nil { + a.Logger.Errorln("GetNushell(): Error removing nu binary:", err) + return + } + } else { + return + } + } + + if !trmm.FileExists(nixAgentBinDir) { + err := os.MkdirAll(nixAgentBinDir, 0755) + if err != nil { + a.Logger.Errorln("GetNushell(): Error creating nixAgentBinDir:", err) + return + } + } + + var ( + assetName string + url string + targzDirName string + ) + + switch runtime.GOOS { + case "darwin": + switch runtime.GOARCH { + case "arm64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-aarch64-darwin-full.tar.gz + assetName = fmt.Sprintf("nu-%s-aarch64-darwin-full.tar.gz", nuVersion) + default: + a.Logger.Debugln("GetNushell(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } + case "linux": + switch runtime.GOARCH { + case "amd64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-x86_64-linux-musl-full.tar.gz + assetName = fmt.Sprintf("nu-%s-x86_64-linux-musl-full.tar.gz", nuVersion) + case "arm64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-aarch64-linux-gnu-full.tar.gz + assetName = fmt.Sprintf("nu-%s-aarch64-linux-gnu-full.tar.gz", nuVersion) + default: + a.Logger.Debugln("GetNushell(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } + default: + a.Logger.Debugln("GetNushell(): Unsupported OS:", runtime.GOOS) + return + } + url = fmt.Sprintf("https://github.com/nushell/nushell/releases/download/%s/%s", nuVersion, assetName) + a.Logger.Debugln("GetNushell(): Nu download url:", url) + + tmpDir, err := os.MkdirTemp("", "trmm") + if err != nil { + a.Logger.Errorln("GetNushell(): Error creating temp directory:", err) + return + } + defer func(path string) { + err := os.RemoveAll(path) + if err != nil { + a.Logger.Errorln("GetNushell(): Error removing temp directory:", err) + } + }(tmpDir) + + tmpAssetName := filepath.Join(tmpDir, assetName) + a.Logger.Debugln("GetNushell(): tmpAssetName:", tmpAssetName) + + rClient := resty.New() + rClient.SetTimeout(20 * time.Minute) + rClient.SetRetryCount(10) + rClient.SetRetryWaitTime(1 * time.Minute) + rClient.SetRetryMaxWaitTime(15 * time.Minute) + if len(a.Proxy) > 0 { + rClient.SetProxy(a.Proxy) + } + + r, err := rClient.R().SetOutput(tmpAssetName).Get(url) + if err != nil { + a.Logger.Errorln("GetNushell(): Unable to download nu from github.", err) + return + } + if r.IsError() { + a.Logger.Errorln("GetNushell(): Unable to download nu from github. Status code", r.StatusCode()) + return + } + + targzDirName, err = a.ExtractTarGz(tmpAssetName, tmpDir) + if err != nil { + a.Logger.Errorln("GetNushell(): Failed to extract downloaded tar.gz file:", err) + return + } + + err = copyFile(path.Join(tmpDir, targzDirName, "nu"), a.NuBin) + if err != nil { + a.Logger.Errorln("GetNushell(): Failed to copy nu file to install dir:", err) + return + } + + err = os.Chmod(a.NuBin, 0755) + if err != nil { + a.Logger.Errorln("GetNushell(): Failed to chmod nu binary:", err) + return + } + +} + +// GetDeno will download deno from GitHub and install (copy) it to nixAgentBinDir +func (a *Agent) GetDeno(force bool) { + if trmm.FileExists(a.DenoBin) { + if force { + err := os.Remove(a.DenoBin) + if err != nil { + a.Logger.Errorln("GetDeno(): Error removing deno binary:", err) + return + } + } else { + return + } + } + + if !trmm.FileExists(nixAgentBinDir) { + err := os.MkdirAll(nixAgentBinDir, 0755) + if err != nil { + a.Logger.Errorln("GetDeno(): Error creating nixAgentBinDir:", err) + return + } + } + + var ( + assetName string + url string + ) + + switch runtime.GOOS { + case "darwin": + switch runtime.GOARCH { + case "arm64": + // https://github.com/denoland/deno/releases/download/v1.38.2/deno-aarch64-apple-darwin.zip + assetName = fmt.Sprintf("deno-aarch64-apple-darwin.zip") + case "amd64": + // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-apple-darwin.zip + assetName = fmt.Sprintf("deno-x86_64-apple-darwin.zip") + default: + a.Logger.Debugln("GetDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } + case "linux": + switch runtime.GOARCH { + case "amd64": + // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-unknown-linux-gnu.zip + assetName = fmt.Sprintf("deno-x86_64-unknown-linux-gnu.zip") + default: + a.Logger.Debugln("GetDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } + default: + a.Logger.Debugln("GetDeno(): Unsupported OS:", runtime.GOOS) + return + } + url = fmt.Sprintf("https://github.com/denoland/deno/releases/download/%s/%s", denoVersion, assetName) + a.Logger.Debugln("GetDeno(): Deno download url:", url) + + tmpDir, err := os.MkdirTemp("", "trmm") + if err != nil { + a.Logger.Errorln("GetDeno(): Error creating temp directory:", err) + return + } + defer func(path string) { + err := os.RemoveAll(path) + if err != nil { + a.Logger.Errorln("GetDeno(): Error removing temp directory:", err) + } + }(tmpDir) + + tmpAssetName := filepath.Join(tmpDir, assetName) + a.Logger.Debugln("GetDeno(): tmpAssetName:", tmpAssetName) + + if !trmm.FileExists(nixAgentBinDir) { + err = os.MkdirAll(nixAgentBinDir, 0755) + if err != nil { + a.Logger.Errorln("GetDeno(): Error creating nixAgentBinDir:", err) + return + } + } + + rClient := resty.New() + rClient.SetTimeout(20 * time.Minute) + rClient.SetRetryCount(10) + rClient.SetRetryWaitTime(1 * time.Minute) + rClient.SetRetryMaxWaitTime(15 * time.Minute) + if len(a.Proxy) > 0 { + rClient.SetProxy(a.Proxy) + } + + r, err := rClient.R().SetOutput(tmpAssetName).Get(url) + if err != nil { + a.Logger.Errorln("GetDeno(): Unable to download deno from github.", err) + return + } + if r.IsError() { + a.Logger.Errorln("GetDeno(): Unable to download deno from github. Status code", r.StatusCode()) + return + } + + err = Unzip(tmpAssetName, tmpDir) + if err != nil { + a.Logger.Errorln("GetDeno(): Failed to unzip downloaded zip file:", err) + return + } + + err = copyFile(path.Join(tmpDir, "deno"), a.DenoBin) + if err != nil { + a.Logger.Errorln("GetDeno(): Failed to copy deno file to install dir:", err) + return + } + + err = os.Chmod(a.DenoBin, 0755) + if err != nil { + a.Logger.Errorln("GetDeno(): Failed to chmod deno binary:", err) + return + } + +} + type SchedTask struct{ Name string } func (a *Agent) PatchMgmnt(enable bool) error { return nil } diff --git a/agent/agent_windows.go b/agent/agent_windows.go index d9f6678..c61a29a 100644 --- a/agent/agent_windows.go +++ b/agent/agent_windows.go @@ -19,6 +19,7 @@ import ( "fmt" "os" "os/exec" + "path" "path/filepath" "runtime" "strconv" @@ -156,9 +157,41 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, case "cmd": exe = tmpfn.Name() case "nushell": - exe = tmpfn.Name() + exe = a.NuBin + cmdArgs = []string{"--no-config-file", tmpfn.Name()} + if !trmm.FileExists(a.NuBin) { + a.Logger.Errorln("RunScript(): Executable does not exist. Install Nu and try again:", a.NuBin) + err := errors.New("File Not Found: " + a.NuBin) + return "", err.Error(), 85, err + } case "deno": - exe = tmpfn.Name() + exe = a.DenoBin + cmdArgs = []string{"run", "--no-prompt"} + if !trmm.FileExists(a.DenoBin) { + a.Logger.Errorln("RunScript(): Executable does not exist. Install deno and try again:", a.DenoBin) + err := errors.New("File Not Found: " + a.DenoBin) + return "", err.Error(), 85, err + } + + // Search the environment variables for DENO_PERMISSIONS and use that to set permissions for the script. + // https://docs.deno.com/runtime/manual/basics/permissions#permissions-list + // DENO_PERMISSIONS is not an official environment variable. + // https://docs.deno.com/runtime/manual/basics/env_variables + for i, v := range envVars { + if strings.HasPrefix(v, "DENO_PERMISSIONS=") { + permissions := strings.Split(v, "=")[1] + cmdArgs = append(cmdArgs, strings.Split(permissions, " ")...) + // Remove the DENO_PERMISSIONS variable from the environment variables slice. + // It's possible more variables may exist with the same prefix. + envVars = append(envVars[:i], envVars[i+1:]...) + break + } + } + + // Can't append a variadic slice after a string arg. + // https://pkg.go.dev/builtin#append + cmdArgs = append(cmdArgs, tmpfn.Name()) + } if len(args) > 0 { @@ -846,6 +879,204 @@ func (a *Agent) GetPython(force bool) { } } +// GetNushell will download nushell from GitHub and install (copy) it to ProgramDir\bin, where ProgramDir is +// initialized to C:\Program Files\TacticalAgent +func (a *Agent) GetNushell(force bool) { + if trmm.FileExists(a.NuBin) { + if force { + err := os.Remove(a.NuBin) + if err != nil { + a.Logger.Errorln("GetNushell(): Error removing nu binary:", err) + return + } + } else { + return + } + } + + programBinDir := path.Join(a.ProgramDir, "bin") + if !trmm.FileExists(programBinDir) { + err := os.MkdirAll(programBinDir, 0755) + if err != nil { + a.Logger.Errorln("GetNushell(): Error creating Program Files bin folder:", err) + return + } + } + + var ( + assetName string + url string + ) + + switch runtime.GOOS { + case "windows": + switch runtime.GOARCH { + case "amd64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-x86_64-windows-msvc-full.zip + assetName = fmt.Sprintf("nu-%s-x86_64-windows-msvc-full.zip", nuVersion) + case "arm64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-aarch64-windows-msvc-full.zip + assetName = fmt.Sprintf("nu-%s-aarch64-windows-msvc-full.zip", nuVersion) + default: + a.Logger.Debugln("GetNushell(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } + default: + a.Logger.Debugln("GetNushell(): Unsupported OS:", runtime.GOOS) + return + } + url = fmt.Sprintf("https://github.com/nushell/nushell/releases/download/%s/%s", nuVersion, assetName) + a.Logger.Debugln("GetNushell(): Nu download url:", url) + + tmpDir, err := os.MkdirTemp("", "trmm") + if err != nil { + a.Logger.Errorln("GetNushell(): Error creating temp directory:", err) + return + } + defer func(path string) { + err := os.RemoveAll(path) + if err != nil { + a.Logger.Errorln("GetNushell(): Error removing temp directory:", err) + } + }(tmpDir) + + tmpAssetName := filepath.Join(tmpDir, assetName) + a.Logger.Debugln("GetNushell(): tmpAssetName:", tmpAssetName) + + rClient := resty.New() + rClient.SetTimeout(20 * time.Minute) + rClient.SetRetryCount(10) + rClient.SetRetryWaitTime(1 * time.Minute) + rClient.SetRetryMaxWaitTime(15 * time.Minute) + if len(a.Proxy) > 0 { + rClient.SetProxy(a.Proxy) + } + + r, err := rClient.R().SetOutput(tmpAssetName).Get(url) + if err != nil { + a.Logger.Errorln("GetNushell(): Unable to download nu from github.", err) + return + } + if r.IsError() { + a.Logger.Errorln("GetNushell(): Unable to download nu from github. Status code", r.StatusCode()) + return + } + + err = Unzip(tmpAssetName, tmpDir) + if err != nil { + a.Logger.Errorln("GetNushell(): Failed to unzip downloaded zip file:", err) + return + } + + err = copyFile(path.Join(tmpDir, "nu.exe"), a.NuBin) + if err != nil { + a.Logger.Errorln("GetNushell(): Failed to copy nu.exe file to install dir:", err) + return + } + +} + +// GetDeno will download deno from GitHub and install (copy) it to nixAgentBinDir +func (a *Agent) GetDeno(force bool) { + if trmm.FileExists(a.DenoBin) { + if force { + err := os.Remove(a.DenoBin) + if err != nil { + a.Logger.Errorln("GetDeno(): Error removing deno binary:", err) + return + } + } else { + return + } + } + + programBinDir := path.Join(a.ProgramDir, "bin") + if !trmm.FileExists(programBinDir) { + err := os.MkdirAll(programBinDir, 0755) + if err != nil { + a.Logger.Errorln("GetDeno(): Error creating Program Files bin folder:", err) + return + } + } + + var ( + assetName string + url string + ) + + switch runtime.GOOS { + case "windows": + switch runtime.GOARCH { + case "amd64": + // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-pc-windows-msvc.zip + assetName = fmt.Sprintf("deno-x86_64-pc-windows-msvc.zip") + default: + a.Logger.Debugln("GetDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } + default: + a.Logger.Debugln("GetDeno(): Unsupported OS:", runtime.GOOS) + return + } + url = fmt.Sprintf("https://github.com/denoland/deno/releases/download/%s/%s", denoVersion, assetName) + a.Logger.Debugln("GetDeno(): Deno download url:", url) + + tmpDir, err := os.MkdirTemp("", "trmm") + if err != nil { + a.Logger.Errorln("GetDeno(): Error creating temp directory:", err) + return + } + defer func(path string) { + err := os.RemoveAll(path) + if err != nil { + a.Logger.Errorln("GetDeno(): Error removing temp directory:", err) + } + }(tmpDir) + + tmpAssetName := filepath.Join(tmpDir, assetName) + a.Logger.Debugln("GetDeno(): tmpAssetName:", tmpAssetName) + + if !trmm.FileExists(nixAgentBinDir) { + err = os.MkdirAll(nixAgentBinDir, 0755) + if err != nil { + a.Logger.Errorln("GetDeno(): Error creating nixAgentBinDir:", err) + return + } + } + + rClient := resty.New() + rClient.SetTimeout(20 * time.Minute) + rClient.SetRetryCount(10) + rClient.SetRetryWaitTime(1 * time.Minute) + rClient.SetRetryMaxWaitTime(15 * time.Minute) + if len(a.Proxy) > 0 { + rClient.SetProxy(a.Proxy) + } + + r, err := rClient.R().SetOutput(tmpAssetName).Get(url) + if err != nil { + a.Logger.Errorln("GetDeno(): Unable to download deno from github.", err) + return + } + if r.IsError() { + a.Logger.Errorln("GetDeno(): Unable to download deno from github. Status code", r.StatusCode()) + return + } + + err = Unzip(tmpAssetName, tmpDir) + if err != nil { + a.Logger.Errorln("GetDeno(): Failed to unzip downloaded zip file:", err) + return + } + + err = copyFile(path.Join(tmpDir, "deno.exe"), a.DenoBin) + if err != nil { + a.Logger.Errorln("GetDeno(): Failed to copy deno.exe file to install dir:", err) + return + } + +} + func (a *Agent) RecoverMesh() { a.Logger.Infoln("Attempting mesh recovery") defer CMD("net", []string{"start", a.MeshSVC}, 60, false) diff --git a/agent/install.go b/agent/install.go index 1d6c75a..4186754 100644 --- a/agent/install.go +++ b/agent/install.go @@ -17,6 +17,7 @@ import ( "io" "net/url" "os" + "path" "path/filepath" "regexp" "runtime" @@ -258,8 +259,13 @@ func (a *Agent) Install(i *Installer) { // check in once a.DoNatsCheckIn() + if runtime.GOOS == "linux" { + // Used for Nushell and Deno binaries + os.MkdirAll(nixAgentBinDir, 0755) + } + if runtime.GOOS == "darwin" { - os.MkdirAll(nixAgentDir, 0755) + os.MkdirAll(nixAgentBinDir, 0755) self, _ := os.Executable() copyFile(self, nixAgentBin) os.Chmod(nixAgentBin, 0755) @@ -300,6 +306,8 @@ func (a *Agent) Install(i *Installer) { } if runtime.GOOS == "windows" { + os.MkdirAll(path.Join(a.ProgramDir, "bin"), 0755) + // send software api a.SendSoftware() @@ -341,7 +349,7 @@ func (a *Agent) Install(i *Installer) { } } - a.installerMsg("Installation was successfull!\nAllow a few minutes for the agent to properly display in the RMM", "info", i.Silent) + a.installerMsg("Installation was successful!\nAllow a few minutes for the agent to properly display in the RMM", "info", i.Silent) } func copyFile(src, dst string) error { diff --git a/agent/rpc.go b/agent/rpc.go index 8ecae5c..03b1508 100644 --- a/agent/rpc.go +++ b/agent/rpc.go @@ -437,6 +437,10 @@ func (a *Agent) RunRPC() { }() case "installpython": go a.GetPython(true) + case "installnushell": + go a.GetNushell(true) + case "installdeno": + go a.GetDeno(true) case "installchoco": go a.InstallChoco() case "installwithchoco": diff --git a/agent/svc.go b/agent/svc.go index e7f03ea..b04bb9d 100644 --- a/agent/svc.go +++ b/agent/svc.go @@ -49,6 +49,9 @@ func (a *Agent) AgentSvc(nc *nats.Conn) { a.Logger.Errorln("AgentSvc() createWinTempDir():", err) } } + a.GetNushell(false) + a.GetDeno(false) + a.RunMigrations() sleepDelay := randRange(7, 25) diff --git a/agent/utils.go b/agent/utils.go index 6eacaca..f915fc5 100644 --- a/agent/utils.go +++ b/agent/utils.go @@ -12,8 +12,10 @@ https://license.tacticalrmm.com package agent import ( + "archive/tar" "archive/zip" "bytes" + "compress/gzip" "fmt" "io" "math" @@ -21,6 +23,7 @@ import ( "net" "os" "os/exec" + "path" "path/filepath" "runtime" goDebug "runtime/debug" @@ -280,6 +283,70 @@ func Unzip(src, dest string) error { return nil } +// ExtractTarGz extracts a tar.gz file to the specified directory. +// Returns the extracted directory name. +// https://stackoverflow.com/questions/57639648/how-to-decompress-tar-gz-file-in-go +func (a *Agent) ExtractTarGz(targz string, destDir string) (extractedDir string, err error) { + gzipStream, err := os.Open(targz) + if err != nil { + a.Logger.Errorln("ExtractTarGz(): Open() failed:", err.Error()) + return "", err + } + + uncompressedStream, err := gzip.NewReader(gzipStream) + if err != nil { + a.Logger.Errorln("ExtractTarGz(): NewReader() failed:", err.Error()) + return "", err + } + + extractedDir = "" + tarReader := tar.NewReader(uncompressedStream) + for true { + header, err := tarReader.Next() + + if err == io.EOF { + break + } + + if err != nil { + a.Logger.Errorln("ExtractTarGz(): Next() failed:", err.Error()) + return "", err + } + + switch header.Typeflag { + case tar.TypeDir: + if err := os.MkdirAll(path.Join(destDir, header.Name), 0755); err != nil { + a.Logger.Errorln("ExtractTarGz(): Mkdir() failed:", err.Error()) + return "", err + } + if extractedDir == "" { + extractedDir = header.Name + } + case tar.TypeReg: + outFile, err := os.Create(path.Join(destDir, header.Name)) + if err != nil { + a.Logger.Errorln("ExtractTarGz(): Create() failed:", err.Error()) + return "", err + } + if _, err := io.Copy(outFile, tarReader); err != nil { + a.Logger.Errorln("ExtractTarGz(): Copy() failed:", err.Error()) + return "", err + } + err = outFile.Close() + if err != nil { + a.Logger.Errorln("ExtractTarGz(): Close() failed:", err.Error()) + return "", err + } + + default: + a.Logger.Errorln("ExtractTarGz(): Unknown type: %s in %s", header.Typeflag, header.Name) + return "", err + } + + } + return extractedDir, nil +} + // https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format/ func ByteCountSI(b uint64) string { const unit = 1024 diff --git a/main.go b/main.go index cc302ca..0d46a62 100644 --- a/main.go +++ b/main.go @@ -117,6 +117,10 @@ func main() { fmt.Println(a.PublicIP()) case "getpython": a.GetPython(true) + case "getdeno": + a.GetDeno(true) + case "getnushell": + a.GetNushell(true) case "runmigrations": a.RunMigrations() case "recovermesh": From 2afdfd7ab88083b27d42898dd2ffe1adf3868c4b Mon Sep 17 00:00:00 2001 From: David Randall Date: Sun, 3 Dec 2023 23:14:27 -0500 Subject: [PATCH 03/19] Add: Server variables are opt-out by default - Pull the Nushell and Deno versions from the server. - Support downloading Nushell and Deno from a url (not GitHUb). - Add support for nu config.nu and env.nu files. - Add support for default Deno permissions. --- agent/agent.go | 3 +- agent/agent_unix.go | 328 ++++++++++++++++++++++++++--------------- agent/agent_windows.go | 242 ++++++++++++++++++++---------- agent/checks.go | 2 +- agent/choco_windows.go | 2 +- agent/embed_darwin.go | 2 +- agent/rpc.go | 42 +++--- agent/svc.go | 56 +++++-- agent/tasks_windows.go | 4 +- main.go | 8 +- shared/types.go | 68 +++++---- 11 files changed, 490 insertions(+), 267 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index cd3c895..c0c947c 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -89,12 +89,11 @@ const ( nixMeshDir = "/opt/tacticalmesh" nixAgentBin = nixAgentDir + "/tacticalagent" nixAgentBinDir = nixAgentDir + "/bin" + nixAgentEtcDir = nixAgentDir + "/etc" nixMeshAgentBin = nixMeshDir + "/meshagent" macPlistPath = "/Library/LaunchDaemons/tacticalagent.plist" macPlistName = "tacticalagent" defaultMacMeshSvcDir = "/usr/local/mesh_services" - nuVersion = "0.87.0" - denoVersion = "v1.38.2" ) var defaultWinTmpDir = filepath.Join(os.Getenv("PROGRAMDATA"), "TacticalRMM") diff --git a/agent/agent_unix.go b/agent/agent_unix.go index aebd808..5de2595 100644 --- a/agent/agent_unix.go +++ b/agent/agent_unix.go @@ -167,7 +167,7 @@ func NewAgentConfig() *rmm.AgentConfig { return ret } -func (a *Agent) RunScript(code string, shell string, args []string, timeout int, runasuser bool, envVars []string) (stdout, stderr string, exitcode int, e error) { +func (a *Agent) RunScript(code string, shell string, args []string, timeout int, runasuser bool, envVars []string, nushellEnableConfig bool, denoDefaultPermissions string) (stdout, stderr string, exitcode int, e error) { code = removeWinNewLines(code) content := []byte(code) @@ -197,12 +197,21 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, opts.IsScript = true switch shell { case "nushell": + var nushellArgs []string + if nushellEnableConfig { + nushellArgs = []string{ + "--config", + path.Join(nixAgentEtcDir, "nushell", "config.nu"), + "--env-config", + path.Join(nixAgentEtcDir, "nushell", "env.nu"), + } + } else { + nushellArgs = []string{"--no-config-file"} + } opts.Shell = a.NuBin - opts.Args = append([]string{ - "--no-config-file", - f.Name(), - }, - args...) + opts.Args = nushellArgs + opts.Args = append(opts.Args, f.Name()) + opts.Args = append(opts.Args, args...) if !trmm.FileExists(a.NuBin) { a.Logger.Errorln("RunScript(): Executable does not exist. Install Nu and try again:", a.NuBin) err := errors.New("File Not Found: " + a.NuBin) @@ -225,6 +234,8 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, // https://docs.deno.com/runtime/manual/basics/permissions#permissions-list // DENO_PERMISSIONS is not an official environment variable. // https://docs.deno.com/runtime/manual/basics/env_variables + // DENO_DEFAULT_PERMISSIONS is used if not found in the environment variables. + found := false for i, v := range envVars { if strings.HasPrefix(v, "DENO_PERMISSIONS=") { permissions := strings.Split(v, "=")[1] @@ -232,9 +243,13 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, // Remove the DENO_PERMISSIONS variable from the environment variables slice. // It's possible more variables may exist with the same prefix. envVars = append(envVars[:i], envVars[i+1:]...) + found = true break } } + if !found && denoDefaultPermissions != "" { + opts.Args = append(opts.Args, strings.Split(denoDefaultPermissions, " ")...) + } // Can't append a variadic slice after a string arg. // https://pkg.go.dev/builtin#append @@ -536,30 +551,19 @@ func (a *Agent) GetWMIInfo() map[string]interface{} { return wmiInfo } -// windows only below TODO add into stub file -func (a *Agent) GetAgentCheckInConfig(ret AgentCheckInConfig) AgentCheckInConfig { - return ret -} - -func (a *Agent) PlatVer() (string, error) { return "", nil } - -func (a *Agent) SendSoftware() {} - -func (a *Agent) UninstallCleanup() {} - -func (a *Agent) RunMigrations() {} - -func GetServiceStatus(name string) (string, error) { return "", nil } - -func (a *Agent) GetPython(force bool) {} +// InstallNushell will download nushell from GitHub and install (copy) it to nixAgentBinDir +func (a *Agent) InstallNushell(force bool) { + conf := a.GetAgentCheckInConfig(a.GetCheckInConfFromAPI()) + if !conf.InstallNushell { + return + } -// GetNushell will download nushell from GitHub and install (copy) it to nixAgentBinDir -func (a *Agent) GetNushell(force bool) { if trmm.FileExists(a.NuBin) { if force { + a.Logger.Debugln(a.NuBin, "InstallNushell(): Forced install. Removing nu binary.") err := os.Remove(a.NuBin) if err != nil { - a.Logger.Errorln("GetNushell(): Error removing nu binary:", err) + a.Logger.Errorln("InstallNushell(): Error removing nu binary:", err) return } } else { @@ -570,60 +574,106 @@ func (a *Agent) GetNushell(force bool) { if !trmm.FileExists(nixAgentBinDir) { err := os.MkdirAll(nixAgentBinDir, 0755) if err != nil { - a.Logger.Errorln("GetNushell(): Error creating nixAgentBinDir:", err) + a.Logger.Errorln("InstallNushell(): Error creating nixAgentBinDir:", err) return } } + if conf.NushellEnableConfig { + // Create 0-byte config files for Nushell + nushellPath := path.Join(nixAgentEtcDir, "nushell") + nushellConfig := path.Join(nushellPath, "config.nu") + nushellEnv := path.Join(nushellPath, "env.nu") + if !trmm.FileExists(nushellPath) { + err := os.MkdirAll(nushellPath, 0755) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error creating nixAgentEtcDir/nushell:", err) + return + } + } + + if !trmm.FileExists(nushellConfig) { + _, err := os.Create(nushellConfig) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error creating nushell config.nu:", err) + return + } + err = os.Chmod(nushellConfig, 0744) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error changing permissions for nushell config.nu:", err) + return + } + } + if !trmm.FileExists(nushellEnv) { + _, err := os.Create(nushellEnv) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error creating nushell env.nu:", err) + return + } + err = os.Chmod(nushellEnv, 0744) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error changing permissions for nushell env.nu:", err) + return + } + } + } + var ( assetName string url string targzDirName string ) - switch runtime.GOOS { - case "darwin": - switch runtime.GOARCH { - case "arm64": - // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-aarch64-darwin-full.tar.gz - assetName = fmt.Sprintf("nu-%s-aarch64-darwin-full.tar.gz", nuVersion) - default: - a.Logger.Debugln("GetNushell(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) - return - } - case "linux": - switch runtime.GOARCH { - case "amd64": - // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-x86_64-linux-musl-full.tar.gz - assetName = fmt.Sprintf("nu-%s-x86_64-linux-musl-full.tar.gz", nuVersion) - case "arm64": - // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-aarch64-linux-gnu-full.tar.gz - assetName = fmt.Sprintf("nu-%s-aarch64-linux-gnu-full.tar.gz", nuVersion) + if conf.InstallNushellUrl != "" { + url = conf.InstallNushellUrl + url = strings.Replace(url, "{OS}", runtime.GOOS, -1) + url = strings.Replace(url, "{ARCH}", runtime.GOARCH, -1) + url = strings.Replace(url, "{VERSION}", conf.InstallNushellVersion, -1) + } else { + switch runtime.GOOS { + case "darwin": + switch runtime.GOARCH { + case "arm64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-aarch64-darwin-full.tar.gz + assetName = fmt.Sprintf("nu-%s-aarch64-darwin-full.tar.gz", conf.InstallNushellVersion) + default: + a.Logger.Debugln("InstallNushell(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } + case "linux": + switch runtime.GOARCH { + case "amd64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-x86_64-linux-musl-full.tar.gz + assetName = fmt.Sprintf("nu-%s-x86_64-linux-musl-full.tar.gz", conf.InstallNushellVersion) + case "arm64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-aarch64-linux-gnu-full.tar.gz + assetName = fmt.Sprintf("nu-%s-aarch64-linux-gnu-full.tar.gz", conf.InstallNushellVersion) + default: + a.Logger.Debugln("InstallNushell(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } default: - a.Logger.Debugln("GetNushell(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + a.Logger.Debugln("InstallNushell(): Unsupported OS:", runtime.GOOS) return } - default: - a.Logger.Debugln("GetNushell(): Unsupported OS:", runtime.GOOS) - return + url = fmt.Sprintf("https://github.com/nushell/nushell/releases/download/%s/%s", conf.InstallNushellVersion, assetName) } - url = fmt.Sprintf("https://github.com/nushell/nushell/releases/download/%s/%s", nuVersion, assetName) - a.Logger.Debugln("GetNushell(): Nu download url:", url) + a.Logger.Debugln("InstallNushell(): Nu download url:", url) tmpDir, err := os.MkdirTemp("", "trmm") if err != nil { - a.Logger.Errorln("GetNushell(): Error creating temp directory:", err) + a.Logger.Errorln("InstallNushell(): Error creating temp directory:", err) return } defer func(path string) { err := os.RemoveAll(path) if err != nil { - a.Logger.Errorln("GetNushell(): Error removing temp directory:", err) + a.Logger.Errorln("InstallNushell(): Error removing temp directory:", err) } }(tmpDir) tmpAssetName := filepath.Join(tmpDir, assetName) - a.Logger.Debugln("GetNushell(): tmpAssetName:", tmpAssetName) + a.Logger.Debugln("InstallNushell(): tmpAssetName:", tmpAssetName) rClient := resty.New() rClient.SetTimeout(20 * time.Minute) @@ -636,41 +686,57 @@ func (a *Agent) GetNushell(force bool) { r, err := rClient.R().SetOutput(tmpAssetName).Get(url) if err != nil { - a.Logger.Errorln("GetNushell(): Unable to download nu from github.", err) + a.Logger.Errorln("InstallNushell(): Unable to download nu:", err) return } if r.IsError() { - a.Logger.Errorln("GetNushell(): Unable to download nu from github. Status code", r.StatusCode()) + a.Logger.Errorln("InstallNushell(): Unable to download nu. Status code:", r.StatusCode()) return } - targzDirName, err = a.ExtractTarGz(tmpAssetName, tmpDir) - if err != nil { - a.Logger.Errorln("GetNushell(): Failed to extract downloaded tar.gz file:", err) - return - } + if conf.InstallNushellUrl != "" { + // InstallNushellUrl is not compressed. + err = copyFile(path.Join(tmpDir, tmpAssetName), a.NuBin) + if err != nil { + a.Logger.Errorln("InstallNushell(): Failed to copy nu file to install dir:", err) + return + } + } else { + // GitHub asset is tar.gz compressed. + targzDirName, err = a.ExtractTarGz(tmpAssetName, tmpDir) + if err != nil { + a.Logger.Errorln("InstallNushell(): Failed to extract downloaded tar.gz file:", err) + return + } - err = copyFile(path.Join(tmpDir, targzDirName, "nu"), a.NuBin) - if err != nil { - a.Logger.Errorln("GetNushell(): Failed to copy nu file to install dir:", err) - return + err = copyFile(path.Join(tmpDir, targzDirName, "nu"), a.NuBin) + if err != nil { + a.Logger.Errorln("InstallNushell(): Failed to copy nu file to install dir:", err) + return + } } err = os.Chmod(a.NuBin, 0755) if err != nil { - a.Logger.Errorln("GetNushell(): Failed to chmod nu binary:", err) + a.Logger.Errorln("InstallNushell(): Failed to chmod nu binary:", err) return } } -// GetDeno will download deno from GitHub and install (copy) it to nixAgentBinDir -func (a *Agent) GetDeno(force bool) { +// InstallDeno will download deno from GitHub and install (copy) it to nixAgentBinDir +func (a *Agent) InstallDeno(force bool) { + conf := a.GetAgentCheckInConfig(a.GetCheckInConfFromAPI()) + if !conf.InstallDeno { + return + } + if trmm.FileExists(a.DenoBin) { if force { + a.Logger.Debugln(a.NuBin, "InstallDeno(): Forced install. Removing deno binary.") err := os.Remove(a.DenoBin) if err != nil { - a.Logger.Errorln("GetDeno(): Error removing deno binary:", err) + a.Logger.Errorln("InstallDeno(): Error removing deno binary:", err) return } } else { @@ -681,7 +747,7 @@ func (a *Agent) GetDeno(force bool) { if !trmm.FileExists(nixAgentBinDir) { err := os.MkdirAll(nixAgentBinDir, 0755) if err != nil { - a.Logger.Errorln("GetDeno(): Error creating nixAgentBinDir:", err) + a.Logger.Errorln("InstallDeno(): Error creating nixAgentBinDir:", err) return } } @@ -691,57 +757,56 @@ func (a *Agent) GetDeno(force bool) { url string ) - switch runtime.GOOS { - case "darwin": - switch runtime.GOARCH { - case "arm64": - // https://github.com/denoland/deno/releases/download/v1.38.2/deno-aarch64-apple-darwin.zip - assetName = fmt.Sprintf("deno-aarch64-apple-darwin.zip") - case "amd64": - // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-apple-darwin.zip - assetName = fmt.Sprintf("deno-x86_64-apple-darwin.zip") - default: - a.Logger.Debugln("GetDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) - return - } - case "linux": - switch runtime.GOARCH { - case "amd64": - // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-unknown-linux-gnu.zip - assetName = fmt.Sprintf("deno-x86_64-unknown-linux-gnu.zip") + if conf.InstallDenoUrl != "" { + url = conf.InstallDenoUrl + url = strings.Replace(url, "{OS}", runtime.GOOS, -1) + url = strings.Replace(url, "{ARCH}", runtime.GOARCH, -1) + url = strings.Replace(url, "{VERSION}", conf.InstallDenoVersion, -1) + } else { + switch runtime.GOOS { + case "darwin": + switch runtime.GOARCH { + case "arm64": + // https://github.com/denoland/deno/releases/download/v1.38.2/deno-aarch64-apple-darwin.zip + assetName = fmt.Sprintf("deno-aarch64-apple-darwin.zip") + case "amd64": + // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-apple-darwin.zip + assetName = fmt.Sprintf("deno-x86_64-apple-darwin.zip") + default: + a.Logger.Debugln("InstallDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } + case "linux": + switch runtime.GOARCH { + case "amd64": + // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-unknown-linux-gnu.zip + assetName = fmt.Sprintf("deno-x86_64-unknown-linux-gnu.zip") + default: + a.Logger.Debugln("InstallDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } default: - a.Logger.Debugln("GetDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + a.Logger.Debugln("InstallDeno(): Unsupported OS:", runtime.GOOS) return } - default: - a.Logger.Debugln("GetDeno(): Unsupported OS:", runtime.GOOS) - return + url = fmt.Sprintf("https://github.com/denoland/deno/releases/download/%s/%s", conf.InstallDenoVersion, assetName) } - url = fmt.Sprintf("https://github.com/denoland/deno/releases/download/%s/%s", denoVersion, assetName) - a.Logger.Debugln("GetDeno(): Deno download url:", url) + a.Logger.Debugln("InstallDeno(): Deno download url:", url) tmpDir, err := os.MkdirTemp("", "trmm") if err != nil { - a.Logger.Errorln("GetDeno(): Error creating temp directory:", err) + a.Logger.Errorln("InstallDeno(): Error creating temp directory:", err) return } defer func(path string) { err := os.RemoveAll(path) if err != nil { - a.Logger.Errorln("GetDeno(): Error removing temp directory:", err) + a.Logger.Errorln("InstallDeno(): Error removing temp directory:", err) } }(tmpDir) tmpAssetName := filepath.Join(tmpDir, assetName) - a.Logger.Debugln("GetDeno(): tmpAssetName:", tmpAssetName) - - if !trmm.FileExists(nixAgentBinDir) { - err = os.MkdirAll(nixAgentBinDir, 0755) - if err != nil { - a.Logger.Errorln("GetDeno(): Error creating nixAgentBinDir:", err) - return - } - } + a.Logger.Debugln("InstallDeno(): tmpAssetName:", tmpAssetName) rClient := resty.New() rClient.SetTimeout(20 * time.Minute) @@ -754,34 +819,65 @@ func (a *Agent) GetDeno(force bool) { r, err := rClient.R().SetOutput(tmpAssetName).Get(url) if err != nil { - a.Logger.Errorln("GetDeno(): Unable to download deno from github.", err) + a.Logger.Errorln("InstallDeno(): Unable to download deno:", err) return } if r.IsError() { - a.Logger.Errorln("GetDeno(): Unable to download deno from github. Status code", r.StatusCode()) + a.Logger.Errorln("InstallDeno(): Unable to download deno. Status code:", r.StatusCode()) return } - err = Unzip(tmpAssetName, tmpDir) - if err != nil { - a.Logger.Errorln("GetDeno(): Failed to unzip downloaded zip file:", err) - return - } + if conf.InstallDenoUrl != "" { + // InstallDenoUrl is not compressed. + err = copyFile(path.Join(tmpDir, tmpAssetName), a.DenoBin) + if err != nil { + a.Logger.Errorln("InstallDeno(): Failed to copy deno file to install dir:", err) + return + } + } else { + // GitHub asset is zip compressed. + err = Unzip(tmpAssetName, tmpDir) + if err != nil { + a.Logger.Errorln("InstallDeno(): Failed to unzip downloaded zip file:", err) + return + } - err = copyFile(path.Join(tmpDir, "deno"), a.DenoBin) - if err != nil { - a.Logger.Errorln("GetDeno(): Failed to copy deno file to install dir:", err) - return + err = copyFile(path.Join(tmpDir, "deno"), a.DenoBin) + if err != nil { + a.Logger.Errorln("InstallDeno(): Failed to copy deno file to install dir:", err) + return + } } err = os.Chmod(a.DenoBin, 0755) if err != nil { - a.Logger.Errorln("GetDeno(): Failed to chmod deno binary:", err) + a.Logger.Errorln("InstallDeno(): Failed to chmod deno binary:", err) return } } +// GetAgentCheckInConfig will get the agent configuration from the server. +// The Windows agent stores the configuration in the registry. The UNIX agent does not store the config. +// @return AgentCheckInConfig +func (a *Agent) GetAgentCheckInConfig(ret AgentCheckInConfig) AgentCheckInConfig { + // TODO: Persist the config to disk. + return ret +} + +// windows only below TODO add into stub file +func (a *Agent) PlatVer() (string, error) { return "", nil } + +func (a *Agent) SendSoftware() {} + +func (a *Agent) UninstallCleanup() {} + +func (a *Agent) RunMigrations() {} + +func GetServiceStatus(name string) (string, error) { return "", nil } + +func (a *Agent) GetPython(force bool) {} + type SchedTask struct{ Name string } func (a *Agent) PatchMgmnt(enable bool) error { return nil } diff --git a/agent/agent_windows.go b/agent/agent_windows.go index c61a29a..cc22cbf 100644 --- a/agent/agent_windows.go +++ b/agent/agent_windows.go @@ -92,7 +92,7 @@ func NewAgentConfig() *rmm.AgentConfig { } } -func (a *Agent) RunScript(code string, shell string, args []string, timeout int, runasuser bool, envVars []string) (stdout, stderr string, exitcode int, e error) { +func (a *Agent) RunScript(code string, shell string, args []string, timeout int, runasuser bool, envVars []string, nushellEnableConfig bool, denoDefaultPermissions string) (stdout, stderr string, exitcode int, e error) { content := []byte(code) @@ -158,7 +158,18 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, exe = tmpfn.Name() case "nushell": exe = a.NuBin - cmdArgs = []string{"--no-config-file", tmpfn.Name()} + var nushellArgs []string + if nushellEnableConfig { + nushellArgs = []string{ + "--config", + path.Join(a.ProgramDir, "etc", "nushell", "config.nu"), + "--env-config", + path.Join(a.ProgramDir, "etc", "nushell", "env.nu"), + } + } else { + nushellArgs = []string{"--no-config-file"} + } + cmdArgs = append(nushellArgs, tmpfn.Name()) if !trmm.FileExists(a.NuBin) { a.Logger.Errorln("RunScript(): Executable does not exist. Install Nu and try again:", a.NuBin) err := errors.New("File Not Found: " + a.NuBin) @@ -177,6 +188,8 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, // https://docs.deno.com/runtime/manual/basics/permissions#permissions-list // DENO_PERMISSIONS is not an official environment variable. // https://docs.deno.com/runtime/manual/basics/env_variables + // DENO_DEFAULT_PERMISSIONS is used if not found in the environment variables. + found := false for i, v := range envVars { if strings.HasPrefix(v, "DENO_PERMISSIONS=") { permissions := strings.Split(v, "=")[1] @@ -184,12 +197,13 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, // Remove the DENO_PERMISSIONS variable from the environment variables slice. // It's possible more variables may exist with the same prefix. envVars = append(envVars[:i], envVars[i+1:]...) + found = true break } } - - // Can't append a variadic slice after a string arg. - // https://pkg.go.dev/builtin#append + if !found && denoDefaultPermissions != "" { + cmdArgs = append(cmdArgs, strings.Split(denoDefaultPermissions, " ")...) + } cmdArgs = append(cmdArgs, tmpfn.Name()) } @@ -879,14 +893,20 @@ func (a *Agent) GetPython(force bool) { } } -// GetNushell will download nushell from GitHub and install (copy) it to ProgramDir\bin, where ProgramDir is +// InstallNushell will download nushell from GitHub and install (copy) it to ProgramDir\bin, where ProgramDir is // initialized to C:\Program Files\TacticalAgent -func (a *Agent) GetNushell(force bool) { +func (a *Agent) InstallNushell(force bool) { + conf := a.GetAgentCheckInConfig(a.GetCheckInConfFromAPI()) + if !conf.InstallNushell { + return + } + if trmm.FileExists(a.NuBin) { if force { + a.Logger.Debugln(a.NuBin, "InstallNushell(): Forced install. Removing nu.exe binary.") err := os.Remove(a.NuBin) if err != nil { - a.Logger.Errorln("GetNushell(): Error removing nu binary:", err) + a.Logger.Errorln("InstallNushell(): Error removing nu.exe binary:", err) return } } else { @@ -898,50 +918,96 @@ func (a *Agent) GetNushell(force bool) { if !trmm.FileExists(programBinDir) { err := os.MkdirAll(programBinDir, 0755) if err != nil { - a.Logger.Errorln("GetNushell(): Error creating Program Files bin folder:", err) + a.Logger.Errorln("InstallNushell(): Error creating Program Files bin folder:", err) return } } + if conf.NushellEnableConfig { + // Create 0-byte config files for Nushell + nushellPath := path.Join(a.ProgramDir, "etc", "nushell") + nushellConfig := path.Join(nushellPath, "config.nu") + nushellEnv := path.Join(nushellPath, "env.nu") + if !trmm.FileExists(nushellPath) { + err := os.MkdirAll(nushellPath, 0755) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error creating Program Files/nushell:", err) + return + } + } + + if !trmm.FileExists(nushellConfig) { + _, err := os.Create(nushellConfig) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error creating nushell config.nu:", err) + return + } + err = os.Chmod(nushellConfig, 0744) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error changing permissions for nushell config.nu:", err) + return + } + } + if !trmm.FileExists(nushellEnv) { + _, err := os.Create(nushellEnv) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error creating nushell env.nu:", err) + return + } + err = os.Chmod(nushellEnv, 0744) + if err != nil { + a.Logger.Errorln("InstallNushell(): Error changing permissions for nushell env.nu:", err) + return + } + } + } + var ( assetName string url string ) - switch runtime.GOOS { - case "windows": - switch runtime.GOARCH { - case "amd64": - // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-x86_64-windows-msvc-full.zip - assetName = fmt.Sprintf("nu-%s-x86_64-windows-msvc-full.zip", nuVersion) - case "arm64": - // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-aarch64-windows-msvc-full.zip - assetName = fmt.Sprintf("nu-%s-aarch64-windows-msvc-full.zip", nuVersion) + if conf.InstallNushellUrl != "" { + url = conf.InstallNushellUrl + url = strings.Replace(url, "{OS}", runtime.GOOS, -1) + url = strings.Replace(url, "{ARCH}", runtime.GOARCH, -1) + url = strings.Replace(url, "{VERSION}", conf.InstallNushellVersion, -1) + } else { + switch runtime.GOOS { + case "windows": + switch runtime.GOARCH { + case "amd64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-x86_64-windows-msvc-full.zip + assetName = fmt.Sprintf("nu-%s-x86_64-windows-msvc-full.zip", conf.InstallNushellVersion) + case "arm64": + // https://github.com/nushell/nushell/releases/download/0.87.0/nu-0.87.0-aarch64-windows-msvc-full.zip + assetName = fmt.Sprintf("nu-%s-aarch64-windows-msvc-full.zip", conf.InstallNushellVersion) + default: + a.Logger.Debugln("InstallNushell(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } default: - a.Logger.Debugln("GetNushell(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + a.Logger.Debugln("InstallNushell(): Unsupported OS:", runtime.GOOS) return } - default: - a.Logger.Debugln("GetNushell(): Unsupported OS:", runtime.GOOS) - return + url = fmt.Sprintf("https://github.com/nushell/nushell/releases/download/%s/%s", conf.InstallNushellVersion, assetName) } - url = fmt.Sprintf("https://github.com/nushell/nushell/releases/download/%s/%s", nuVersion, assetName) - a.Logger.Debugln("GetNushell(): Nu download url:", url) + a.Logger.Debugln("InstallNushell(): Nu download url:", url) tmpDir, err := os.MkdirTemp("", "trmm") if err != nil { - a.Logger.Errorln("GetNushell(): Error creating temp directory:", err) + a.Logger.Errorln("InstallNushell(): Error creating temp directory:", err) return } defer func(path string) { err := os.RemoveAll(path) if err != nil { - a.Logger.Errorln("GetNushell(): Error removing temp directory:", err) + a.Logger.Errorln("InstallNushell(): Error removing temp directory:", err) } }(tmpDir) tmpAssetName := filepath.Join(tmpDir, assetName) - a.Logger.Debugln("GetNushell(): tmpAssetName:", tmpAssetName) + a.Logger.Debugln("InstallNushell(): tmpAssetName:", tmpAssetName) rClient := resty.New() rClient.SetTimeout(20 * time.Minute) @@ -954,35 +1020,50 @@ func (a *Agent) GetNushell(force bool) { r, err := rClient.R().SetOutput(tmpAssetName).Get(url) if err != nil { - a.Logger.Errorln("GetNushell(): Unable to download nu from github.", err) + a.Logger.Errorln("InstallNushell(): Unable to download nu:", err) return } if r.IsError() { - a.Logger.Errorln("GetNushell(): Unable to download nu from github. Status code", r.StatusCode()) + a.Logger.Errorln("InstallNushell(): Unable to download nu. Status code:", r.StatusCode()) return } - err = Unzip(tmpAssetName, tmpDir) - if err != nil { - a.Logger.Errorln("GetNushell(): Failed to unzip downloaded zip file:", err) - return - } + if conf.InstallNushellUrl != "" { + // InstallNushellUrl is not compressed. + err = copyFile(path.Join(tmpDir, tmpAssetName), a.NuBin) + if err != nil { + a.Logger.Errorln("InstallNushell(): Failed to copy nu file to install dir:", err) + return + } + } else { + err = Unzip(tmpAssetName, tmpDir) + if err != nil { + a.Logger.Errorln("InstallNushell(): Failed to unzip downloaded zip file:", err) + return + } - err = copyFile(path.Join(tmpDir, "nu.exe"), a.NuBin) - if err != nil { - a.Logger.Errorln("GetNushell(): Failed to copy nu.exe file to install dir:", err) - return + err = copyFile(path.Join(tmpDir, "nu.exe"), a.NuBin) + if err != nil { + a.Logger.Errorln("InstallNushell(): Failed to copy nu.exe file to install dir:", err) + return + } } } -// GetDeno will download deno from GitHub and install (copy) it to nixAgentBinDir -func (a *Agent) GetDeno(force bool) { +// InstallDeno will download deno from GitHub and install (copy) it to ProgramDir\bin, where ProgramDir is +// initialized to C:\Program Files\TacticalAgent +func (a *Agent) InstallDeno(force bool) { + conf := a.GetAgentCheckInConfig(a.GetCheckInConfFromAPI()) + if !conf.InstallDeno { + return + } + if trmm.FileExists(a.DenoBin) { if force { err := os.Remove(a.DenoBin) if err != nil { - a.Logger.Errorln("GetDeno(): Error removing deno binary:", err) + a.Logger.Errorln("InstallDeno(): Error removing deno binary:", err) return } } else { @@ -994,7 +1075,7 @@ func (a *Agent) GetDeno(force bool) { if !trmm.FileExists(programBinDir) { err := os.MkdirAll(programBinDir, 0755) if err != nil { - a.Logger.Errorln("GetDeno(): Error creating Program Files bin folder:", err) + a.Logger.Errorln("InstallDeno(): Error creating Program Files bin folder:", err) return } } @@ -1004,45 +1085,44 @@ func (a *Agent) GetDeno(force bool) { url string ) - switch runtime.GOOS { - case "windows": - switch runtime.GOARCH { - case "amd64": - // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-pc-windows-msvc.zip - assetName = fmt.Sprintf("deno-x86_64-pc-windows-msvc.zip") + if conf.InstallDenoUrl != "" { + url = conf.InstallDenoUrl + url = strings.Replace(url, "{OS}", runtime.GOOS, -1) + url = strings.Replace(url, "{ARCH}", runtime.GOARCH, -1) + url = strings.Replace(url, "{VERSION}", conf.InstallDenoVersion, -1) + } else { + switch runtime.GOOS { + case "windows": + switch runtime.GOARCH { + case "amd64": + // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-pc-windows-msvc.zip + assetName = fmt.Sprintf("deno-x86_64-pc-windows-msvc.zip") + default: + a.Logger.Debugln("InstallDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + return + } default: - a.Logger.Debugln("GetDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) + a.Logger.Debugln("InstallDeno(): Unsupported OS:", runtime.GOOS) return } - default: - a.Logger.Debugln("GetDeno(): Unsupported OS:", runtime.GOOS) - return + url = fmt.Sprintf("https://github.com/denoland/deno/releases/download/%s/%s", conf.InstallDenoVersion, assetName) } - url = fmt.Sprintf("https://github.com/denoland/deno/releases/download/%s/%s", denoVersion, assetName) - a.Logger.Debugln("GetDeno(): Deno download url:", url) + a.Logger.Debugln("InstallDeno(): Deno download url:", url) tmpDir, err := os.MkdirTemp("", "trmm") if err != nil { - a.Logger.Errorln("GetDeno(): Error creating temp directory:", err) + a.Logger.Errorln("InstallDeno(): Error creating temp directory:", err) return } defer func(path string) { err := os.RemoveAll(path) if err != nil { - a.Logger.Errorln("GetDeno(): Error removing temp directory:", err) + a.Logger.Errorln("InstallDeno(): Error removing temp directory:", err) } }(tmpDir) tmpAssetName := filepath.Join(tmpDir, assetName) - a.Logger.Debugln("GetDeno(): tmpAssetName:", tmpAssetName) - - if !trmm.FileExists(nixAgentBinDir) { - err = os.MkdirAll(nixAgentBinDir, 0755) - if err != nil { - a.Logger.Errorln("GetDeno(): Error creating nixAgentBinDir:", err) - return - } - } + a.Logger.Debugln("InstallDeno(): tmpAssetName:", tmpAssetName) rClient := resty.New() rClient.SetTimeout(20 * time.Minute) @@ -1055,24 +1135,34 @@ func (a *Agent) GetDeno(force bool) { r, err := rClient.R().SetOutput(tmpAssetName).Get(url) if err != nil { - a.Logger.Errorln("GetDeno(): Unable to download deno from github.", err) + a.Logger.Errorln("InstallDeno(): Unable to download deno:", err) return } if r.IsError() { - a.Logger.Errorln("GetDeno(): Unable to download deno from github. Status code", r.StatusCode()) + a.Logger.Errorln("InstallDeno(): Unable to download deno. Status code:", r.StatusCode()) return } - err = Unzip(tmpAssetName, tmpDir) - if err != nil { - a.Logger.Errorln("GetDeno(): Failed to unzip downloaded zip file:", err) - return - } + if conf.InstallDenoUrl != "" { + // InstallDenoUrl is not compressed. + err = copyFile(path.Join(tmpDir, tmpAssetName), a.DenoBin) + if err != nil { + a.Logger.Errorln("InstallDeno(): Failed to copy deno file to install dir:", err) + return + } + } else { + // GitHub asset is zip compressed. + err = Unzip(tmpAssetName, tmpDir) + if err != nil { + a.Logger.Errorln("InstallDeno(): Failed to unzip downloaded zip file:", err) + return + } - err = copyFile(path.Join(tmpDir, "deno.exe"), a.DenoBin) - if err != nil { - a.Logger.Errorln("GetDeno(): Failed to copy deno.exe file to install dir:", err) - return + err = copyFile(path.Join(tmpDir, "deno.exe"), a.DenoBin) + if err != nil { + a.Logger.Errorln("InstallDeno(): Failed to copy deno.exe file to install dir:", err) + return + } } } diff --git a/agent/checks.go b/agent/checks.go index 930e947..5e0905d 100644 --- a/agent/checks.go +++ b/agent/checks.go @@ -169,7 +169,7 @@ type ScriptCheckResult struct { // ScriptCheck runs either bat, powershell or python script func (a *Agent) ScriptCheck(data rmm.Check, r *resty.Client) { start := time.Now() - stdout, stderr, retcode, _ := a.RunScript(data.Script.Code, data.Script.Shell, data.ScriptArgs, data.Timeout, data.Script.RunAsUser, data.EnvVars) + stdout, stderr, retcode, _ := a.RunScript(data.Script.Code, data.Script.Shell, data.ScriptArgs, data.Timeout, data.Script.RunAsUser, data.EnvVars, data.NushellEnableConfig, data.DenoDefaultPermissions) payload := ScriptCheckResult{ ID: data.CheckPK, diff --git a/agent/choco_windows.go b/agent/choco_windows.go index f75b15f..7019785 100644 --- a/agent/choco_windows.go +++ b/agent/choco_windows.go @@ -45,7 +45,7 @@ func (a *Agent) InstallChoco() { return } - _, _, exitcode, err := a.RunScript(string(r.Body()), "powershell", []string{}, 900, false, []string{}) + _, _, exitcode, err := a.RunScript(string(r.Body()), "powershell", []string{}, 900, false, []string{}, false, "") if err != nil { a.Logger.Debugln(err) a.rClient.R().SetBody(result).Post(url) diff --git a/agent/embed_darwin.go b/agent/embed_darwin.go index 6a42a6d..c69225a 100644 --- a/agent/embed_darwin.go +++ b/agent/embed_darwin.go @@ -20,5 +20,5 @@ import _ "embed" var ventura_mesh_fix string func (a *Agent) FixVenturaMesh() { - a.RunScript(ventura_mesh_fix, "foo", []string{}, 45, false, []string{}) + a.RunScript(ventura_mesh_fix, "foo", []string{}, 45, false, []string{}, false, "") } diff --git a/agent/rpc.go b/agent/rpc.go index 03b1508..a58e6f5 100644 --- a/agent/rpc.go +++ b/agent/rpc.go @@ -26,22 +26,24 @@ import ( ) type NatsMsg struct { - Func string `json:"func"` - Timeout int `json:"timeout"` - Data map[string]string `json:"payload"` - ScriptArgs []string `json:"script_args"` - ProcPID int32 `json:"procpid"` - TaskPK int `json:"taskpk"` - ScheduledTask SchedTask `json:"schedtaskpayload"` - RecoveryCommand string `json:"recoverycommand"` - UpdateGUIDs []string `json:"guids"` - ChocoProgName string `json:"choco_prog_name"` - PendingActionPK int `json:"pending_action_pk"` - PatchMgmt bool `json:"patch_mgmt"` - ID int `json:"id"` - Code string `json:"code"` - RunAsUser bool `json:"run_as_user"` - EnvVars []string `json:"env_vars"` + Func string `json:"func"` + Timeout int `json:"timeout"` + Data map[string]string `json:"payload"` + ScriptArgs []string `json:"script_args"` + ProcPID int32 `json:"procpid"` + TaskPK int `json:"taskpk"` + ScheduledTask SchedTask `json:"schedtaskpayload"` + RecoveryCommand string `json:"recoverycommand"` + UpdateGUIDs []string `json:"guids"` + ChocoProgName string `json:"choco_prog_name"` + PendingActionPK int `json:"pending_action_pk"` + PatchMgmt bool `json:"patch_mgmt"` + ID int `json:"id"` + Code string `json:"code"` + RunAsUser bool `json:"run_as_user"` + EnvVars []string `json:"env_vars"` + NushellEnableConfig bool `json:"nushell_enable_config"` + DenoDefaultPermissions string `json:"deno_default_permissions"` } var ( @@ -264,7 +266,7 @@ func (a *Agent) RunRPC() { var resultData rmm.RunScriptResp ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle)) start := time.Now() - stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout, p.RunAsUser, p.EnvVars) + stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout, p.RunAsUser, p.EnvVars, p.NushellEnableConfig, p.DenoDefaultPermissions) resultData.ExecTime = time.Since(start).Seconds() resultData.ID = p.ID @@ -294,7 +296,7 @@ func (a *Agent) RunRPC() { var retData rmm.RunScriptResp ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle)) start := time.Now() - stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout, p.RunAsUser, p.EnvVars) + stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout, p.RunAsUser, p.EnvVars, p.NushellEnableConfig, p.DenoDefaultPermissions) retData.ExecTime = time.Since(start).Seconds() if err != nil { @@ -438,9 +440,9 @@ func (a *Agent) RunRPC() { case "installpython": go a.GetPython(true) case "installnushell": - go a.GetNushell(true) + go a.InstallNushell(true) case "installdeno": - go a.GetDeno(true) + go a.InstallDeno(true) case "installchoco": go a.InstallChoco() case "installwithchoco": diff --git a/agent/svc.go b/agent/svc.go index b04bb9d..0827448 100644 --- a/agent/svc.go +++ b/agent/svc.go @@ -29,15 +29,23 @@ func (a *Agent) RunAsService(nc *nats.Conn) { } type AgentCheckInConfig struct { - Hello int `json:"checkin_hello"` - AgentInfo int `json:"checkin_agentinfo"` - WinSvc int `json:"checkin_winsvc"` - PubIP int `json:"checkin_pubip"` - Disks int `json:"checkin_disks"` - SW int `json:"checkin_sw"` - WMI int `json:"checkin_wmi"` - SyncMesh int `json:"checkin_syncmesh"` - LimitData bool `json:"limit_data"` + Hello int `json:"checkin_hello"` + AgentInfo int `json:"checkin_agentinfo"` + WinSvc int `json:"checkin_winsvc"` + PubIP int `json:"checkin_pubip"` + Disks int `json:"checkin_disks"` + SW int `json:"checkin_sw"` + WMI int `json:"checkin_wmi"` + SyncMesh int `json:"checkin_syncmesh"` + LimitData bool `json:"limit_data"` + InstallNushell bool `json:"install_nushell"` + InstallNushellVersion string `json:"install_nushell_version"` + InstallNushellUrl string `json:"install_nushell_url"` + NushellEnableConfig bool `json:"nushell_enable_config"` + InstallDeno bool `json:"install_deno"` + InstallDenoVersion string `json:"install_deno_version"` + InstallDenoUrl string `json:"install_deno_url"` + DenoDefaultPermissions string `json:"deno_default_permissions"` } func (a *Agent) AgentSvc(nc *nats.Conn) { @@ -49,8 +57,6 @@ func (a *Agent) AgentSvc(nc *nats.Conn) { a.Logger.Errorln("AgentSvc() createWinTempDir():", err) } } - a.GetNushell(false) - a.GetDeno(false) a.RunMigrations() @@ -64,8 +70,9 @@ func (a *Agent) AgentSvc(nc *nats.Conn) { a.CleanupAgentUpdates() } + // Windows has GetAgentCheckInConfig() while unix has a stub GetAgentCheckInConfig() conf := a.GetAgentCheckInConfig(a.GetCheckInConfFromAPI()) - a.Logger.Debugf("+%v\n", conf) + a.Logger.Debugf("AgentCheckInConf: %+v\n", conf) for _, s := range natsCheckin { if conf.LimitData && stringInSlice(s, limitNatsData) { continue @@ -75,6 +82,15 @@ func (a *Agent) AgentSvc(nc *nats.Conn) { } } + // The server conf check is also done in the functions to keep the parameters the same. + // Don't force a download when restarting the service. + if conf.InstallNushell { + a.InstallNushell(false) + } + if conf.InstallDeno { + a.InstallDeno(false) + } + go a.SyncMeshNodeID() time.Sleep(time.Duration(randRange(1, 3)) * time.Second) @@ -142,6 +158,14 @@ func (a *Agent) GetCheckInConfFromAPI() AgentCheckInConfig { ret.WMI = randRange(3000, 4000) ret.SyncMesh = randRange(800, 1200) ret.LimitData = false + ret.InstallNushell = false + ret.InstallNushellVersion = "" + ret.InstallNushellUrl = "" + ret.NushellEnableConfig = false + ret.InstallDeno = false + ret.InstallDenoVersion = "" + ret.InstallDenoUrl = "" + ret.DenoDefaultPermissions = "" } else { ret.Hello = r.Result().(*AgentCheckInConfig).Hello ret.AgentInfo = r.Result().(*AgentCheckInConfig).AgentInfo @@ -152,6 +176,14 @@ func (a *Agent) GetCheckInConfFromAPI() AgentCheckInConfig { ret.WMI = r.Result().(*AgentCheckInConfig).WMI ret.SyncMesh = r.Result().(*AgentCheckInConfig).SyncMesh ret.LimitData = r.Result().(*AgentCheckInConfig).LimitData + ret.InstallNushell = r.Result().(*AgentCheckInConfig).InstallNushell + ret.InstallNushellVersion = r.Result().(*AgentCheckInConfig).InstallNushellVersion + ret.InstallNushellUrl = r.Result().(*AgentCheckInConfig).InstallNushellUrl + ret.NushellEnableConfig = r.Result().(*AgentCheckInConfig).NushellEnableConfig + ret.InstallDeno = r.Result().(*AgentCheckInConfig).InstallDeno + ret.InstallDenoVersion = r.Result().(*AgentCheckInConfig).InstallDenoVersion + ret.InstallDenoUrl = r.Result().(*AgentCheckInConfig).InstallDenoUrl + ret.DenoDefaultPermissions = r.Result().(*AgentCheckInConfig).DenoDefaultPermissions } return ret } diff --git a/agent/tasks_windows.go b/agent/tasks_windows.go index ab199f4..a4db248 100644 --- a/agent/tasks_windows.go +++ b/agent/tasks_windows.go @@ -14,13 +14,13 @@ package agent import ( "encoding/json" "fmt" + "github.com/amidaware/taskmaster" "os" "path/filepath" "strings" "time" rmm "github.com/amidaware/rmmagent/shared" - "github.com/amidaware/taskmaster" "github.com/rickb777/date/period" ) @@ -59,7 +59,7 @@ func (a *Agent) RunTask(id int) error { action_start := time.Now() if action.ActionType == "script" { - stdout, stderr, retcode, err := a.RunScript(action.Code, action.Shell, action.Args, action.Timeout, action.RunAsUser, action.EnvVars) + stdout, stderr, retcode, err := a.RunScript(action.Code, action.Shell, action.Args, action.Timeout, action.RunAsUser, action.EnvVars, action.NushellEnableConfig, action.DenoDefaultPermissions) if err != nil { a.Logger.Debugln(err) diff --git a/main.go b/main.go index 0d46a62..4ccbdbf 100644 --- a/main.go +++ b/main.go @@ -117,10 +117,10 @@ func main() { fmt.Println(a.PublicIP()) case "getpython": a.GetPython(true) - case "getdeno": - a.GetDeno(true) - case "getnushell": - a.GetNushell(true) + case "installdeno": + a.InstallDeno(true) + case "installnushell": + a.InstallNushell(true) case "runmigrations": a.RunMigrations() case "recovermesh": diff --git a/shared/types.go b/shared/types.go index d7a6f24..f98beb9 100644 --- a/shared/types.go +++ b/shared/types.go @@ -157,29 +157,31 @@ type CheckInfo struct { } type Check struct { - Script Script `json:"script"` - AssignedTasks []AssignedTask `json:"assigned_tasks"` - CheckPK int `json:"id"` - CheckType string `json:"check_type"` - Status string `json:"status"` - Threshold int `json:"threshold"` - Disk string `json:"disk"` - IP string `json:"ip"` - ScriptArgs []string `json:"script_args"` - EnvVars []string `json:"env_vars"` - Timeout int `json:"timeout"` - ServiceName string `json:"svc_name"` - PassStartPending bool `json:"pass_if_start_pending"` - PassNotExist bool `json:"pass_if_svc_not_exist"` - RestartIfStopped bool `json:"restart_if_stopped"` - LogName string `json:"log_name"` - EventID int `json:"event_id"` - EventIDWildcard bool `json:"event_id_is_wildcard"` - EventType string `json:"event_type"` - EventSource string `json:"event_source"` - EventMessage string `json:"event_message"` - FailWhen string `json:"fail_when"` - SearchLastDays int `json:"search_last_days"` + Script Script `json:"script"` + AssignedTasks []AssignedTask `json:"assigned_tasks"` + CheckPK int `json:"id"` + CheckType string `json:"check_type"` + Status string `json:"status"` + Threshold int `json:"threshold"` + Disk string `json:"disk"` + IP string `json:"ip"` + ScriptArgs []string `json:"script_args"` + EnvVars []string `json:"env_vars"` + NushellEnableConfig bool `json:"nushell_enable_config"` + DenoDefaultPermissions string `json:"deno_default_permissions"` + Timeout int `json:"timeout"` + ServiceName string `json:"svc_name"` + PassStartPending bool `json:"pass_if_start_pending"` + PassNotExist bool `json:"pass_if_svc_not_exist"` + RestartIfStopped bool `json:"restart_if_stopped"` + LogName string `json:"log_name"` + EventID int `json:"event_id"` + EventIDWildcard bool `json:"event_id_is_wildcard"` + EventType string `json:"event_type"` + EventSource string `json:"event_source"` + EventMessage string `json:"event_message"` + FailWhen string `json:"fail_when"` + SearchLastDays int `json:"search_last_days"` } type AllChecks struct { @@ -188,15 +190,17 @@ type AllChecks struct { } type TaskAction struct { - ActionType string `json:"type"` - Command string `json:"command"` - Shell string `json:"shell"` - ScriptName string `json:"script_name"` - Code string `json:"code"` - Args []string `json:"script_args"` - Timeout int `json:"timeout"` - RunAsUser bool `json:"run_as_user"` - EnvVars []string `json:"env_vars"` + ActionType string `json:"type"` + Command string `json:"command"` + Shell string `json:"shell"` + ScriptName string `json:"script_name"` + Code string `json:"code"` + Args []string `json:"script_args"` + Timeout int `json:"timeout"` + RunAsUser bool `json:"run_as_user"` + EnvVars []string `json:"env_vars"` + NushellEnableConfig bool `json:"nushell_enable_config"` + DenoDefaultPermissions string `json:"deno_default_permissions"` } type AutomatedTask struct { From 055f11b0e3a60986b595f3a1272bdf74a2dfdaf8 Mon Sep 17 00:00:00 2001 From: conlan0 <87742085+conlan0@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:34:14 -0500 Subject: [PATCH 04/19] Add shutdown command --- agent/rpc.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/agent/rpc.go b/agent/rpc.go index 8ecae5c..120faed 100644 --- a/agent/rpc.go +++ b/agent/rpc.go @@ -339,6 +339,22 @@ func (a *Agent) RunRPC() { msg.Respond(resp) }() + case "shutdown": + go func() { + a.Logger.Debugln("Scheduling immediate shutdown") + var resp []byte + ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle)) + ret.Encode("ok") + msg.Respond(resp) + if runtime.GOOS == "windows" { + CMD("shutdown.exe", []string{"/s", "/t", "5", "/f"}, 15, false) + } else { + opts := a.NewCMDOpts() + opts.Command = "shutdown" + a.CmdV2(opts) + } + }() + case "rebootnow": go func() { a.Logger.Debugln("Scheduling immediate reboot") From d3eb6e041b7cbc1af83b42de09f4a99e1eeacc6f Mon Sep 17 00:00:00 2001 From: conlan0 <87742085+conlan0@users.noreply.github.com> Date: Wed, 21 Feb 2024 21:12:18 -0500 Subject: [PATCH 05/19] update macos/linux command --- agent/rpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/rpc.go b/agent/rpc.go index 120faed..e9e8828 100644 --- a/agent/rpc.go +++ b/agent/rpc.go @@ -350,7 +350,7 @@ func (a *Agent) RunRPC() { CMD("shutdown.exe", []string{"/s", "/t", "5", "/f"}, 15, false) } else { opts := a.NewCMDOpts() - opts.Command = "shutdown" + opts.Command = "shutdown -h now" a.CmdV2(opts) } }() From e40dcf6079f645919e26f9bacba49a02454ddfb7 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Thu, 22 Feb 2024 04:32:12 +0000 Subject: [PATCH 06/19] use filepath --- agent/install.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/agent/install.go b/agent/install.go index 4186754..0c180fa 100644 --- a/agent/install.go +++ b/agent/install.go @@ -17,7 +17,6 @@ import ( "io" "net/url" "os" - "path" "path/filepath" "regexp" "runtime" @@ -306,7 +305,7 @@ func (a *Agent) Install(i *Installer) { } if runtime.GOOS == "windows" { - os.MkdirAll(path.Join(a.ProgramDir, "bin"), 0755) + os.MkdirAll(filepath.Join(a.ProgramDir, "bin"), 0755) // send software api a.SendSoftware() From a29709c599a5e465015a4e42085b02940dea86f9 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Thu, 22 Feb 2024 11:36:45 -0800 Subject: [PATCH 07/19] use filepath --- agent/agent_unix.go | 19 +++++++++---------- agent/agent_windows.go | 23 +++++++++++------------ agent/utils.go | 5 ++--- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/agent/agent_unix.go b/agent/agent_unix.go index e0eac92..edaa92e 100644 --- a/agent/agent_unix.go +++ b/agent/agent_unix.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" "os" - "path" "path/filepath" "runtime" "strconv" @@ -201,9 +200,9 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, if nushellEnableConfig { nushellArgs = []string{ "--config", - path.Join(nixAgentEtcDir, "nushell", "config.nu"), + filepath.Join(nixAgentEtcDir, "nushell", "config.nu"), "--env-config", - path.Join(nixAgentEtcDir, "nushell", "env.nu"), + filepath.Join(nixAgentEtcDir, "nushell", "env.nu"), } } else { nushellArgs = []string{"--no-config-file"} @@ -606,9 +605,9 @@ func (a *Agent) InstallNushell(force bool) { if conf.NushellEnableConfig { // Create 0-byte config files for Nushell - nushellPath := path.Join(nixAgentEtcDir, "nushell") - nushellConfig := path.Join(nushellPath, "config.nu") - nushellEnv := path.Join(nushellPath, "env.nu") + nushellPath := filepath.Join(nixAgentEtcDir, "nushell") + nushellConfig := filepath.Join(nushellPath, "config.nu") + nushellEnv := filepath.Join(nushellPath, "env.nu") if !trmm.FileExists(nushellPath) { err := os.MkdirAll(nushellPath, 0755) if err != nil { @@ -721,7 +720,7 @@ func (a *Agent) InstallNushell(force bool) { if conf.InstallNushellUrl != "" { // InstallNushellUrl is not compressed. - err = copyFile(path.Join(tmpDir, tmpAssetName), a.NuBin) + err = copyFile(filepath.Join(tmpDir, tmpAssetName), a.NuBin) if err != nil { a.Logger.Errorln("InstallNushell(): Failed to copy nu file to install dir:", err) return @@ -734,7 +733,7 @@ func (a *Agent) InstallNushell(force bool) { return } - err = copyFile(path.Join(tmpDir, targzDirName, "nu"), a.NuBin) + err = copyFile(filepath.Join(tmpDir, targzDirName, "nu"), a.NuBin) if err != nil { a.Logger.Errorln("InstallNushell(): Failed to copy nu file to install dir:", err) return @@ -854,7 +853,7 @@ func (a *Agent) InstallDeno(force bool) { if conf.InstallDenoUrl != "" { // InstallDenoUrl is not compressed. - err = copyFile(path.Join(tmpDir, tmpAssetName), a.DenoBin) + err = copyFile(filepath.Join(tmpDir, tmpAssetName), a.DenoBin) if err != nil { a.Logger.Errorln("InstallDeno(): Failed to copy deno file to install dir:", err) return @@ -867,7 +866,7 @@ func (a *Agent) InstallDeno(force bool) { return } - err = copyFile(path.Join(tmpDir, "deno"), a.DenoBin) + err = copyFile(filepath.Join(tmpDir, "deno"), a.DenoBin) if err != nil { a.Logger.Errorln("InstallDeno(): Failed to copy deno file to install dir:", err) return diff --git a/agent/agent_windows.go b/agent/agent_windows.go index cc22cbf..f9d1b6e 100644 --- a/agent/agent_windows.go +++ b/agent/agent_windows.go @@ -19,7 +19,6 @@ import ( "fmt" "os" "os/exec" - "path" "path/filepath" "runtime" "strconv" @@ -162,9 +161,9 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, if nushellEnableConfig { nushellArgs = []string{ "--config", - path.Join(a.ProgramDir, "etc", "nushell", "config.nu"), + filepath.Join(a.ProgramDir, "etc", "nushell", "config.nu"), "--env-config", - path.Join(a.ProgramDir, "etc", "nushell", "env.nu"), + filepath.Join(a.ProgramDir, "etc", "nushell", "env.nu"), } } else { nushellArgs = []string{"--no-config-file"} @@ -914,7 +913,7 @@ func (a *Agent) InstallNushell(force bool) { } } - programBinDir := path.Join(a.ProgramDir, "bin") + programBinDir := filepath.Join(a.ProgramDir, "bin") if !trmm.FileExists(programBinDir) { err := os.MkdirAll(programBinDir, 0755) if err != nil { @@ -925,9 +924,9 @@ func (a *Agent) InstallNushell(force bool) { if conf.NushellEnableConfig { // Create 0-byte config files for Nushell - nushellPath := path.Join(a.ProgramDir, "etc", "nushell") - nushellConfig := path.Join(nushellPath, "config.nu") - nushellEnv := path.Join(nushellPath, "env.nu") + nushellPath := filepath.Join(a.ProgramDir, "etc", "nushell") + nushellConfig := filepath.Join(nushellPath, "config.nu") + nushellEnv := filepath.Join(nushellPath, "env.nu") if !trmm.FileExists(nushellPath) { err := os.MkdirAll(nushellPath, 0755) if err != nil { @@ -1030,7 +1029,7 @@ func (a *Agent) InstallNushell(force bool) { if conf.InstallNushellUrl != "" { // InstallNushellUrl is not compressed. - err = copyFile(path.Join(tmpDir, tmpAssetName), a.NuBin) + err = copyFile(filepath.Join(tmpDir, tmpAssetName), a.NuBin) if err != nil { a.Logger.Errorln("InstallNushell(): Failed to copy nu file to install dir:", err) return @@ -1042,7 +1041,7 @@ func (a *Agent) InstallNushell(force bool) { return } - err = copyFile(path.Join(tmpDir, "nu.exe"), a.NuBin) + err = copyFile(filepath.Join(tmpDir, "nu.exe"), a.NuBin) if err != nil { a.Logger.Errorln("InstallNushell(): Failed to copy nu.exe file to install dir:", err) return @@ -1071,7 +1070,7 @@ func (a *Agent) InstallDeno(force bool) { } } - programBinDir := path.Join(a.ProgramDir, "bin") + programBinDir := filepath.Join(a.ProgramDir, "bin") if !trmm.FileExists(programBinDir) { err := os.MkdirAll(programBinDir, 0755) if err != nil { @@ -1145,7 +1144,7 @@ func (a *Agent) InstallDeno(force bool) { if conf.InstallDenoUrl != "" { // InstallDenoUrl is not compressed. - err = copyFile(path.Join(tmpDir, tmpAssetName), a.DenoBin) + err = copyFile(filepath.Join(tmpDir, tmpAssetName), a.DenoBin) if err != nil { a.Logger.Errorln("InstallDeno(): Failed to copy deno file to install dir:", err) return @@ -1158,7 +1157,7 @@ func (a *Agent) InstallDeno(force bool) { return } - err = copyFile(path.Join(tmpDir, "deno.exe"), a.DenoBin) + err = copyFile(filepath.Join(tmpDir, "deno.exe"), a.DenoBin) if err != nil { a.Logger.Errorln("InstallDeno(): Failed to copy deno.exe file to install dir:", err) return diff --git a/agent/utils.go b/agent/utils.go index 39d8fe2..938271a 100644 --- a/agent/utils.go +++ b/agent/utils.go @@ -23,7 +23,6 @@ import ( "net" "os" "os/exec" - "path" "path/filepath" "runtime" goDebug "runtime/debug" @@ -315,7 +314,7 @@ func (a *Agent) ExtractTarGz(targz string, destDir string) (extractedDir string, switch header.Typeflag { case tar.TypeDir: - if err := os.MkdirAll(path.Join(destDir, header.Name), 0755); err != nil { + if err := os.MkdirAll(filepath.Join(destDir, header.Name), 0755); err != nil { a.Logger.Errorln("ExtractTarGz(): Mkdir() failed:", err.Error()) return "", err } @@ -323,7 +322,7 @@ func (a *Agent) ExtractTarGz(targz string, destDir string) (extractedDir string, extractedDir = header.Name } case tar.TypeReg: - outFile, err := os.Create(path.Join(destDir, header.Name)) + outFile, err := os.Create(filepath.Join(destDir, header.Name)) if err != nil { a.Logger.Errorln("ExtractTarGz(): Create() failed:", err.Error()) return "", err From 47b482419a132856c378fe7f7743ac9d853d6044 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Thu, 22 Feb 2024 22:23:43 -0800 Subject: [PATCH 08/19] fixes to tar --- agent/utils.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/agent/utils.go b/agent/utils.go index 938271a..7f326d8 100644 --- a/agent/utils.go +++ b/agent/utils.go @@ -16,6 +16,7 @@ import ( "archive/zip" "bytes" "compress/gzip" + "errors" "fmt" "io" "math" @@ -291,16 +292,18 @@ func (a *Agent) ExtractTarGz(targz string, destDir string) (extractedDir string, a.Logger.Errorln("ExtractTarGz(): Open() failed:", err.Error()) return "", err } + defer gzipStream.Close() uncompressedStream, err := gzip.NewReader(gzipStream) if err != nil { a.Logger.Errorln("ExtractTarGz(): NewReader() failed:", err.Error()) return "", err } + defer uncompressedStream.Close() extractedDir = "" tarReader := tar.NewReader(uncompressedStream) - for true { + for { header, err := tarReader.Next() if err == io.EOF { @@ -338,8 +341,9 @@ func (a *Agent) ExtractTarGz(targz string, destDir string) (extractedDir string, } default: - a.Logger.Errorln("ExtractTarGz(): Unknown type: %s in %s", header.Typeflag, header.Name) - return "", err + errMsg := fmt.Sprintf("ExtractTarGz(): Unknown type: %v in %s", header.Typeflag, header.Name) + a.Logger.Errorln(errMsg) + return "", errors.New(errMsg) } } From 996631e108698e642c801efb970dfdc9494db74f Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Thu, 22 Feb 2024 22:29:39 -0800 Subject: [PATCH 09/19] do not block --- agent/svc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/svc.go b/agent/svc.go index 0827448..5770f4c 100644 --- a/agent/svc.go +++ b/agent/svc.go @@ -85,10 +85,10 @@ func (a *Agent) AgentSvc(nc *nats.Conn) { // The server conf check is also done in the functions to keep the parameters the same. // Don't force a download when restarting the service. if conf.InstallNushell { - a.InstallNushell(false) + go a.InstallNushell(false) } if conf.InstallDeno { - a.InstallDeno(false) + go a.InstallDeno(false) } go a.SyncMeshNodeID() From 98cae02ec6c77fa56b51686d4c179b5500bdf8e2 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Thu, 22 Feb 2024 22:42:35 -0800 Subject: [PATCH 10/19] tweaks to the install bin funcs --- agent/agent_unix.go | 38 +++++++++++++++++++------------- agent/agent_windows.go | 50 +++++++++++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/agent/agent_unix.go b/agent/agent_unix.go index edaa92e..0777c64 100644 --- a/agent/agent_unix.go +++ b/agent/agent_unix.go @@ -577,6 +577,10 @@ func (a *Agent) GetWMIInfo() map[string]interface{} { // InstallNushell will download nushell from GitHub and install (copy) it to nixAgentBinDir func (a *Agent) InstallNushell(force bool) { + sleepDelay := randRange(1, 10) + a.Logger.Debugf("InstallNushell() sleeping for %v seconds", sleepDelay) + time.Sleep(time.Duration(sleepDelay) * time.Second) + conf := a.GetAgentCheckInConfig(a.GetCheckInConfFromAPI()) if !conf.InstallNushell { return @@ -650,9 +654,9 @@ func (a *Agent) InstallNushell(force bool) { if conf.InstallNushellUrl != "" { url = conf.InstallNushellUrl - url = strings.Replace(url, "{OS}", runtime.GOOS, -1) - url = strings.Replace(url, "{ARCH}", runtime.GOARCH, -1) - url = strings.Replace(url, "{VERSION}", conf.InstallNushellVersion, -1) + url = strings.ReplaceAll(url, "{OS}", runtime.GOOS) + url = strings.ReplaceAll(url, "{ARCH}", runtime.GOARCH) + url = strings.ReplaceAll(url, "{VERSION}", conf.InstallNushellVersion) } else { switch runtime.GOOS { case "darwin": @@ -684,15 +688,15 @@ func (a *Agent) InstallNushell(force bool) { } a.Logger.Debugln("InstallNushell(): Nu download url:", url) - tmpDir, err := os.MkdirTemp("", "trmm") + tmpDir, err := os.MkdirTemp("", "nutemp") if err != nil { - a.Logger.Errorln("InstallNushell(): Error creating temp directory:", err) + a.Logger.Errorln("InstallNushell(): Error creating nushell temp directory:", err) return } defer func(path string) { err := os.RemoveAll(path) if err != nil { - a.Logger.Errorln("InstallNushell(): Error removing temp directory:", err) + a.Logger.Errorln("InstallNushell(): Error removing nushell temp directory:", err) } }(tmpDir) @@ -750,6 +754,10 @@ func (a *Agent) InstallNushell(force bool) { // InstallDeno will download deno from GitHub and install (copy) it to nixAgentBinDir func (a *Agent) InstallDeno(force bool) { + sleepDelay := randRange(1, 10) + a.Logger.Debugf("InstallDeno() sleeping for %v seconds", sleepDelay) + time.Sleep(time.Duration(sleepDelay) * time.Second) + conf := a.GetAgentCheckInConfig(a.GetCheckInConfFromAPI()) if !conf.InstallDeno { return @@ -783,19 +791,19 @@ func (a *Agent) InstallDeno(force bool) { if conf.InstallDenoUrl != "" { url = conf.InstallDenoUrl - url = strings.Replace(url, "{OS}", runtime.GOOS, -1) - url = strings.Replace(url, "{ARCH}", runtime.GOARCH, -1) - url = strings.Replace(url, "{VERSION}", conf.InstallDenoVersion, -1) + url = strings.ReplaceAll(url, "{OS}", runtime.GOOS) + url = strings.ReplaceAll(url, "{ARCH}", runtime.GOARCH) + url = strings.ReplaceAll(url, "{VERSION}", conf.InstallDenoVersion) } else { switch runtime.GOOS { case "darwin": switch runtime.GOARCH { case "arm64": // https://github.com/denoland/deno/releases/download/v1.38.2/deno-aarch64-apple-darwin.zip - assetName = fmt.Sprintf("deno-aarch64-apple-darwin.zip") + assetName = "deno-aarch64-apple-darwin.zip" case "amd64": // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-apple-darwin.zip - assetName = fmt.Sprintf("deno-x86_64-apple-darwin.zip") + assetName = "deno-x86_64-apple-darwin.zip" default: a.Logger.Debugln("InstallDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) return @@ -804,7 +812,7 @@ func (a *Agent) InstallDeno(force bool) { switch runtime.GOARCH { case "amd64": // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-unknown-linux-gnu.zip - assetName = fmt.Sprintf("deno-x86_64-unknown-linux-gnu.zip") + assetName = "deno-x86_64-unknown-linux-gnu.zip" default: a.Logger.Debugln("InstallDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) return @@ -817,15 +825,15 @@ func (a *Agent) InstallDeno(force bool) { } a.Logger.Debugln("InstallDeno(): Deno download url:", url) - tmpDir, err := os.MkdirTemp("", "trmm") + tmpDir, err := os.MkdirTemp("", "denotemp") if err != nil { - a.Logger.Errorln("InstallDeno(): Error creating temp directory:", err) + a.Logger.Errorln("InstallDeno(): Error creating deno temp directory:", err) return } defer func(path string) { err := os.RemoveAll(path) if err != nil { - a.Logger.Errorln("InstallDeno(): Error removing temp directory:", err) + a.Logger.Errorln("InstallDeno(): Error removing deno temp directory:", err) } }(tmpDir) diff --git a/agent/agent_windows.go b/agent/agent_windows.go index f9d1b6e..e125dda 100644 --- a/agent/agent_windows.go +++ b/agent/agent_windows.go @@ -845,6 +845,10 @@ func (a *Agent) GetPython(force bool) { return } + sleepDelay := randRange(1, 10) + a.Logger.Debugf("GetPython() sleeping for %v seconds", sleepDelay) + time.Sleep(time.Duration(sleepDelay) * time.Second) + var archZip string var folder string switch runtime.GOARCH { @@ -895,6 +899,10 @@ func (a *Agent) GetPython(force bool) { // InstallNushell will download nushell from GitHub and install (copy) it to ProgramDir\bin, where ProgramDir is // initialized to C:\Program Files\TacticalAgent func (a *Agent) InstallNushell(force bool) { + sleepDelay := randRange(1, 10) + a.Logger.Debugf("InstallNushell() sleeping for %v seconds", sleepDelay) + time.Sleep(time.Duration(sleepDelay) * time.Second) + conf := a.GetAgentCheckInConfig(a.GetCheckInConfFromAPI()) if !conf.InstallNushell { return @@ -968,9 +976,9 @@ func (a *Agent) InstallNushell(force bool) { if conf.InstallNushellUrl != "" { url = conf.InstallNushellUrl - url = strings.Replace(url, "{OS}", runtime.GOOS, -1) - url = strings.Replace(url, "{ARCH}", runtime.GOARCH, -1) - url = strings.Replace(url, "{VERSION}", conf.InstallNushellVersion, -1) + url = strings.ReplaceAll(url, "{OS}", runtime.GOOS) + url = strings.ReplaceAll(url, "{ARCH}", runtime.GOARCH) + url = strings.ReplaceAll(url, "{VERSION}", conf.InstallNushellVersion) } else { switch runtime.GOOS { case "windows": @@ -993,15 +1001,21 @@ func (a *Agent) InstallNushell(force bool) { } a.Logger.Debugln("InstallNushell(): Nu download url:", url) - tmpDir, err := os.MkdirTemp("", "trmm") + err := createWinTempDir() + if err != nil { + a.Logger.Errorln("InstallNushell(): createWinTempDir:", err) + return + } + + tmpDir, err := os.MkdirTemp(a.WinTmpDir, "nutemp") if err != nil { - a.Logger.Errorln("InstallNushell(): Error creating temp directory:", err) + a.Logger.Errorln("InstallNushell(): Error creating nushell temp directory:", err) return } defer func(path string) { err := os.RemoveAll(path) if err != nil { - a.Logger.Errorln("InstallNushell(): Error removing temp directory:", err) + a.Logger.Errorln("InstallNushell(): Error removing nushell temp directory:", err) } }(tmpDir) @@ -1053,6 +1067,10 @@ func (a *Agent) InstallNushell(force bool) { // InstallDeno will download deno from GitHub and install (copy) it to ProgramDir\bin, where ProgramDir is // initialized to C:\Program Files\TacticalAgent func (a *Agent) InstallDeno(force bool) { + sleepDelay := randRange(1, 10) + a.Logger.Debugf("InstallDeno() sleeping for %v seconds", sleepDelay) + time.Sleep(time.Duration(sleepDelay) * time.Second) + conf := a.GetAgentCheckInConfig(a.GetCheckInConfFromAPI()) if !conf.InstallDeno { return @@ -1086,16 +1104,16 @@ func (a *Agent) InstallDeno(force bool) { if conf.InstallDenoUrl != "" { url = conf.InstallDenoUrl - url = strings.Replace(url, "{OS}", runtime.GOOS, -1) - url = strings.Replace(url, "{ARCH}", runtime.GOARCH, -1) - url = strings.Replace(url, "{VERSION}", conf.InstallDenoVersion, -1) + url = strings.ReplaceAll(url, "{OS}", runtime.GOOS) + url = strings.ReplaceAll(url, "{ARCH}", runtime.GOARCH) + url = strings.ReplaceAll(url, "{VERSION}", conf.InstallDenoVersion) } else { switch runtime.GOOS { case "windows": switch runtime.GOARCH { case "amd64": // https://github.com/denoland/deno/releases/download/v1.38.2/deno-x86_64-pc-windows-msvc.zip - assetName = fmt.Sprintf("deno-x86_64-pc-windows-msvc.zip") + assetName = "deno-x86_64-pc-windows-msvc.zip" default: a.Logger.Debugln("InstallDeno(): Unsupported architecture and OS:", runtime.GOARCH, runtime.GOOS) return @@ -1108,15 +1126,21 @@ func (a *Agent) InstallDeno(force bool) { } a.Logger.Debugln("InstallDeno(): Deno download url:", url) - tmpDir, err := os.MkdirTemp("", "trmm") + err := createWinTempDir() + if err != nil { + a.Logger.Errorln("InstallDeno(): createWinTempDir:", err) + return + } + + tmpDir, err := os.MkdirTemp(a.WinTmpDir, "denotemp") if err != nil { - a.Logger.Errorln("InstallDeno(): Error creating temp directory:", err) + a.Logger.Errorln("InstallDeno(): Error creating deno temp directory:", err) return } defer func(path string) { err := os.RemoveAll(path) if err != nil { - a.Logger.Errorln("InstallDeno(): Error removing temp directory:", err) + a.Logger.Errorln("InstallDeno(): Error removing deno temp directory:", err) } }(tmpDir) From eca05c3e76517f729bed0893a7658d58c2b1b96d Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 23 Feb 2024 12:50:07 -0800 Subject: [PATCH 11/19] set user-agent header --- agent/agent.go | 3 +++ agent/utils.go | 1 + 2 files changed, 4 insertions(+) diff --git a/agent/agent.go b/agent/agent.go index 32dc341..cb061be 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -60,6 +60,7 @@ type Agent struct { PyBin string NuBin string DenoBin string + AgentHeader string Headers map[string]string Logger *logrus.Logger Version string @@ -141,6 +142,7 @@ func New(logger *logrus.Logger, version string) *Agent { ac := NewAgentConfig() + agentHeader := fmt.Sprintf("trmm/%s/%s/%s", version, runtime.GOOS, runtime.GOARCH) headers := make(map[string]string) if len(ac.Token) > 0 { headers["Content-Type"] = "application/json" @@ -255,6 +257,7 @@ func New(logger *logrus.Logger, version string) *Agent { NuBin: nuBin, DenoBin: denoBin, Headers: headers, + AgentHeader: agentHeader, Logger: logger, Version: version, Debug: logger.IsLevelEnabled(logrus.DebugLevel), diff --git a/agent/utils.go b/agent/utils.go index 7f326d8..9ef3a0c 100644 --- a/agent/utils.go +++ b/agent/utils.go @@ -92,6 +92,7 @@ func DoPing(host string) (PingResponse, error) { func (a *Agent) PublicIP() string { a.Logger.Debugln("PublicIP start") client := resty.New() + client.SetHeader("User-Agent", a.AgentHeader) client.SetTimeout(4 * time.Second) if len(a.Proxy) > 0 { client.SetProxy(a.Proxy) From 964c31e119c6c72de66921a1bd54a11477d1165c Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 15 Mar 2024 01:07:35 -0700 Subject: [PATCH 12/19] strings.Title is deprecated --- agent/agent_unix.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/agent/agent_unix.go b/agent/agent_unix.go index 0777c64..1070f0b 100644 --- a/agent/agent_unix.go +++ b/agent/agent_unix.go @@ -37,6 +37,8 @@ import ( psHost "github.com/shirou/gopsutil/v3/host" "github.com/spf13/viper" trmm "github.com/wh1te909/trmm-shared" + "golang.org/x/text/cases" + "golang.org/x/text/language" ) func ShowStatus(version string) { @@ -130,7 +132,8 @@ func (a *Agent) osString() string { if err != nil { return "error getting host info" } - return fmt.Sprintf("%s %s %s %s", strings.Title(h.Platform), h.PlatformVersion, h.KernelArch, h.KernelVersion) + plat := cases.Title(language.AmericanEnglish).String(h.Platform) + return fmt.Sprintf("%s %s %s %s", plat, h.PlatformVersion, h.KernelArch, h.KernelVersion) } func NewAgentConfig() *rmm.AgentConfig { From 57fb99996cd1aacb9ebf7490abb1b7466094a29c Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 15 Mar 2024 01:24:15 -0700 Subject: [PATCH 13/19] update reqs --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 12a5fc6..8935406 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/go-resty/resty/v2 v2.11.0 github.com/gonutz/w32/v2 v2.11.1 github.com/iamacarpet/go-win64api v0.0.0-20230324134531-ef6dbdd6db97 - github.com/nats-io/nats.go v1.32.0 + github.com/nats-io/nats.go v1.33.1 github.com/rickb777/date v1.19.1 github.com/shirou/gopsutil/v3 v3.23.12 github.com/sirupsen/logrus v1.9.3 diff --git a/go.sum b/go.sum index c720f3e..bab2ece 100644 --- a/go.sum +++ b/go.sum @@ -111,6 +111,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/nats-io/nats.go v1.32.0 h1:Bx9BZS+aXYlxW08k8Gd3yR2s73pV5XSoAQUyp1Kwvp0= github.com/nats-io/nats.go v1.32.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nats.go v1.33.1 h1:8TxLZZ/seeEfR97qV0/Bl939tpDnt2Z2fK3HkPypj70= +github.com/nats-io/nats.go v1.33.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= From 51f6e93a35274fa0002f199c8c070904446866c8 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Sun, 17 Mar 2024 08:10:09 +0000 Subject: [PATCH 14/19] deno needs .ts extension for typescript --- agent/agent_unix.go | 2 +- agent/utils.go | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/agent/agent_unix.go b/agent/agent_unix.go index 1070f0b..5033dba 100644 --- a/agent/agent_unix.go +++ b/agent/agent_unix.go @@ -173,7 +173,7 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int, code = removeWinNewLines(code) content := []byte(code) - f, err := createNixTmpFile() + f, err := createNixTmpFile(shell) if err != nil { a.Logger.Errorln("RunScript createNixTmpFile()", err) return "", err.Error(), 85, err diff --git a/agent/utils.go b/agent/utils.go index 9ef3a0c..42c8da5 100644 --- a/agent/utils.go +++ b/agent/utils.go @@ -433,14 +433,19 @@ func getCwd() (string, error) { return filepath.Dir(self), nil } -func createNixTmpFile() (*os.File, error) { +func createNixTmpFile(shell ...string) (*os.File, error) { var f *os.File cwd, err := getCwd() if err != nil { return f, err } - f, err = os.CreateTemp(cwd, "trmm") + ext := "" + if len(shell) > 0 && shell[0] == "deno" { + ext = ".ts" + } + + f, err = os.CreateTemp(cwd, fmt.Sprintf("trmm*%s", ext)) if err != nil { return f, err } From 3e1546e08bc599ecfd18442cf302b57e35dc61eb Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Sun, 24 Mar 2024 01:22:46 -0700 Subject: [PATCH 15/19] fmt --- agent/rpc.go | 2 +- agent/tasks_windows.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/agent/rpc.go b/agent/rpc.go index d65d02d..a745ed3 100644 --- a/agent/rpc.go +++ b/agent/rpc.go @@ -356,7 +356,7 @@ func (a *Agent) RunRPC() { a.CmdV2(opts) } }() - + case "rebootnow": go func() { a.Logger.Debugln("Scheduling immediate reboot") diff --git a/agent/tasks_windows.go b/agent/tasks_windows.go index f6bedbc..3ccdead 100644 --- a/agent/tasks_windows.go +++ b/agent/tasks_windows.go @@ -14,12 +14,13 @@ package agent import ( "encoding/json" "fmt" - "github.com/amidaware/taskmaster" "os" "path/filepath" "strings" "time" + "github.com/amidaware/taskmaster" + rmm "github.com/amidaware/rmmagent/shared" "github.com/rickb777/date/period" ) From c91cfcff434bfe185d2a2d431fde65467de056e6 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Sun, 24 Mar 2024 01:23:06 -0700 Subject: [PATCH 16/19] vscode settings --- .vscode/settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f094099..dbc3a61 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,14 +3,14 @@ "[go]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.organizeImports": false, + "source.organizeImports": "always" }, "editor.snippetSuggestions": "none", }, "[go.mod]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.organizeImports": true, + "source.organizeImports": "explicit" }, }, "gopls": { From 38bb9e4eddaa7c2ecdfa06108357dcca6724b456 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Sun, 24 Mar 2024 01:31:14 -0700 Subject: [PATCH 17/19] update nats --- go.mod | 4 ++-- go.sum | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8935406..829495c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/go-resty/resty/v2 v2.11.0 github.com/gonutz/w32/v2 v2.11.1 github.com/iamacarpet/go-win64api v0.0.0-20230324134531-ef6dbdd6db97 - github.com/nats-io/nats.go v1.33.1 + github.com/nats-io/nats.go v1.34.0 github.com/rickb777/date v1.19.1 github.com/shirou/gopsutil/v3 v3.23.12 github.com/sirupsen/logrus v1.9.3 @@ -31,6 +31,7 @@ require ( github.com/jaypipes/ghw v0.12.0 github.com/kardianos/service v1.2.2 github.com/spf13/viper v1.18.2 + golang.org/x/text v0.14.0 ) require ( @@ -73,7 +74,6 @@ require ( golang.org/x/crypto v0.18.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/sync v0.5.0 // indirect - golang.org/x/text v0.14.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2 // indirect diff --git a/go.sum b/go.sum index bab2ece..4087b60 100644 --- a/go.sum +++ b/go.sum @@ -109,10 +109,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/nats-io/nats.go v1.32.0 h1:Bx9BZS+aXYlxW08k8Gd3yR2s73pV5XSoAQUyp1Kwvp0= -github.com/nats-io/nats.go v1.32.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= -github.com/nats-io/nats.go v1.33.1 h1:8TxLZZ/seeEfR97qV0/Bl939tpDnt2Z2fK3HkPypj70= -github.com/nats-io/nats.go v1.33.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nats.go v1.34.0 h1:fnxnPCNiwIG5w08rlMcEKTUw4AV/nKyGCOJE8TdhSPk= +github.com/nats-io/nats.go v1.34.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= From 12f1b6dc02b563fa3f08bf0debae50fb3cde1ce0 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Sun, 24 Mar 2024 02:23:08 -0700 Subject: [PATCH 18/19] lint/deprecations --- agent/checks.go | 2 +- agent/eventlog_windows.go | 9 ++++++--- agent/services_windows.go | 5 +++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/agent/checks.go b/agent/checks.go index 5e0905d..d428527 100644 --- a/agent/checks.go +++ b/agent/checks.go @@ -260,7 +260,7 @@ func (a *Agent) EventLogCheck(data rmm.Check, r *resty.Client) { for _, i := range evtLog { if i.EventType == data.EventType { - if !data.EventIDWildcard && !(int(i.EventID) == data.EventID) { + if !data.EventIDWildcard && (int(i.EventID) != data.EventID) { continue } diff --git a/agent/eventlog_windows.go b/agent/eventlog_windows.go index f4c9fec..61c316c 100644 --- a/agent/eventlog_windows.go +++ b/agent/eventlog_windows.go @@ -158,8 +158,11 @@ func getResourceMessage(providerName, sourceName string, eventID uint32, argsptr return "", err } - handle, err := LoadLibraryEx(syscall.StringToUTF16Ptr(val), 0, - DONT_RESOLVE_DLL_REFERENCES|LOAD_LIBRARY_AS_DATAFILE) + handlePtr, err := windows.UTF16PtrFromString(val) + if err != nil { + return "", err + } + handle, err := LoadLibraryEx(handlePtr, 0, DONT_RESOLVE_DLL_REFERENCES|LOAD_LIBRARY_AS_DATAFILE) if err != nil { return "", err } @@ -180,7 +183,7 @@ func getResourceMessage(providerName, sourceName string, eventID uint32, argsptr return "", err } message, _ := bytesToString(msgbuf[:numChars*2]) - message = strings.Replace(message, "\r", "", -1) + message = strings.ReplaceAll(message, "\r", "") message = strings.TrimSuffix(message, "\n") return message, nil } diff --git a/agent/services_windows.go b/agent/services_windows.go index e782021..7337f77 100644 --- a/agent/services_windows.go +++ b/agent/services_windows.go @@ -119,9 +119,10 @@ func (a *Agent) EditService(name, startupType string) rmm.WinSvcResp { } conf.StartType = startType - if startupType == "autodelay" { + switch startupType { + case "autodelay": conf.DelayedAutoStart = true - } else if startupType == "auto" { + case "auto": conf.DelayedAutoStart = false } From b09eaf84de68dfb4f3cb6db6ff364a6745efd943 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Sun, 24 Mar 2024 12:06:21 -0700 Subject: [PATCH 19/19] bump version --- build/rmm.exe.manifest | 2 +- build/setup.iss | 4 +++- main.go | 2 +- versioninfo.json | 14 +++++++------- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/build/rmm.exe.manifest b/build/rmm.exe.manifest index c375d11..805a958 100644 --- a/build/rmm.exe.manifest +++ b/build/rmm.exe.manifest @@ -3,7 +3,7 @@ diff --git a/build/setup.iss b/build/setup.iss index 6b9ccf2..60b9159 100644 --- a/build/setup.iss +++ b/build/setup.iss @@ -1,5 +1,5 @@ #define MyAppName "Tactical RMM Agent" -#define MyAppVersion "2.6.2" +#define MyAppVersion "2.7.0" #define MyAppPublisher "AmidaWare Inc" #define MyAppURL "https://github.com/amidaware" #define MyAppExeName "tacticalrmm.exe" @@ -28,6 +28,8 @@ WizardStyle=modern RestartApplications=no CloseApplications=no MinVersion=6.0 +VersionInfoVersion=1.0.0.0 +AppCopyright="Copyright (C) 2024 {#MyAppPublisher}" [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" diff --git a/main.go b/main.go index 3f8fa65..4167140 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ import ( ) var ( - version = "2.6.2" + version = "2.7.0" log = logrus.New() logFile *os.File ) diff --git a/versioninfo.json b/versioninfo.json index 354a9d5..64f85cf 100644 --- a/versioninfo.json +++ b/versioninfo.json @@ -2,14 +2,14 @@ "FixedFileInfo": { "FileVersion": { "Major": 2, - "Minor": 6, - "Patch": 2, + "Minor": 7, + "Patch": 0, "Build": 0 }, "ProductVersion": { "Major": 2, - "Minor": 6, - "Patch": 2, + "Minor": 7, + "Patch": 0, "Build": 0 }, "FileFlagsMask": "3f", @@ -22,14 +22,14 @@ "Comments": "", "CompanyName": "AmidaWare Inc", "FileDescription": "Tactical RMM Agent", - "FileVersion": "v2.6.2.0", + "FileVersion": "v2.7.0.0", "InternalName": "tacticalrmm.exe", - "LegalCopyright": "Copyright (c) 2023 AmidaWare Inc", + "LegalCopyright": "Copyright (c) 2024 AmidaWare Inc", "LegalTrademarks": "", "OriginalFilename": "tacticalrmm.exe", "PrivateBuild": "", "ProductName": "Tactical RMM Agent", - "ProductVersion": "v2.6.2.0", + "ProductVersion": "v2.7.0.0", "SpecialBuild": "" }, "VarFileInfo": {