diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3c4f05a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: Run tests + +on: + push: + branches: + - "*" + pull_request: + branches: + - "*" + +jobs: + build: + runs-on: ubuntu-latest + name: Run tests + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-go@v3 + with: + go-version: '1.18.3' + + - name: Ensure linux agent compiles + run: | + ARCHS='amd64 386 arm64 arm' + for i in ${ARCHS}; do + env CGO_ENABLED=0 GOOS=linux GOARCH=${i} go build -ldflags "-s -w" + done + + - name: Ensure windows agent compiles + run: | + ARCHS='amd64 386' + for i in ${ARCHS}; do + env CGO_ENABLED=0 GOOS=windows GOARCH=${i} go build -ldflags "-s -w" + done + + - name: Ensure mac agent compiles + run: | + ARCHS='amd64 arm64' + for i in ${ARCHS}; do + env CGO_ENABLED=0 GOOS=darwin GOARCH=${i} go build -ldflags "-s -w" + done + + - name: Ensure freebsd agent compiles + run: | + ARCHS='amd64 386 arm64 arm' + for i in ${ARCHS}; do + env CGO_ENABLED=0 GOOS=freebsd GOARCH=${i} go build -ldflags "-s -w" + done diff --git a/agent/agent.go b/agent/agent.go index 567d56a..2e465e0 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -66,6 +66,9 @@ type Agent struct { Platform string GoArch string ServiceConfig *service.Config + NatsServer string + NatsProxyPath string + NatsProxyPort string } const ( @@ -139,9 +142,25 @@ func New(logger *logrus.Logger, version string) *Agent { }, } + var natsProxyPath, natsProxyPort string + if ac.NatsProxyPath == "" { + natsProxyPath = "natsws" + } + + if ac.NatsProxyPort == "" { + natsProxyPort = "443" + } + + // check if using nats standard tcp, otherwise use nats websockets by default + var natsServer string + if ac.NatsStandardPort != "" { + natsServer = fmt.Sprintf("tls://%s:%s", ac.APIURL, ac.NatsStandardPort) + } else { + natsServer = fmt.Sprintf("wss://%s:%s", ac.APIURL, natsProxyPort) + } + return &Agent{ Hostname: info.Hostname, - Arch: info.Architecture, BaseURL: ac.BaseURL, AgentID: ac.AgentID, ApiURL: ac.APIURL, @@ -164,6 +183,9 @@ func New(logger *logrus.Logger, version string) *Agent { Platform: runtime.GOOS, GoArch: runtime.GOARCH, ServiceConfig: svcConf, + NatsServer: natsServer, + NatsProxyPath: natsProxyPath, + NatsProxyPort: natsProxyPort, } } @@ -360,6 +382,7 @@ func (a *Agent) setupNatsOptions() []nats.Option { opts = append(opts, nats.RetryOnFailedConnect(true)) opts = append(opts, nats.MaxReconnects(-1)) opts = append(opts, nats.ReconnectBufSize(-1)) + opts = append(opts, nats.ProxyPath(a.NatsProxyPath)) return opts } @@ -385,6 +408,7 @@ func (a *Agent) CleanupAgentUpdates() { return } + // winagent-v* is deprecated files, err := filepath.Glob("winagent-v*.exe") if err == nil { for _, f := range files { @@ -392,6 +416,13 @@ func (a *Agent) CleanupAgentUpdates() { } } + agents, err := filepath.Glob("tacticalagent-v*.exe") + if err == nil { + for _, f := range agents { + os.Remove(f) + } + } + cderr = os.Chdir(os.Getenv("TMP")) if cderr != nil { a.Logger.Errorln(cderr) diff --git a/agent/agent_linux.go b/agent/agent_unix.go similarity index 89% rename from agent/agent_linux.go rename to agent/agent_unix.go index 1027344..037b112 100644 --- a/agent/agent_linux.go +++ b/agent/agent_unix.go @@ -1,3 +1,6 @@ +//go:build !windows +// +build !windows + /* Copyright 2022 AmidaWare LLC. @@ -15,6 +18,7 @@ import ( "bufio" "fmt" "os" + "path/filepath" "runtime" "strconv" "strings" @@ -142,15 +146,18 @@ func NewAgentConfig() *rmm.AgentConfig { pk, _ := strconv.Atoi(agentpk) ret := &rmm.AgentConfig{ - BaseURL: viper.GetString("baseurl"), - AgentID: viper.GetString("agentid"), - APIURL: viper.GetString("apiurl"), - Token: viper.GetString("token"), - AgentPK: agentpk, - PK: pk, - Cert: viper.GetString("cert"), - Proxy: viper.GetString("proxy"), - CustomMeshDir: viper.GetString("meshdir"), + BaseURL: viper.GetString("baseurl"), + AgentID: viper.GetString("agentid"), + APIURL: viper.GetString("apiurl"), + Token: viper.GetString("token"), + AgentPK: agentpk, + PK: pk, + Cert: viper.GetString("cert"), + Proxy: viper.GetString("proxy"), + CustomMeshDir: viper.GetString("meshdir"), + NatsProxyPath: viper.GetString("natsproxypath"), + NatsProxyPort: viper.GetString("natsproxyport"), + NatsStandardPort: viper.GetString("natsstandardport"), } return ret } @@ -244,8 +251,29 @@ func (a *Agent) AgentUpdate(url, inno, version string) { os.Chmod(f.Name(), 0755) err = os.Rename(f.Name(), self) if err != nil { - a.Logger.Errorln("AgentUpdate() os.Rename():", err) - return + a.Logger.Debugln("Detected /tmp on different filesystem") + // rename does not work when src and dest are on different filesystems + // so we need to manually copy it to the same fs then rename it + cwd, err := os.Getwd() + if err != nil { + a.Logger.Errorln("AgentUpdate() os.Getwd():", err) + return + } + // create a tmpfile in same fs as agent + tmpfile := filepath.Join(cwd, GenerateAgentID()) + defer os.Remove(tmpfile) + a.Logger.Debugln("Copying tmpfile from", f.Name(), "to", tmpfile) + cperr := copyFile(f.Name(), tmpfile) + if cperr != nil { + a.Logger.Errorln("AgentUpdate() copyFile:", cperr) + return + } + os.Chmod(tmpfile, 0755) + rerr := os.Rename(tmpfile, self) + if rerr != nil { + a.Logger.Errorln("AgentUpdate() os.Rename():", rerr) + return + } } opts := a.NewCMDOpts() diff --git a/agent/agent_windows.go b/agent/agent_windows.go index 0912cd2..70367c7 100644 --- a/agent/agent_windows.go +++ b/agent/agent_windows.go @@ -61,17 +61,23 @@ func NewAgentConfig() *rmm.AgentConfig { cert, _, _ := k.GetStringValue("Cert") proxy, _, _ := k.GetStringValue("Proxy") customMeshDir, _, _ := k.GetStringValue("MeshDir") + natsProxyPath, _, _ := k.GetStringValue("NatsProxyPath") + natsProxyPort, _, _ := k.GetStringValue("NatsProxyPort") + natsStandardPort, _, _ := k.GetStringValue("NatsStandardPort") return &rmm.AgentConfig{ - BaseURL: baseurl, - AgentID: agentid, - APIURL: apiurl, - Token: token, - AgentPK: agentpk, - PK: pk, - Cert: cert, - Proxy: proxy, - CustomMeshDir: customMeshDir, + BaseURL: baseurl, + AgentID: agentid, + APIURL: apiurl, + Token: token, + AgentPK: agentpk, + PK: pk, + Cert: cert, + Proxy: proxy, + CustomMeshDir: customMeshDir, + NatsProxyPath: natsProxyPath, + NatsProxyPort: natsProxyPort, + NatsStandardPort: natsStandardPort, } } @@ -635,7 +641,7 @@ func (a *Agent) AgentUninstall(code string) { func (a *Agent) addDefenderExlusions() { code := ` Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*' -Add-MpPreference -ExclusionPath 'C:\Windows\Temp\winagent-v*.exe' +Add-MpPreference -ExclusionPath 'C:\Windows\Temp\tacticalagent-v*.exe' Add-MpPreference -ExclusionPath 'C:\Windows\Temp\trmm\*' Add-MpPreference -ExclusionPath 'C:\Program Files\Mesh Agent\*' ` diff --git a/agent/checkin.go b/agent/checkin.go index 1deebbd..9990198 100644 --- a/agent/checkin.go +++ b/agent/checkin.go @@ -12,7 +12,6 @@ https://license.tacticalrmm.com package agent import ( - "fmt" "runtime" "time" @@ -78,8 +77,7 @@ func (a *Agent) NatsMessage(nc *nats.Conn, mode string) { func (a *Agent) DoNatsCheckIn() { opts := a.setupNatsOptions() - server := fmt.Sprintf("tls://%s:4222", a.ApiURL) - nc, err := nats.Connect(server, opts...) + nc, err := nats.Connect(a.NatsServer, opts...) if err != nil { a.Logger.Errorln(err) return diff --git a/agent/install.go b/agent/install.go index 8614794..a975581 100644 --- a/agent/install.go +++ b/agent/install.go @@ -82,11 +82,6 @@ func (a *Agent) Install(i *Installer) { a.Logger.Debugln("API:", i.SaltMaster) - terr := TestTCP(fmt.Sprintf("%s:4222", i.SaltMaster)) - if terr != nil { - a.installerMsg(fmt.Sprintf("ERROR: Either port 4222 TCP is not open on your RMM, or nats.service is not running.\n\n%s", terr.Error()), "error", i.Silent) - } - baseURL := u.Scheme + "://" + u.Host a.Logger.Debugln("Base URL:", baseURL) @@ -138,14 +133,6 @@ func (a *Agent) Install(i *Installer) { rClient.SetProxy(i.Proxy) } - var arch string - switch a.Arch { - case "x86_64": - arch = "64" - case "x86": - arch = "32" - } - var installerMeshSystemEXE string if len(i.MeshDir) > 0 { installerMeshSystemEXE = filepath.Join(i.MeshDir, "MeshAgent.exe") @@ -159,7 +146,7 @@ func (a *Agent) Install(i *Installer) { mesh := filepath.Join(a.ProgramDir, a.MeshInstaller) if i.LocalMesh == "" { a.Logger.Infoln("Downloading mesh agent...") - payload := map[string]string{"arch": arch, "plat": a.Platform} + payload := map[string]string{"goarch": a.GoArch, "plat": a.Platform} r, err := rClient.R().SetBody(payload).SetOutput(mesh).Post(fmt.Sprintf("%s/api/v3/meshexe/", baseURL)) if err != nil { a.installerMsg(fmt.Sprintf("Failed to download mesh agent: %s", err.Error()), "error", i.Silent) diff --git a/agent/install_linux.go b/agent/install_unix.go similarity index 96% rename from agent/install_linux.go rename to agent/install_unix.go index b9debcc..cf6a068 100644 --- a/agent/install_linux.go +++ b/agent/install_unix.go @@ -1,3 +1,6 @@ +//go:build !windows +// +build !windows + /* Copyright 2022 AmidaWare LLC. diff --git a/agent/process.go b/agent/process.go index bd3e4ee..ae7c392 100644 --- a/agent/process.go +++ b/agent/process.go @@ -64,9 +64,16 @@ func (a *Agent) KillHungUpdates() { if err != nil { continue } + + // winagent-v* is deprecated if strings.Contains(p.Exe, "winagent-v") { a.Logger.Debugln("killing process", p.Exe) KillProc(int32(p.PID)) } + + if strings.Contains(p.Exe, "tacticalagent-v") { + a.Logger.Debugln("killing process", p.Exe) + KillProc(int32(p.PID)) + } } } diff --git a/agent/rpc.go b/agent/rpc.go index 7d24d0b..e457ecd 100644 --- a/agent/rpc.go +++ b/agent/rpc.go @@ -54,8 +54,7 @@ func (a *Agent) RunRPC() { var wg sync.WaitGroup wg.Add(1) opts := a.setupNatsOptions() - server := fmt.Sprintf("tls://%s:4222", a.ApiURL) - nc, err := nats.Connect(server, opts...) + nc, err := nats.Connect(a.NatsServer, opts...) if err != nil { a.Logger.Fatalln("RunRPC() nats.Connect()", err) } diff --git a/agent/svc.go b/agent/svc.go index 6e21b15..e360ebf 100644 --- a/agent/svc.go +++ b/agent/svc.go @@ -12,7 +12,6 @@ https://license.tacticalrmm.com package agent import ( - "fmt" "sync" "time" @@ -38,8 +37,7 @@ func (a *Agent) AgentSvc() { time.Sleep(time.Duration(sleepDelay) * time.Second) opts := a.setupNatsOptions() - server := fmt.Sprintf("tls://%s:4222", a.ApiURL) - nc, err := nats.Connect(server, opts...) + nc, err := nats.Connect(a.NatsServer, opts...) if err != nil { a.Logger.Fatalln("AgentSvc() nats.Connect()", err) } diff --git a/build/rmm.exe.manifest b/build/rmm.exe.manifest index b37646b..243bb4d 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 99bcd12..3a10b72 100644 --- a/build/setup.iss +++ b/build/setup.iss @@ -1,5 +1,5 @@ #define MyAppName "Tactical RMM Agent" -#define MyAppVersion "2.0.4" +#define MyAppVersion "2.1.0" #define MyAppPublisher "AmidaWare LLC" #define MyAppURL "https://github.com/amidaware" #define MyAppExeName "tacticalrmm.exe" diff --git a/main.go b/main.go index 392c376..ce69cfb 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ import ( ) var ( - version = "2.0.4" + version = "2.1.0" log = logrus.New() logFile *os.File ) @@ -197,6 +197,6 @@ func installUsage() { } func updateUsage() { - u := `Usage: tacticalrmm.exe -m update -updateurl https://example.com/winagent-vX.X.X.exe -inno winagent-vX.X.X.exe -updatever 1.1.1` + u := `Usage: tacticalrmm.exe -m update -updateurl https://example.com/tacticalagent-vX.X.X.exe -inno tacticalagent-vX.X.X.exe -updatever 1.1.1` fmt.Println(u) } diff --git a/shared/types.go b/shared/types.go index 64ae5be..2038f5e 100644 --- a/shared/types.go +++ b/shared/types.go @@ -33,15 +33,18 @@ type ProcessMsg struct { } type AgentConfig struct { - BaseURL string - AgentID string - APIURL string - Token string - AgentPK string - PK int - Cert string - Proxy string - CustomMeshDir string + BaseURL string + AgentID string + APIURL string + Token string + AgentPK string + PK int + Cert string + Proxy string + CustomMeshDir string + NatsProxyPath string + NatsProxyPort string + NatsStandardPort string } type RunScriptResp struct { diff --git a/versioninfo.json b/versioninfo.json index 13186f9..ef230db 100644 --- a/versioninfo.json +++ b/versioninfo.json @@ -2,14 +2,14 @@ "FixedFileInfo": { "FileVersion": { "Major": 2, - "Minor": 0, - "Patch": 4, + "Minor": 1, + "Patch": 0, "Build": 0 }, "ProductVersion": { "Major": 2, - "Minor": 0, - "Patch": 4, + "Minor": 1, + "Patch": 0, "Build": 0 }, "FileFlagsMask": "3f", @@ -22,14 +22,14 @@ "Comments": "", "CompanyName": "AmidaWare LLC", "FileDescription": "Tactical RMM Agent", - "FileVersion": "v2.0.4.0", + "FileVersion": "v2.1.0.0", "InternalName": "tacticalrmm.exe", "LegalCopyright": "Copyright (c) 2022 AmidaWare LLC", "LegalTrademarks": "", "OriginalFilename": "tacticalrmm.exe", "PrivateBuild": "", "ProductName": "Tactical RMM Agent", - "ProductVersion": "v2.0.4.0", + "ProductVersion": "v2.1.0.0", "SpecialBuild": "" }, "VarFileInfo": {