26 Commits

Author SHA1 Message Date
wh1te909
697e112536 posix tasks 2025-01-24 23:31:15 +00:00
wh1te909
687e890a10 bump version 2024-07-08 16:10:55 -07:00
wh1te909
c64fc857c9 update cmd 2024-06-30 09:40:14 -07:00
wh1te909
5fef58f764 update reqs 2024-06-23 13:56:34 -07:00
wh1te909
ecad4d02d9 update bundled python version on windows >= 8.1 to 3.11.9 2024-06-23 13:56:01 -07:00
wh1te909
b09eaf84de bump version 2024-03-24 12:06:21 -07:00
wh1te909
12f1b6dc02 lint/deprecations 2024-03-24 02:23:08 -07:00
wh1te909
38bb9e4edd update nats 2024-03-24 01:31:14 -07:00
wh1te909
c91cfcff43 vscode settings 2024-03-24 01:23:06 -07:00
wh1te909
3e1546e08b fmt 2024-03-24 01:22:46 -07:00
wh1te909
51f6e93a35 deno needs .ts extension for typescript 2024-03-17 08:10:09 +00:00
wh1te909
57fb99996c update reqs 2024-03-15 01:24:15 -07:00
wh1te909
964c31e119 strings.Title is deprecated 2024-03-15 01:07:35 -07:00
wh1te909
eca05c3e76 set user-agent header 2024-02-23 12:50:07 -08:00
wh1te909
98cae02ec6 tweaks to the install bin funcs 2024-02-22 22:42:35 -08:00
wh1te909
996631e108 do not block 2024-02-22 22:29:39 -08:00
wh1te909
47b482419a fixes to tar 2024-02-22 22:23:43 -08:00
Dan
67abc85168 Merge pull request #44 from conlan0/develop
Add agent shutdown command
2024-02-22 14:32:19 -08:00
wh1te909
a29709c599 use filepath 2024-02-22 11:36:45 -08:00
wh1te909
e40dcf6079 use filepath 2024-02-22 04:32:12 +00:00
Dan
dc10c33be8 Merge pull request #38 from NiceGuyIT/feature/cross-platform-scripting
[Feature] Add cross site scripting
2024-02-21 20:28:07 -08:00
conlan0
d3eb6e041b update macos/linux command 2024-02-21 21:12:18 -05:00
conlan0
055f11b0e3 Add shutdown command 2024-02-21 20:34:14 -05:00
David Randall
2afdfd7ab8 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.
2023-12-03 23:14:27 -05:00
David Randall
87e1b29ef6 Add: Download the nu and deno binaries from GitHub
Signed-off-by: David Randall <David@NiceGuyIT.biz>
2023-11-18 20:03:29 -05:00
David Randall
6ba3272dc0 [Feature] Add cross site scripting 2023-11-12 13:24:24 -05:00
21 changed files with 1216 additions and 259 deletions

View File

@@ -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": {

View File

@@ -15,6 +15,7 @@ import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"math"
@@ -58,6 +59,12 @@ type Agent struct {
MeshSystemEXE string
MeshSVC string
PyBin string
PyVer string
PyBaseDir string
PyDir string
NuBin string
DenoBin string
AgentHeader string
Headers map[string]string
Logger *logrus.Logger
Version string
@@ -86,6 +93,8 @@ const (
nixAgentDir = "/opt/tacticalagent"
nixMeshDir = "/opt/tacticalmesh"
nixAgentBin = nixAgentDir + "/tacticalagent"
nixAgentBinDir = nixAgentDir + "/bin"
nixAgentEtcDir = nixAgentDir + "/etc"
nixMeshAgentBin = nixMeshDir + "/meshagent"
macPlistPath = "/Library/LaunchDaemons/tacticalagent.plist"
macPlistName = "tacticalagent"
@@ -111,16 +120,45 @@ func New(logger *logrus.Logger, version string) *Agent {
hostname = info.Hostname
}
var pybin string
switch runtime.GOARCH {
case "amd64":
pybin = filepath.Join(pd, "py38-x64", "python.exe")
case "386":
pybin = filepath.Join(pd, "py38-x32", "python.exe")
pyver := "n/a"
pybin := "n/a"
pyBaseDir := "n/a"
pydir := "n/a"
if runtime.GOOS == "windows" {
major := info.OS.Major
minor := info.OS.Minor
if major > 6 || (major == 6 && minor >= 3) {
// Windows 8.1 or higher
pyver = "3.11.9"
} else {
pyver = "3.8.7"
}
pydir = "py" + pyver + "_" + runtime.GOARCH
pyBaseDir = filepath.Join(pd, "python")
pybin = filepath.Join(pyBaseDir, pydir, "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()
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"
@@ -232,7 +270,13 @@ func New(logger *logrus.Logger, version string) *Agent {
MeshSystemEXE: MeshSysExe,
MeshSVC: meshSvcName,
PyBin: pybin,
PyVer: pyver,
PyBaseDir: pyBaseDir,
PyDir: pydir,
NuBin: nuBin,
DenoBin: denoBin,
Headers: headers,
AgentHeader: agentHeader,
Logger: logger,
Version: version,
Debug: logger.IsLevelEnabled(logrus.DebugLevel),
@@ -603,3 +647,124 @@ func createWinTempDir() error {
}
return nil
}
func (a *Agent) RunTask(id int) error {
data := rmm.AutomatedTask{}
url := fmt.Sprintf("/api/v3/%d/%s/taskrunner/", id, a.AgentID)
r1, gerr := a.rClient.R().Get(url)
if gerr != nil {
a.Logger.Debugln(gerr)
return gerr
}
if r1.IsError() {
a.Logger.Debugln("Run Task:", r1.String())
return nil
}
if err := json.Unmarshal(r1.Body(), &data); err != nil {
a.Logger.Debugln(err)
return err
}
start := time.Now()
type TaskResult struct {
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
RetCode int `json:"retcode"`
ExecTime float64 `json:"execution_time"`
}
payload := TaskResult{}
// loop through all task actions
for _, action := range data.TaskActions {
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, action.NushellEnableConfig, action.DenoDefaultPermissions)
if err != nil {
a.Logger.Debugln(err)
}
// add text to stdout showing which script ran if more than 1 script
action_exec_time := time.Since(action_start).Seconds()
if len(data.TaskActions) > 1 {
payload.Stdout += fmt.Sprintf("\n------------\nRunning Script: %s. Execution Time: %f\n------------\n\n", action.ScriptName, action_exec_time)
}
// save results
payload.Stdout += stdout
payload.Stderr += stderr
payload.RetCode = retcode
if !data.ContinueOnError && stderr != "" {
break
}
} else if action.ActionType == "cmd" {
var stdout, stderr string
switch runtime.GOOS {
case "windows":
out, err := CMDShell(action.Shell, []string{}, action.Command, action.Timeout, false, action.RunAsUser)
if err != nil {
a.Logger.Debugln(err)
}
stdout = out[0]
stderr = out[1]
if stderr == "" {
payload.RetCode = 0
} else {
payload.RetCode = 1
}
default:
opts := a.NewCMDOpts()
opts.Shell = action.Shell
opts.Command = action.Command
opts.Timeout = time.Duration(action.Timeout)
out := a.CmdV2(opts)
if out.Status.Error != nil {
a.Logger.Debugln("RunTask() cmd: ", out.Status.Error.Error())
}
stdout = out.Stdout
stderr = out.Stderr
payload.RetCode = out.Status.Exit
}
if len(data.TaskActions) > 1 {
action_exec_time := time.Since(action_start).Seconds()
// add text to stdout showing which script ran
payload.Stdout += fmt.Sprintf("\n------------\nRunning Command: %s. Execution Time: %f\n------------\n\n", action.Command, action_exec_time)
}
// save results
payload.Stdout += stdout
payload.Stderr += stderr
if payload.RetCode != 0 {
if !data.ContinueOnError {
break
}
}
} else {
a.Logger.Debugln("Invalid Action", action)
}
}
payload.ExecTime = time.Since(start).Seconds()
_, perr := a.rClient.R().SetBody(payload).Patch(url)
if perr != nil {
a.Logger.Debugln(perr)
return perr
}
return nil
}

View File

@@ -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 {
@@ -166,11 +169,11 @@ 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)
f, err := createNixTmpFile()
f, err := createNixTmpFile(shell)
if err != nil {
a.Logger.Errorln("RunScript createNixTmpFile()", err)
return "", err.Error(), 85, err
@@ -194,10 +197,75 @@ 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":
var nushellArgs []string
if nushellEnableConfig {
nushellArgs = []string{
"--config",
filepath.Join(nixAgentEtcDir, "nushell", "config.nu"),
"--env-config",
filepath.Join(nixAgentEtcDir, "nushell", "env.nu"),
}
} else {
nushellArgs = []string{"--no-config-file"}
}
opts.Shell = a.NuBin
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)
return "", err.Error(), 85, err
}
case "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
// 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]
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:]...)
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
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 {
@@ -510,11 +578,329 @@ func (a *Agent) GetWMIInfo() map[string]interface{} {
return wmiInfo
}
// windows only below TODO add into stub file
// 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
}
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("InstallNushell(): Error removing nu binary:", err)
return
}
} else {
return
}
}
if !trmm.FileExists(nixAgentBinDir) {
err := os.MkdirAll(nixAgentBinDir, 0755)
if err != nil {
a.Logger.Errorln("InstallNushell(): Error creating nixAgentBinDir:", err)
return
}
}
if conf.NushellEnableConfig {
// Create 0-byte config files for Nushell
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 {
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
)
if conf.InstallNushellUrl != "" {
url = conf.InstallNushellUrl
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":
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("InstallNushell(): Unsupported OS:", runtime.GOOS)
return
}
url = fmt.Sprintf("https://github.com/nushell/nushell/releases/download/%s/%s", conf.InstallNushellVersion, assetName)
}
a.Logger.Debugln("InstallNushell(): Nu download url:", url)
tmpDir, err := os.MkdirTemp("", "nutemp")
if err != nil {
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 nushell temp directory:", err)
}
}(tmpDir)
tmpAssetName := filepath.Join(tmpDir, assetName)
a.Logger.Debugln("InstallNushell(): 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("InstallNushell(): Unable to download nu:", err)
return
}
if r.IsError() {
a.Logger.Errorln("InstallNushell(): Unable to download nu. Status code:", r.StatusCode())
return
}
if conf.InstallNushellUrl != "" {
// InstallNushellUrl is not compressed.
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
}
} 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(filepath.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("InstallNushell(): Failed to chmod nu binary:", err)
return
}
}
// 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
}
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("InstallDeno(): Error removing deno binary:", err)
return
}
} else {
return
}
}
if !trmm.FileExists(nixAgentBinDir) {
err := os.MkdirAll(nixAgentBinDir, 0755)
if err != nil {
a.Logger.Errorln("InstallDeno(): Error creating nixAgentBinDir:", err)
return
}
}
var (
assetName string
url string
)
if conf.InstallDenoUrl != "" {
url = conf.InstallDenoUrl
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 = "deno-aarch64-apple-darwin.zip"
case "amd64":
// https://github.com/denoland/deno/releases/download/v1.38.2/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
}
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 = "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("InstallDeno(): Unsupported OS:", runtime.GOOS)
return
}
url = fmt.Sprintf("https://github.com/denoland/deno/releases/download/%s/%s", conf.InstallDenoVersion, assetName)
}
a.Logger.Debugln("InstallDeno(): Deno download url:", url)
tmpDir, err := os.MkdirTemp("", "denotemp")
if err != nil {
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 deno temp directory:", err)
}
}(tmpDir)
tmpAssetName := filepath.Join(tmpDir, assetName)
a.Logger.Debugln("InstallDeno(): 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("InstallDeno(): Unable to download deno:", err)
return
}
if r.IsError() {
a.Logger.Errorln("InstallDeno(): Unable to download deno. Status code:", r.StatusCode())
return
}
if conf.InstallDenoUrl != "" {
// InstallDenoUrl is not compressed.
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
}
} 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(filepath.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("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() {}
@@ -555,8 +941,6 @@ func (a *Agent) GetInstalledSoftware() []trmm.WinSoftwareList { return []trmm.Wi
func (a *Agent) ChecksRunning() bool { return false }
func (a *Agent) RunTask(id int) error { return nil }
func (a *Agent) InstallChoco() {}
func (a *Agent) InstallWithChoco(name string) (string, error) { return "", nil }

View File

@@ -91,7 +91,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)
@@ -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,56 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int,
cmdArgs = []string{tmpfn.Name()}
case "cmd":
exe = tmpfn.Name()
case "nushell":
exe = a.NuBin
var nushellArgs []string
if nushellEnableConfig {
nushellArgs = []string{
"--config",
filepath.Join(a.ProgramDir, "etc", "nushell", "config.nu"),
"--env-config",
filepath.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)
return "", err.Error(), 85, err
}
case "deno":
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
// 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]
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:]...)
found = true
break
}
}
if !found && denoDefaultPermissions != "" {
cmdArgs = append(cmdArgs, strings.Split(denoDefaultPermissions, " ")...)
}
cmdArgs = append(cmdArgs, tmpfn.Name())
}
if len(args) > 0 {
@@ -698,6 +752,15 @@ func (a *Agent) AgentUninstall(code string) {
// RunMigrations cleans up unused stuff from older agents
func (a *Agent) RunMigrations() {
// changed pybin dirs in v2.8.0
for _, i := range []string{"py38-x64", "py38-x32"} {
py := filepath.Join(a.ProgramDir, i)
if trmm.FileExists(py) {
os.RemoveAll(py)
}
}
for _, i := range []string{"nssm.exe", "nssm-x86.exe"} {
nssm := filepath.Join(a.ProgramDir, i)
if trmm.FileExists(nssm) {
@@ -791,26 +854,23 @@ func (a *Agent) GetPython(force bool) {
return
}
var archZip string
var folder string
switch runtime.GOARCH {
case "amd64":
archZip = "py38-x64.zip"
folder = "py38-x64"
case "386":
archZip = "py38-x32.zip"
folder = "py38-x32"
}
pyFolder := filepath.Join(a.ProgramDir, folder)
pyZip := filepath.Join(a.ProgramDir, archZip)
a.Logger.Debugln(pyZip)
a.Logger.Debugln(a.PyBin)
defer os.Remove(pyZip)
if force {
os.RemoveAll(pyFolder)
os.RemoveAll(a.PyBaseDir)
}
sleepDelay := randRange(1, 10)
a.Logger.Debugf("GetPython() sleeping for %v seconds\n", sleepDelay)
time.Sleep(time.Duration(sleepDelay) * time.Second)
if !trmm.FileExists(a.PyBaseDir) {
os.MkdirAll(a.PyBaseDir, 0775)
}
archZip := a.PyDir + ".zip"
pyZip := filepath.Join(a.PyBaseDir, archZip)
defer os.Remove(pyZip)
rClient := resty.New()
rClient.SetTimeout(20 * time.Minute)
rClient.SetRetryCount(10)
@@ -820,7 +880,7 @@ func (a *Agent) GetPython(force bool) {
rClient.SetProxy(a.Proxy)
}
url := fmt.Sprintf("https://github.com/amidaware/rmmagent/releases/download/v2.0.0/%s", archZip)
url := fmt.Sprintf("https://github.com/amidaware/rmmagent/releases/download/v2.8.0/%s", archZip)
a.Logger.Debugln(url)
r, err := rClient.R().SetOutput(pyZip).Get(url)
if err != nil {
@@ -832,12 +892,306 @@ func (a *Agent) GetPython(force bool) {
return
}
err = Unzip(pyZip, a.ProgramDir)
err = Unzip(pyZip, a.PyBaseDir)
if err != nil {
a.Logger.Errorln(err)
}
}
// 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
}
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("InstallNushell(): Error removing nu.exe binary:", err)
return
}
} else {
return
}
}
programBinDir := filepath.Join(a.ProgramDir, "bin")
if !trmm.FileExists(programBinDir) {
err := os.MkdirAll(programBinDir, 0755)
if err != nil {
a.Logger.Errorln("InstallNushell(): Error creating Program Files bin folder:", err)
return
}
}
if conf.NushellEnableConfig {
// Create 0-byte config files for Nushell
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 {
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
)
if conf.InstallNushellUrl != "" {
url = conf.InstallNushellUrl
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":
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("InstallNushell(): Unsupported OS:", runtime.GOOS)
return
}
url = fmt.Sprintf("https://github.com/nushell/nushell/releases/download/%s/%s", conf.InstallNushellVersion, assetName)
}
a.Logger.Debugln("InstallNushell(): Nu download url:", url)
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 nushell temp directory:", err)
return
}
defer func(path string) {
err := os.RemoveAll(path)
if err != nil {
a.Logger.Errorln("InstallNushell(): Error removing nushell temp directory:", err)
}
}(tmpDir)
tmpAssetName := filepath.Join(tmpDir, assetName)
a.Logger.Debugln("InstallNushell(): 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("InstallNushell(): Unable to download nu:", err)
return
}
if r.IsError() {
a.Logger.Errorln("InstallNushell(): Unable to download nu. Status code:", r.StatusCode())
return
}
if conf.InstallNushellUrl != "" {
// InstallNushellUrl is not compressed.
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
}
} else {
err = Unzip(tmpAssetName, tmpDir)
if err != nil {
a.Logger.Errorln("InstallNushell(): Failed to unzip downloaded zip file:", err)
return
}
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
}
}
}
// 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
}
if trmm.FileExists(a.DenoBin) {
if force {
err := os.Remove(a.DenoBin)
if err != nil {
a.Logger.Errorln("InstallDeno(): Error removing deno binary:", err)
return
}
} else {
return
}
}
programBinDir := filepath.Join(a.ProgramDir, "bin")
if !trmm.FileExists(programBinDir) {
err := os.MkdirAll(programBinDir, 0755)
if err != nil {
a.Logger.Errorln("InstallDeno(): Error creating Program Files bin folder:", err)
return
}
}
var (
assetName string
url string
)
if conf.InstallDenoUrl != "" {
url = conf.InstallDenoUrl
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 = "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("InstallDeno(): Unsupported OS:", runtime.GOOS)
return
}
url = fmt.Sprintf("https://github.com/denoland/deno/releases/download/%s/%s", conf.InstallDenoVersion, assetName)
}
a.Logger.Debugln("InstallDeno(): Deno download url:", url)
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 deno temp directory:", err)
return
}
defer func(path string) {
err := os.RemoveAll(path)
if err != nil {
a.Logger.Errorln("InstallDeno(): Error removing deno temp directory:", err)
}
}(tmpDir)
tmpAssetName := filepath.Join(tmpDir, assetName)
a.Logger.Debugln("InstallDeno(): 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("InstallDeno(): Unable to download deno:", err)
return
}
if r.IsError() {
a.Logger.Errorln("InstallDeno(): Unable to download deno. Status code:", r.StatusCode())
return
}
if conf.InstallDenoUrl != "" {
// InstallDenoUrl is not compressed.
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
}
} 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(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
}
}
}
func (a *Agent) RecoverMesh() {
a.Logger.Infoln("Attempting mesh recovery")
defer CMD("net", []string{"start", a.MeshSVC}, 60, false)

View File

@@ -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,
@@ -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
}

View File

@@ -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)

View File

@@ -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, "")
}

View File

@@ -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
}

View File

@@ -258,8 +258,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 +305,8 @@ func (a *Agent) Install(i *Installer) {
}
if runtime.GOOS == "windows" {
os.MkdirAll(filepath.Join(a.ProgramDir, "bin"), 0755)
// send software api
a.SendSoftware()
@@ -341,7 +348,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 {

View File

@@ -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 {
@@ -339,6 +341,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 -h now"
a.CmdV2(opts)
}
}()
case "rebootnow":
go func() {
a.Logger.Debugln("Scheduling immediate reboot")
@@ -437,6 +455,10 @@ func (a *Agent) RunRPC() {
}()
case "installpython":
go a.GetPython(true)
case "installnushell":
go a.InstallNushell(true)
case "installdeno":
go a.InstallDeno(true)
case "installchoco":
go a.InstallChoco()
case "installwithchoco":

View File

@@ -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
}

View File

@@ -29,18 +29,28 @@ 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) {
a.RunMigrations()
if runtime.GOOS == "windows" {
go a.GetPython(false)
@@ -49,7 +59,6 @@ func (a *Agent) AgentSvc(nc *nats.Conn) {
a.Logger.Errorln("AgentSvc() createWinTempDir():", err)
}
}
a.RunMigrations()
sleepDelay := randRange(7, 25)
a.Logger.Debugf("AgentSvc() sleeping for %v seconds", sleepDelay)
@@ -61,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
@@ -72,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 {
go a.InstallNushell(false)
}
if conf.InstallDeno {
go a.InstallDeno(false)
}
go a.SyncMeshNodeID()
time.Sleep(time.Duration(randRange(1, 3)) * time.Second)
@@ -139,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
@@ -149,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
}

View File

@@ -12,119 +12,16 @@ https://license.tacticalrmm.com
package agent
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"time"
rmm "github.com/amidaware/rmmagent/shared"
"github.com/amidaware/taskmaster"
"github.com/rickb777/date/period"
)
func (a *Agent) RunTask(id int) error {
data := rmm.AutomatedTask{}
url := fmt.Sprintf("/api/v3/%d/%s/taskrunner/", id, a.AgentID)
r1, gerr := a.rClient.R().Get(url)
if gerr != nil {
a.Logger.Debugln(gerr)
return gerr
}
if r1.IsError() {
a.Logger.Debugln("Run Task:", r1.String())
return nil
}
if err := json.Unmarshal(r1.Body(), &data); err != nil {
a.Logger.Debugln(err)
return err
}
start := time.Now()
type TaskResult struct {
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
RetCode int `json:"retcode"`
ExecTime float64 `json:"execution_time"`
}
payload := TaskResult{}
// loop through all task actions
for _, action := range data.TaskActions {
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)
if err != nil {
a.Logger.Debugln(err)
}
// add text to stdout showing which script ran if more than 1 script
action_exec_time := time.Since(action_start).Seconds()
if len(data.TaskActions) > 1 {
payload.Stdout += fmt.Sprintf("\n------------\nRunning Script: %s. Execution Time: %f\n------------\n\n", action.ScriptName, action_exec_time)
}
// save results
payload.Stdout += stdout
payload.Stderr += stderr
payload.RetCode = retcode
if !data.ContinueOnError && stderr != "" {
break
}
} else if action.ActionType == "cmd" {
// out[0] == stdout, out[1] == stderr
out, err := CMDShell(action.Shell, []string{}, action.Command, action.Timeout, false, action.RunAsUser)
if err != nil {
a.Logger.Debugln(err)
}
if len(data.TaskActions) > 1 {
action_exec_time := time.Since(action_start).Seconds()
// add text to stdout showing which script ran
payload.Stdout += fmt.Sprintf("\n------------\nRunning Command: %s. Execution Time: %f\n------------\n\n", action.Command, action_exec_time)
}
// save results
payload.Stdout += out[0]
payload.Stderr += out[1]
// no error
if out[1] == "" {
payload.RetCode = 0
} else {
payload.RetCode = 1
if !data.ContinueOnError {
break
}
}
} else {
a.Logger.Debugln("Invalid Action", action)
}
}
payload.ExecTime = time.Since(start).Seconds()
_, perr := a.rClient.R().SetBody(payload).Patch(url)
if perr != nil {
a.Logger.Debugln(perr)
return perr
}
return nil
}
type SchedTask struct {
PK int `json:"pk"`
Type string `json:"type"`

View File

@@ -12,8 +12,11 @@ https://license.tacticalrmm.com
package agent
import (
"archive/tar"
"archive/zip"
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"math"
@@ -89,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)
@@ -280,6 +284,73 @@ 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
}
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 {
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(filepath.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(filepath.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:
errMsg := fmt.Sprintf("ExtractTarGz(): Unknown type: %v in %s", header.Typeflag, header.Name)
a.Logger.Errorln(errMsg)
return "", errors.New(errMsg)
}
}
return extractedDir, nil
}
// https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format/
func ByteCountSI(b uint64) string {
const unit = 1024
@@ -362,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
}

View File

@@ -3,7 +3,7 @@
<assemblyIdentity
type="win32"
name="TacticalRMM"
version="2.6.2.0"
version="2.8.0.0"
processorArchitecture="*"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>

View File

@@ -1,5 +1,5 @@
#define MyAppName "Tactical RMM Agent"
#define MyAppVersion "2.6.2"
#define MyAppVersion "2.8.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"

20
go.mod
View File

@@ -7,30 +7,31 @@ require (
github.com/elastic/go-sysinfo v1.11.2
github.com/go-ole/go-ole v1.3.0
github.com/go-ping/ping v1.1.0
github.com/go-resty/resty/v2 v2.11.0
github.com/go-resty/resty/v2 v2.13.1
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.36.0
github.com/rickb777/date v1.19.1
github.com/shirou/gopsutil/v3 v3.23.12
github.com/sirupsen/logrus v1.9.3
github.com/ugorji/go/codec v1.2.12
github.com/wh1te909/go-win64api v0.0.0-20230802051600-21b24f62e846
github.com/wh1te909/trmm-shared v0.0.0-20220227075846-f9f757361139
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.16.0
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0
)
require (
github.com/amidaware/taskmaster v0.0.0-20220111015025-c9cd178bbbf2
github.com/go-cmd/cmd v1.4.2
github.com/go-cmd/cmd v1.4.3
)
require (
github.com/fourcorelabs/wintoken v1.0.0
github.com/jaypipes/ghw v0.12.0
github.com/kardianos/service v1.2.2
github.com/spf13/viper v1.18.2
github.com/spf13/viper v1.19.0
golang.org/x/text v0.15.0
)
require (
@@ -51,7 +52,7 @@ require (
github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
github.com/prometheus/procfs v0.8.0 // indirect
@@ -70,10 +71,9 @@ require (
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/crypto v0.23.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
golang.org/x/sync v0.6.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

55
go.sum
View File

@@ -28,8 +28,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-cmd/cmd v1.4.2 h1:pnX38iIJHh4huzBSqfkAZkfXrVwM/5EccAJmrVqMnbg=
github.com/go-cmd/cmd v1.4.2/go.mod h1:u3hxg/ry+D5kwh8WvUkHLAMe2zQCaXd00t35WfQaOFk=
github.com/go-cmd/cmd v1.4.3 h1:6y3G+3UqPerXvPcXvj+5QNPHT02BUw7p6PsqRxLNA7Y=
github.com/go-cmd/cmd v1.4.3/go.mod h1:u3hxg/ry+D5kwh8WvUkHLAMe2zQCaXd00t35WfQaOFk=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
@@ -37,8 +37,8 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -109,8 +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.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
github.com/nats-io/nats.go v1.36.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=
@@ -125,8 +125,8 @@ github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -167,17 +167,19 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
@@ -200,9 +202,9 @@ go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTV
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -219,16 +221,16 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -257,26 +259,27 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

View File

@@ -25,7 +25,7 @@ import (
)
var (
version = "2.6.2"
version = "2.8.0"
log = logrus.New()
logFile *os.File
)
@@ -117,6 +117,10 @@ func main() {
fmt.Println(a.PublicIP())
case "getpython":
a.GetPython(true)
case "installdeno":
a.InstallDeno(true)
case "installnushell":
a.InstallNushell(true)
case "runmigrations":
a.RunMigrations()
case "recovermesh":

View File

@@ -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 {

View File

@@ -2,14 +2,14 @@
"FixedFileInfo": {
"FileVersion": {
"Major": 2,
"Minor": 6,
"Patch": 2,
"Minor": 8,
"Patch": 0,
"Build": 0
},
"ProductVersion": {
"Major": 2,
"Minor": 6,
"Patch": 2,
"Minor": 8,
"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.8.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.8.0.0",
"SpecialBuild": ""
},
"VarFileInfo": {