refactor continues
This commit is contained in:
parent
c038774f2c
commit
51f1eab127
30 changed files with 1373 additions and 474 deletions
|
|
@ -3,12 +3,12 @@ https://github.com/amidaware/tacticalrmm
|
||||||
|
|
||||||
#### building the agent - linux
|
#### building the agent - linux
|
||||||
```
|
```
|
||||||
env CGO_ENABLED=0 GOOS=<GOOS> GOARCH=<GOARCH> go build -ldflags "-s -w"
|
env CGO_ENABLED=0 GOOS=<GOOS> GOARCH=<GOARCH> go build -ldflags "-s -w -X 'main.Version=v2.0.4"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### building the agent - windows
|
#### building the agent - windows
|
||||||
```
|
```
|
||||||
$env:CGO_ENABLED="0";$env:GOOS="windows";$env:GOARCH="amd64"; go build -ldflags "-s -w"
|
$env:CGO_ENABLED="0";$env:GOOS="windows";$env:GOARCH="amd64"; go build -ldflags "-s -w -X 'main.Version=v2.0.4"
|
||||||
```
|
```
|
||||||
|
|
||||||
### tests
|
### tests
|
||||||
|
|
|
||||||
62
agent/choco/choco.go
Normal file
62
agent/choco/choco.go
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
package choco
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InstallChoco() {
|
||||||
|
config := config.NewAgentConfig()
|
||||||
|
var result ChocoInstalled
|
||||||
|
result.AgentID = config.AgentID
|
||||||
|
result.Installed = false
|
||||||
|
|
||||||
|
rClient := resty.New()
|
||||||
|
rClient.SetTimeout(30 * time.Second)
|
||||||
|
if len(config.Proxy) > 0 {
|
||||||
|
rClient.SetProxy(config.Proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
url := "/api/v3/choco/"
|
||||||
|
r, err := rClient.R().Get("https://chocolatey.org/install.ps1")
|
||||||
|
if err != nil {
|
||||||
|
api.PostPayload(result, url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.IsError() {
|
||||||
|
api.PostPayload(result, url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, exitcode, err := system.RunScript(string(r.Body()), "powershell", []string{}, 900)
|
||||||
|
if err != nil {
|
||||||
|
api.PostPayload(result, url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if exitcode != 0 {
|
||||||
|
api.PostPayload(result, url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Installed = true
|
||||||
|
api.PostPayload(result, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InstallWithChoco(name string) (string, error) {
|
||||||
|
out, err := system.CMD("choco.exe", []string{"install", name, "--yes", "--force", "--force-dependencies", "--no-progress"}, 1200, false)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if out[1] != "" {
|
||||||
|
return out[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return out[0], nil
|
||||||
|
}
|
||||||
6
agent/choco/structs.go
Normal file
6
agent/choco/structs.go
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package choco
|
||||||
|
|
||||||
|
type ChocoInstalled struct {
|
||||||
|
AgentID string `json:"agent_id"`
|
||||||
|
Installed bool `json:"installed"`
|
||||||
|
}
|
||||||
57
agent/network/network.go
Normal file
57
agent/network/network.go
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PublicIP returns the agent's public ip
|
||||||
|
// Tries 3 times before giving up
|
||||||
|
func PublicIP(proxy string) string {
|
||||||
|
client := resty.New()
|
||||||
|
client.SetTimeout(4 * time.Second)
|
||||||
|
if len(proxy) > 0 {
|
||||||
|
client.SetProxy(proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
urls := []string{"https://icanhazip.tacticalrmm.io/", "https://icanhazip.com", "https://ifconfig.co/ip"}
|
||||||
|
ip := "error"
|
||||||
|
for _, url := range urls {
|
||||||
|
r, err := client.R().Get(url)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = utils.StripAll(r.String())
|
||||||
|
if !IsValidIP(ip) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
v4 := net.ParseIP(ip)
|
||||||
|
if v4.To4() == nil {
|
||||||
|
r1, err := client.R().Get("https://ifconfig.me/ip")
|
||||||
|
if err != nil {
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv4 := utils.StripAll(r1.String())
|
||||||
|
if !IsValidIP(ipv4) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv4
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidIP checks for a valid ipv4 or ipv6
|
||||||
|
func IsValidIP(ip string) bool {
|
||||||
|
return net.ParseIP(ip) != nil
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
package patching
|
package patching
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/patching/wua"
|
"github.com/amidaware/rmmagent/agent/patching/wua"
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -59,3 +65,68 @@ func GetUpdates() (PackageList, error) {
|
||||||
// a.Logger.Debugln(err)
|
// a.Logger.Debugln(err)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InstallUpdates(guids []string) {
|
||||||
|
config := config.NewAgentConfig()
|
||||||
|
session, err := wua.NewUpdateSession()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer session.Close()
|
||||||
|
for _, id := range guids {
|
||||||
|
var result WinUpdateInstallResult
|
||||||
|
result.AgentID = config.AgentID
|
||||||
|
result.UpdateID = id
|
||||||
|
query := fmt.Sprintf("UpdateID='%s'", id)
|
||||||
|
updts, err := session.GetWUAUpdateCollection(query)
|
||||||
|
if err != nil {
|
||||||
|
result.Success = false
|
||||||
|
api.Patch(result, "/api/v3/winupdates/")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
defer updts.Release()
|
||||||
|
updtCnt, err := updts.Count()
|
||||||
|
if err != nil {
|
||||||
|
result.Success = false
|
||||||
|
api.Patch(result, "/api/v3/winupdates/")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if updtCnt == 0 {
|
||||||
|
superseded := SupersededUpdate{AgentID: config.AgentID, UpdateID: id}
|
||||||
|
api.PostPayload(superseded, "/api/v3/superseded/")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < int(updtCnt); i++ {
|
||||||
|
u, err := updts.Item(i)
|
||||||
|
if err != nil {
|
||||||
|
result.Success = false
|
||||||
|
api.Patch(result, "/api/v3/winupdates/")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.InstallWUAUpdate(u)
|
||||||
|
if err != nil {
|
||||||
|
result.Success = false
|
||||||
|
api.Patch(result, "/api/v3/winupdates/")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Success = true
|
||||||
|
api.Patch(result, "/api/v3/winupdates/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
needsReboot, err := system.SystemRebootRequired()
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
rebootPayload := AgentNeedsReboot{AgentID: config.AgentID, NeedsReboot: needsReboot}
|
||||||
|
err = api.Put(rebootPayload, "/api/v3/winupdates/")
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,19 @@ type Package struct {
|
||||||
Installed bool `json:"installed"`
|
Installed bool `json:"installed"`
|
||||||
Downloaded bool `json:"downloaded"`
|
Downloaded bool `json:"downloaded"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WinUpdateInstallResult struct {
|
||||||
|
AgentID string `json:"agent_id"`
|
||||||
|
UpdateID string `json:"guid"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupersededUpdate struct {
|
||||||
|
AgentID string `json:"agent_id"`
|
||||||
|
UpdateID string `json:"guid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentNeedsReboot struct {
|
||||||
|
AgentID string `json:"agent_id"`
|
||||||
|
NeedsReboot bool `json:"needs_reboot"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
"github.com/gonutz/w32/v2"
|
"github.com/gonutz/w32/v2"
|
||||||
|
"golang.org/x/sys/windows/svc"
|
||||||
"golang.org/x/sys/windows/svc/mgr"
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -162,3 +164,136 @@ func serviceStartType(num uint32) string {
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ControlService(name, action string) WinSvcResp {
|
||||||
|
conn, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer conn.Disconnect()
|
||||||
|
srv, err := conn.OpenService(name)
|
||||||
|
if err != nil {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer srv.Close()
|
||||||
|
var status svc.Status
|
||||||
|
switch action {
|
||||||
|
|
||||||
|
case "stop":
|
||||||
|
status, err = srv.Control(svc.Stop)
|
||||||
|
if err != nil {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||||
|
}
|
||||||
|
timeout := time.Now().Add(30 * time.Second)
|
||||||
|
for status.State != svc.Stopped {
|
||||||
|
if timeout.Before(time.Now()) {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: "Timed out waiting for service to stop"}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
status, err = srv.Query()
|
||||||
|
if err != nil {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WinSvcResp{Success: true, ErrorMsg: ""}
|
||||||
|
|
||||||
|
case "start":
|
||||||
|
err := srv.Start()
|
||||||
|
if err != nil {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WinSvcResp{Success: true, ErrorMsg: ""}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: "Something went wrong"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetServiceDetail(name string) Service {
|
||||||
|
ret := Service{}
|
||||||
|
|
||||||
|
conn, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
defer conn.Disconnect()
|
||||||
|
srv, err := conn.OpenService(name)
|
||||||
|
if err != nil {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
defer srv.Close()
|
||||||
|
q, err := srv.Query()
|
||||||
|
if err != nil {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, err := srv.Config()
|
||||||
|
if err != nil {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.BinPath = utils.CleanString(conf.BinaryPathName)
|
||||||
|
ret.Description = utils.CleanString(conf.Description)
|
||||||
|
ret.DisplayName = utils.CleanString(conf.DisplayName)
|
||||||
|
ret.Name = name
|
||||||
|
ret.PID = q.ProcessId
|
||||||
|
ret.StartType = serviceStartType(uint32(conf.StartType))
|
||||||
|
ret.Status = serviceStatusText(uint32(q.State))
|
||||||
|
ret.Username = utils.CleanString(conf.ServiceStartName)
|
||||||
|
ret.DelayedAutoStart = conf.DelayedAutoStart
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditService(name, startupType string) WinSvcResp {
|
||||||
|
conn, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||||
|
}
|
||||||
|
defer conn.Disconnect()
|
||||||
|
|
||||||
|
srv, err := conn.OpenService(name)
|
||||||
|
if err != nil {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||||
|
}
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
conf, err := srv.Config()
|
||||||
|
if err != nil {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
var startType uint32
|
||||||
|
switch startupType {
|
||||||
|
case "auto":
|
||||||
|
startType = 2
|
||||||
|
case "autodelay":
|
||||||
|
startType = 2
|
||||||
|
case "manual":
|
||||||
|
startType = 3
|
||||||
|
case "disabled":
|
||||||
|
startType = 4
|
||||||
|
default:
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: "Unknown startup type provided"}
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.StartType = startType
|
||||||
|
switch startupType {
|
||||||
|
case "autodelay":
|
||||||
|
conf.DelayedAutoStart = true
|
||||||
|
case "auto":
|
||||||
|
conf.DelayedAutoStart = false
|
||||||
|
}
|
||||||
|
|
||||||
|
err = srv.UpdateConfig(conf)
|
||||||
|
if err != nil {
|
||||||
|
return WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WinSvcResp{Success: true, ErrorMsg: ""}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,8 @@ type Service struct {
|
||||||
StartType string `json:"start_type"`
|
StartType string `json:"start_type"`
|
||||||
DelayedAutoStart bool `json:"autodelay"`
|
DelayedAutoStart bool `json:"autodelay"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WinSvcResp struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
ErrorMsg string `json:"errormsg"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,12 @@ type CmdOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type SchedTask struct{ Name string }
|
type SchedTask struct{ Name string }
|
||||||
|
|
||||||
|
type ProcessMsg struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Pid int `json:"pid"`
|
||||||
|
MemBytes uint64 `json:"membytes"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
UID int `json:"id"`
|
||||||
|
CPU string `json:"cpu_percent"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,14 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
ps "github.com/elastic/go-sysinfo"
|
ps "github.com/elastic/go-sysinfo"
|
||||||
gocmd "github.com/go-cmd/cmd"
|
gocmd "github.com/go-cmd/cmd"
|
||||||
|
"github.com/shirou/gopsutil/cpu"
|
||||||
|
gops "github.com/shirou/gopsutil/v3/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CmdStatus struct {
|
type CmdStatus struct {
|
||||||
|
|
@ -196,3 +199,68 @@ func BootTime() int64 {
|
||||||
info := host.Info()
|
info := host.Info()
|
||||||
return info.BootTime.Unix()
|
return info.BootTime.Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCPULoadAvg() int {
|
||||||
|
fallback := false
|
||||||
|
pyCode := `
|
||||||
|
import psutil
|
||||||
|
try:
|
||||||
|
print(int(round(psutil.cpu_percent(interval=10))), end='')
|
||||||
|
except:
|
||||||
|
print("pyerror", end='')
|
||||||
|
`
|
||||||
|
pypercent, err := RunPythonCode(pyCode, 13, []string{})
|
||||||
|
if err != nil || pypercent == "pyerror" {
|
||||||
|
fallback = true
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := strconv.Atoi(pypercent)
|
||||||
|
if err != nil {
|
||||||
|
fallback = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if fallback {
|
||||||
|
percent, err := cpu.Percent(10*time.Second, false)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(math.Round(percent[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProcsRPC() []ProcessMsg {
|
||||||
|
ret := make([]ProcessMsg, 0)
|
||||||
|
|
||||||
|
procs, _ := ps.Processes()
|
||||||
|
for i, process := range procs {
|
||||||
|
p, err := process.Info()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.PID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
m, _ := process.Memory()
|
||||||
|
proc, gerr := gops.NewProcess(int32(p.PID))
|
||||||
|
if gerr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cpu, _ := proc.CPUPercent()
|
||||||
|
user, _ := proc.Username()
|
||||||
|
|
||||||
|
ret = append(ret, ProcessMsg{
|
||||||
|
Name: p.Name,
|
||||||
|
Pid: p.PID,
|
||||||
|
MemBytes: m.Resident,
|
||||||
|
Username: user,
|
||||||
|
UID: i,
|
||||||
|
CPU: fmt.Sprintf("%.1f", cpu),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,7 @@ func init() {
|
||||||
|
|
||||||
func PostPayload(payload interface{}, url string) error {
|
func PostPayload(payload interface{}, url string) error {
|
||||||
_, err := restyC.R().SetBody(payload).Post("/api/v3/syncmesh/")
|
_, err := restyC.R().SetBody(payload).Post("/api/v3/syncmesh/")
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetResult(result interface{}, url string) (*resty.Response, error) {
|
func GetResult(result interface{}, url string) (*resty.Response, error) {
|
||||||
|
|
@ -52,3 +48,22 @@ func GetResult(result interface{}, url string) (*resty.Response, error) {
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Get(url string) (*resty.Response, error) {
|
||||||
|
r, err := restyC.R().Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Patch(payload interface{}, url string) error {
|
||||||
|
_, err := restyC.R().SetBody(payload).Patch(url)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Put(payload interface{}, url string) error {
|
||||||
|
_, err := restyC.R().SetBody(payload).Put(url)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
||||||
421
agent/tactical/checks/checks.go
Normal file
421
agent/tactical/checks/checks.go
Normal file
|
|
@ -0,0 +1,421 @@
|
||||||
|
package checks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/events"
|
||||||
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
ps "github.com/elastic/go-sysinfo"
|
||||||
|
"github.com/go-ping/ping"
|
||||||
|
"github.com/shirou/gopsutil/disk"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckRunner(agentID string) error {
|
||||||
|
sleepDelay := utils.RandRange(14, 22)
|
||||||
|
time.Sleep(time.Duration(sleepDelay) * time.Second)
|
||||||
|
for {
|
||||||
|
interval, err := GetCheckInterval(agentID)
|
||||||
|
if err == nil && !ChecksRunning() {
|
||||||
|
_, err = system.CMD(system.GetProgramEXE(), []string{"-m", "checkrunner"}, 600, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(interval) * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCheckInterval(agentID string) (int, error) {
|
||||||
|
r, err := api.GetResult(CheckInfo{}, fmt.Sprintf("/api/v3/%s/checkinterval/", agentID))
|
||||||
|
if err != nil {
|
||||||
|
return 120, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.IsError() {
|
||||||
|
return 120, fmt.Errorf("checkinterval response code: %v", r.StatusCode())
|
||||||
|
}
|
||||||
|
|
||||||
|
interval := r.Result().(*CheckInfo).Interval
|
||||||
|
return interval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChecksRunning prevents duplicate checks from running
|
||||||
|
// Have to do it this way, can't use atomic because they can run from both rpc and tacticalagent services
|
||||||
|
func ChecksRunning() bool {
|
||||||
|
running := false
|
||||||
|
procs, err := ps.Processes()
|
||||||
|
if err != nil {
|
||||||
|
return running
|
||||||
|
}
|
||||||
|
Out:
|
||||||
|
for _, process := range procs {
|
||||||
|
p, err := process.Info()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.PID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.Exe != system.GetProgramEXE() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range p.Args {
|
||||||
|
if arg == "runchecks" || arg == "checkrunner" {
|
||||||
|
running = true
|
||||||
|
break Out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return running
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunChecks(agentID string, force bool) error {
|
||||||
|
data := AllChecks{}
|
||||||
|
var url string
|
||||||
|
if force {
|
||||||
|
url = fmt.Sprintf("/api/v3/%s/runchecks/", agentID)
|
||||||
|
} else {
|
||||||
|
url = fmt.Sprintf("/api/v3/%s/checkrunner/", agentID)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := api.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.IsError() {
|
||||||
|
return fmt.Errorf("response error: %d", r.StatusCode())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(r.Body(), &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
eventLogChecks := make([]Check, 0)
|
||||||
|
winServiceChecks := make([]Check, 0)
|
||||||
|
for _, check := range data.Checks {
|
||||||
|
switch check.CheckType {
|
||||||
|
case "diskspace":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c Check, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
utils.RandomCheckDelay()
|
||||||
|
SendDiskCheckResult(DiskCheck(c))
|
||||||
|
}(check, &wg)
|
||||||
|
case "cpuload":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c Check, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
CPULoadCheck(c)
|
||||||
|
}(check, &wg)
|
||||||
|
case "memory":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c Check, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
utils.RandomCheckDelay()
|
||||||
|
MemCheck(c)
|
||||||
|
}(check, &wg)
|
||||||
|
case "ping":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c Check, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
utils.RandomCheckDelay()
|
||||||
|
SendPingCheckResult(PingCheck(c))
|
||||||
|
}(check, &wg)
|
||||||
|
case "script":
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c Check, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
utils.RandomCheckDelay()
|
||||||
|
ScriptCheck(c)
|
||||||
|
}(check, &wg)
|
||||||
|
case "winsvc":
|
||||||
|
winServiceChecks = append(winServiceChecks, check)
|
||||||
|
case "eventlog":
|
||||||
|
eventLogChecks = append(eventLogChecks, check)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(winServiceChecks) > 0 {
|
||||||
|
wg.Add(len(winServiceChecks))
|
||||||
|
go func(wg *sync.WaitGroup) {
|
||||||
|
for _, winSvcCheck := range winServiceChecks {
|
||||||
|
defer wg.Done()
|
||||||
|
SendWinSvcCheckResult(WinSvcCheck(winSvcCheck))
|
||||||
|
}
|
||||||
|
}(&wg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(eventLogChecks) > 0 {
|
||||||
|
wg.Add(len(eventLogChecks))
|
||||||
|
go func(wg *sync.WaitGroup) {
|
||||||
|
for _, evtCheck := range eventLogChecks {
|
||||||
|
defer wg.Done()
|
||||||
|
EventLogCheck(evtCheck)
|
||||||
|
}
|
||||||
|
}(&wg)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendDiskCheckResult(payload DiskCheckResult) error {
|
||||||
|
err := api.Patch(payload, "/api/v3/checkrunner/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DiskCheck(data Check) (payload DiskCheckResult) {
|
||||||
|
payload.ID = data.CheckPK
|
||||||
|
usage, err := disk.Usage(data.Disk)
|
||||||
|
if err != nil {
|
||||||
|
payload.Exists = false
|
||||||
|
payload.MoreInfo = fmt.Sprintf("Disk %s does not exist", data.Disk)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.Exists = true
|
||||||
|
payload.PercentUsed = usage.UsedPercent
|
||||||
|
payload.MoreInfo = fmt.Sprintf("Total: %s, Free: %s", utils.ByteCountSI(usage.Total), utils.ByteCountSI(usage.Free))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func CPULoadCheck(data Check) error {
|
||||||
|
payload := CPUMemResult{
|
||||||
|
ID: data.CheckPK,
|
||||||
|
Percent: system.GetCPULoadAvg(),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := api.PostPayload(payload, "/api/v3/checkrunner/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MemCheck(data Check) error {
|
||||||
|
host, _ := ps.Host()
|
||||||
|
mem, _ := host.Memory()
|
||||||
|
percent := (float64(mem.Used) / float64(mem.Total)) * 100
|
||||||
|
|
||||||
|
payload := CPUMemResult{ID: data.CheckPK, Percent: int(math.Round(percent))}
|
||||||
|
err := api.PostPayload(payload, "/api/v3/checkrunner/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendPingCheckResult(payload PingCheckResponse) error {
|
||||||
|
err := api.Patch(payload, "/api/v3/checkrunner/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PingCheck(data Check) (payload PingCheckResponse) {
|
||||||
|
payload.ID = data.CheckPK
|
||||||
|
|
||||||
|
out, err := DoPing(data.IP)
|
||||||
|
if err != nil {
|
||||||
|
payload.Status = "failing"
|
||||||
|
payload.Output = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.Status = out.Status
|
||||||
|
payload.Output = out.Output
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoPing(host string) (PingResponse, error) {
|
||||||
|
var ret PingResponse
|
||||||
|
pinger, err := ping.NewPinger(host)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
pinger.OnRecv = func(pkt *ping.Packet) {
|
||||||
|
fmt.Fprintf(&buf, "%d bytes from %s: icmp_seq=%d time=%v\n",
|
||||||
|
pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pinger.OnFinish = func(stats *ping.Statistics) {
|
||||||
|
fmt.Fprintf(&buf, "\n--- %s ping statistics ---\n", stats.Addr)
|
||||||
|
fmt.Fprintf(&buf, "%d packets transmitted, %d packets received, %v%% packet loss\n",
|
||||||
|
stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
|
||||||
|
fmt.Fprintf(&buf, "round-trip min/avg/max/stddev = %v/%v/%v/%v\n",
|
||||||
|
stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pinger.Count = 3
|
||||||
|
pinger.Size = 24
|
||||||
|
pinger.Interval = time.Second
|
||||||
|
pinger.Timeout = 5 * time.Second
|
||||||
|
pinger.SetPrivileged(true)
|
||||||
|
|
||||||
|
err = pinger.Run()
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Output = buf.String()
|
||||||
|
|
||||||
|
stats := pinger.Statistics()
|
||||||
|
|
||||||
|
if stats.PacketsRecv == stats.PacketsSent || stats.PacketLoss == 0 {
|
||||||
|
ret.Status = "passing"
|
||||||
|
} else {
|
||||||
|
ret.Status = "failing"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ScriptCheck(data Check) error {
|
||||||
|
start := time.Now()
|
||||||
|
stdout, stderr, retcode, _ := system.RunScript(data.Script.Code, data.Script.Shell, data.ScriptArgs, data.Timeout)
|
||||||
|
|
||||||
|
payload := ScriptCheckResult{
|
||||||
|
ID: data.CheckPK,
|
||||||
|
Stdout: stdout,
|
||||||
|
Stderr: stderr,
|
||||||
|
Retcode: retcode,
|
||||||
|
Runtime: time.Since(start).Seconds(),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := api.Patch(payload, "/api/v3/checkrunner/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendWinSvcCheckResult(payload WinSvcCheckResult) error {
|
||||||
|
err := api.Patch(payload, "/api/v3/checkrunner/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WinSvcCheck(data Check) (payload WinSvcCheckResult) {
|
||||||
|
payload.ID = data.CheckPK
|
||||||
|
status, err := services.GetServiceStatus(data.ServiceName)
|
||||||
|
if err != nil {
|
||||||
|
if data.PassNotExist {
|
||||||
|
payload.Status = "passing"
|
||||||
|
} else {
|
||||||
|
payload.Status = "failing"
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.MoreInfo = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.MoreInfo = fmt.Sprintf("Status: %s", status)
|
||||||
|
if status == "running" {
|
||||||
|
payload.Status = "passing"
|
||||||
|
} else if status == "start_pending" && data.PassStartPending {
|
||||||
|
payload.Status = "passing"
|
||||||
|
} else {
|
||||||
|
if data.RestartIfStopped {
|
||||||
|
ret := services.ControlService(data.ServiceName, "start")
|
||||||
|
if ret.Success {
|
||||||
|
payload.Status = "passing"
|
||||||
|
payload.MoreInfo = "Status: running"
|
||||||
|
} else {
|
||||||
|
payload.Status = "failing"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payload.Status = "failing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func EventLogCheck(data Check) error {
|
||||||
|
log := make([]events.EventLogMsg, 0)
|
||||||
|
evtLog, _ := events.GetEventLog(data.LogName, data.SearchLastDays)
|
||||||
|
|
||||||
|
for _, i := range evtLog {
|
||||||
|
if i.EventType == data.EventType {
|
||||||
|
if !data.EventIDWildcard && !(int(i.EventID) == data.EventID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.EventSource == "" && data.EventMessage == "" {
|
||||||
|
if data.EventIDWildcard {
|
||||||
|
log = append(log, i)
|
||||||
|
} else if int(i.EventID) == data.EventID {
|
||||||
|
log = append(log, i)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.EventSource != "" && data.EventMessage != "" {
|
||||||
|
if data.EventIDWildcard {
|
||||||
|
if strings.Contains(i.Source, data.EventSource) && strings.Contains(i.Message, data.EventMessage) {
|
||||||
|
log = append(log, i)
|
||||||
|
}
|
||||||
|
} else if int(i.EventID) == data.EventID {
|
||||||
|
if strings.Contains(i.Source, data.EventSource) && strings.Contains(i.Message, data.EventMessage) {
|
||||||
|
log = append(log, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.EventSource != "" && strings.Contains(i.Source, data.EventSource) {
|
||||||
|
if data.EventIDWildcard {
|
||||||
|
log = append(log, i)
|
||||||
|
} else if int(i.EventID) == data.EventID {
|
||||||
|
log = append(log, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.EventMessage != "" && strings.Contains(i.Message, data.EventMessage) {
|
||||||
|
if data.EventIDWildcard {
|
||||||
|
log = append(log, i)
|
||||||
|
} else if int(i.EventID) == data.EventID {
|
||||||
|
log = append(log, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := EventLogCheckResult{ID: data.CheckPK, Log: log}
|
||||||
|
err := api.Patch(payload, "/api/v3/checkrunner/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
package checks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/system"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/api"
|
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
|
||||||
ps "github.com/elastic/go-sysinfo"
|
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckRunner(agentID string) error {
|
|
||||||
sleepDelay := utils.RandRange(14, 22)
|
|
||||||
time.Sleep(time.Duration(sleepDelay) * time.Second)
|
|
||||||
for {
|
|
||||||
interval, err := GetCheckInterval(agentID)
|
|
||||||
if err == nil && !ChecksRunning() {
|
|
||||||
_, err = system.CMD(system.GetProgramEXE(), []string{"-m", "checkrunner"}, 600, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Duration(interval) * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetCheckInterval(agentID string) (int, error) {
|
|
||||||
r, err := api.GetResult(CheckInfo{}, fmt.Sprintf("/api/v3/%s/checkinterval/", agentID))
|
|
||||||
if err != nil {
|
|
||||||
return 120, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.IsError() {
|
|
||||||
return 120, fmt.Errorf("checkinterval response code: %v", r.StatusCode())
|
|
||||||
}
|
|
||||||
|
|
||||||
interval := r.Result().(*CheckInfo).Interval
|
|
||||||
return interval, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChecksRunning prevents duplicate checks from running
|
|
||||||
// Have to do it this way, can't use atomic because they can run from both rpc and tacticalagent services
|
|
||||||
func ChecksRunning() bool {
|
|
||||||
running := false
|
|
||||||
procs, err := ps.Processes()
|
|
||||||
if err != nil {
|
|
||||||
return running
|
|
||||||
}
|
|
||||||
Out:
|
|
||||||
for _, process := range procs {
|
|
||||||
p, err := process.Info()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p.PID == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p.Exe != system.GetProgramEXE() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, arg := range p.Args {
|
|
||||||
if arg == "runchecks" || arg == "checkrunner" {
|
|
||||||
running = true
|
|
||||||
break Out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return running
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunChecks(agentID string, force bool) error {
|
|
||||||
data := rmm.AllChecks{}
|
|
||||||
var url string
|
|
||||||
if force {
|
|
||||||
url = fmt.Sprintf("/api/v3/%s/runchecks/", agentID)
|
|
||||||
} else {
|
|
||||||
url = fmt.Sprintf("/api/v3/%s/checkrunner/", agentID)
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := a.rClient.R().Get(url)
|
|
||||||
if err != nil {
|
|
||||||
//a.Logger.Debugln(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.IsError() {
|
|
||||||
//a.Logger.Debugln("Checkrunner response code:", r.StatusCode())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(r.Body(), &data); err != nil {
|
|
||||||
//a.Logger.Debugln(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
eventLogChecks := make([]rmm.Check, 0)
|
|
||||||
winServiceChecks := make([]rmm.Check, 0)
|
|
||||||
for _, check := range data.Checks {
|
|
||||||
switch check.CheckType {
|
|
||||||
case "diskspace":
|
|
||||||
wg.Add(1)
|
|
||||||
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
|
||||||
defer wg.Done()
|
|
||||||
utils.RandomCheckDelay()
|
|
||||||
a.SendDiskCheckResult(a.DiskCheck(c), r)
|
|
||||||
}(check, &wg, a.rClient)
|
|
||||||
case "cpuload":
|
|
||||||
wg.Add(1)
|
|
||||||
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
|
||||||
defer wg.Done()
|
|
||||||
a.CPULoadCheck(c, r)
|
|
||||||
}(check, &wg, a.rClient)
|
|
||||||
case "memory":
|
|
||||||
wg.Add(1)
|
|
||||||
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
|
||||||
defer wg.Done()
|
|
||||||
randomCheckDelay()
|
|
||||||
a.MemCheck(c, r)
|
|
||||||
}(check, &wg, a.rClient)
|
|
||||||
case "ping":
|
|
||||||
wg.Add(1)
|
|
||||||
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
|
||||||
defer wg.Done()
|
|
||||||
randomCheckDelay()
|
|
||||||
a.SendPingCheckResult(a.PingCheck(c), r)
|
|
||||||
}(check, &wg, a.rClient)
|
|
||||||
case "script":
|
|
||||||
wg.Add(1)
|
|
||||||
go func(c rmm.Check, wg *sync.WaitGroup, r *resty.Client) {
|
|
||||||
defer wg.Done()
|
|
||||||
randomCheckDelay()
|
|
||||||
a.ScriptCheck(c, r)
|
|
||||||
}(check, &wg, a.rClient)
|
|
||||||
case "winsvc":
|
|
||||||
winServiceChecks = append(winServiceChecks, check)
|
|
||||||
case "eventlog":
|
|
||||||
eventLogChecks = append(eventLogChecks, check)
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(winServiceChecks) > 0 {
|
|
||||||
wg.Add(len(winServiceChecks))
|
|
||||||
go func(wg *sync.WaitGroup, r *resty.Client) {
|
|
||||||
for _, winSvcCheck := range winServiceChecks {
|
|
||||||
defer wg.Done()
|
|
||||||
a.SendWinSvcCheckResult(a.WinSvcCheck(winSvcCheck), r)
|
|
||||||
}
|
|
||||||
}(&wg, a.rClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(eventLogChecks) > 0 {
|
|
||||||
wg.Add(len(eventLogChecks))
|
|
||||||
go func(wg *sync.WaitGroup, r *resty.Client) {
|
|
||||||
for _, evtCheck := range eventLogChecks {
|
|
||||||
defer wg.Done()
|
|
||||||
a.EventLogCheck(evtCheck, r)
|
|
||||||
}
|
|
||||||
}(&wg, a.rClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,91 @@
|
||||||
package checks
|
package checks
|
||||||
|
|
||||||
|
import "github.com/amidaware/rmmagent/agent/events"
|
||||||
|
|
||||||
type CheckInfo struct {
|
type CheckInfo struct {
|
||||||
AgentPK int `json:"agent"`
|
AgentPK int `json:"agent"`
|
||||||
Interval int `json:"check_interval"`
|
Interval int `json:"check_interval"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AllChecks struct {
|
||||||
|
CheckInfo
|
||||||
|
Checks []Check
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssignedTask struct {
|
||||||
|
TaskPK int `json:"id"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Script struct {
|
||||||
|
Shell string `json:"shell"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
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 DiskCheckResult struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
MoreInfo string `json:"more_info"`
|
||||||
|
PercentUsed float64 `json:"percent_used"`
|
||||||
|
Exists bool `json:"exists"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CPUMemResult struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Percent int `json:"percent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PingCheckResponse struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
AgentID string `json:"agent_id"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Output string `json:"output"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PingResponse struct {
|
||||||
|
Status string
|
||||||
|
Output string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScriptCheckResult struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Stdout string `json:"stdout"`
|
||||||
|
Stderr string `json:"stderr"`
|
||||||
|
Retcode int `json:"retcode"`
|
||||||
|
Runtime float64 `json:"runtime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WinSvcCheckResult struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
MoreInfo string `json:"more_info"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventLogCheckResult struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Log []events.EventLogMsg `json:"log"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ package mesh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/api"
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SyncMeshNodeID(agentID string) error {
|
func SyncMeshNodeID() error {
|
||||||
|
config := config.NewAgentConfig()
|
||||||
id, err := GetMeshNodeID()
|
id, err := GetMeshNodeID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -13,7 +15,7 @@ func SyncMeshNodeID(agentID string) error {
|
||||||
|
|
||||||
payload := MeshNodeID{
|
payload := MeshNodeID{
|
||||||
Func: "syncmesh",
|
Func: "syncmesh",
|
||||||
Agentid: agentID,
|
Agentid: config.AgentID,
|
||||||
NodeID: utils.StripAll(id),
|
NodeID: utils.StripAll(id),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ package mesh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/system"
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
|
|
@ -71,7 +73,7 @@ func getMeshBinLocation() string {
|
||||||
return MeshSysBin
|
return MeshSysBin
|
||||||
}
|
}
|
||||||
|
|
||||||
func installMesh(meshbin, exe, proxy string) (string, error) {
|
func InstallMesh(meshbin, exe, proxy string) (string, error) {
|
||||||
var meshNodeID string
|
var meshNodeID string
|
||||||
meshInstallArgs := []string{"-fullinstall"}
|
meshInstallArgs := []string{"-fullinstall"}
|
||||||
if len(proxy) > 0 {
|
if len(proxy) > 0 {
|
||||||
|
|
@ -79,7 +81,6 @@ func installMesh(meshbin, exe, proxy string) (string, error) {
|
||||||
meshInstallArgs = append(meshInstallArgs, meshProxy)
|
meshInstallArgs = append(meshInstallArgs, meshProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
//a.Logger.Debugln("Mesh install args:", meshInstallArgs)
|
|
||||||
meshOut, meshErr := system.CMD(meshbin, meshInstallArgs, int(90), false)
|
meshOut, meshErr := system.CMD(meshbin, meshInstallArgs, int(90), false)
|
||||||
if meshErr != nil {
|
if meshErr != nil {
|
||||||
fmt.Println(meshOut[0])
|
fmt.Println(meshOut[0])
|
||||||
|
|
@ -88,30 +89,24 @@ func installMesh(meshbin, exe, proxy string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(meshOut)
|
fmt.Println(meshOut)
|
||||||
//a.Logger.Debugln("Sleeping for 5")
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
meshSuccess := false
|
meshSuccess := false
|
||||||
|
|
||||||
for !meshSuccess {
|
for !meshSuccess {
|
||||||
//a.Logger.Debugln("Getting mesh node id")
|
|
||||||
pMesh, pErr := system.CMD(exe, []string{"-nodeid"}, int(30), false)
|
pMesh, pErr := system.CMD(exe, []string{"-nodeid"}, int(30), false)
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
//a.Logger.Errorln(pErr)
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if pMesh[1] != "" {
|
if pMesh[1] != "" {
|
||||||
//a.Logger.Errorln(pMesh[1])
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
meshNodeID = utils.StripAll(pMesh[0])
|
meshNodeID = utils.StripAll(pMesh[0])
|
||||||
//a.Logger.Debugln("Node id:", meshNodeID)
|
|
||||||
if strings.Contains(strings.ToLower(meshNodeID), "not defined") {
|
if strings.Contains(strings.ToLower(meshNodeID), "not defined") {
|
||||||
//a.Logger.Errorln(meshNodeID)
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -121,3 +116,10 @@ func installMesh(meshbin, exe, proxy string) (string, error) {
|
||||||
|
|
||||||
return meshNodeID, nil
|
return meshNodeID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RecoverMesh() {
|
||||||
|
defer system.CMD("net", []string{"start", "mesh agent"}, 60, false)
|
||||||
|
_, _ = system.CMD("net", []string{"stop", "mesh agent"}, 60, false)
|
||||||
|
ForceKillMesh()
|
||||||
|
SyncMeshNodeID()
|
||||||
|
}
|
||||||
|
|
|
||||||
4
agent/tactical/rpc/rpc_linux.go
Normal file
4
agent/tactical/rpc/rpc_linux.go
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
func RunRPC(version string) {
|
||||||
|
}
|
||||||
|
|
@ -9,35 +9,51 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/choco"
|
||||||
"github.com/amidaware/rmmagent/agent/events"
|
"github.com/amidaware/rmmagent/agent/events"
|
||||||
|
"github.com/amidaware/rmmagent/agent/network"
|
||||||
"github.com/amidaware/rmmagent/agent/patching"
|
"github.com/amidaware/rmmagent/agent/patching"
|
||||||
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
|
"github.com/amidaware/rmmagent/agent/software"
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/checks"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/mesh"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/service"
|
"github.com/amidaware/rmmagent/agent/tactical/service"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
||||||
|
ttasks "github.com/amidaware/rmmagent/agent/tactical/tasks"
|
||||||
"github.com/amidaware/rmmagent/agent/tasks"
|
"github.com/amidaware/rmmagent/agent/tasks"
|
||||||
rmm "github.com/amidaware/rmmagent/shared"
|
ksvc "github.com/kardianos/service"
|
||||||
nats "github.com/nats-io/nats.go"
|
nats "github.com/nats-io/nats.go"
|
||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RunRPC(a *rmm.AgentConfig) {
|
var (
|
||||||
//a.Logger.Infoln("Agent service started")
|
agentUpdateLocker uint32
|
||||||
go service.RunAsService()
|
getWinUpdateLocker uint32
|
||||||
|
installWinUpdateLocker uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunRPC(version string) {
|
||||||
|
config := config.NewAgentConfig()
|
||||||
|
go service.RunAsService(version)
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
opts := service.SetupNatsOptions()
|
opts := service.SetupNatsOptions()
|
||||||
server := fmt.Sprintf("tls://%s:4222", a.APIURL)
|
server := fmt.Sprintf("tls://%s:4222", config.APIURL)
|
||||||
nc, err := nats.Connect(server, opts...)
|
nc, err := nats.Connect(server, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//a.Logger.Fatalln("RunRPC() nats.Connect()", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nc.Subscribe(a.AgentID, func(msg *nats.Msg) {
|
nc.Subscribe(config.AgentID, func(msg *nats.Msg) {
|
||||||
var payload *NatsMsg
|
var payload *NatsMsg
|
||||||
var mh codec.MsgpackHandle
|
var mh codec.MsgpackHandle
|
||||||
mh.RawToString = true
|
mh.RawToString = true
|
||||||
|
|
||||||
dec := codec.NewDecoderBytes(msg.Data, &mh)
|
dec := codec.NewDecoderBytes(msg.Data, &mh)
|
||||||
if err := dec.Decode(&payload); err != nil {
|
if err := dec.Decode(&payload); err != nil {
|
||||||
//a.Logger.Errorln(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +62,6 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func() {
|
go func() {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
//a.Logger.Debugln("pong")
|
|
||||||
ret.Encode("pong")
|
ret.Encode("pong")
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}()
|
}()
|
||||||
|
|
@ -57,7 +72,6 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
err := patching.PatchMgmnt(p.PatchMgmt)
|
err := patching.PatchMgmnt(p.PatchMgmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//a.Logger.Errorln("PatchMgmnt:", err.Error())
|
|
||||||
ret.Encode(err.Error())
|
ret.Encode(err.Error())
|
||||||
} else {
|
} else {
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
|
|
@ -71,7 +85,6 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
success, err := tasks.CreateSchedTask(p.ScheduledTask)
|
success, err := tasks.CreateSchedTask(p.ScheduledTask)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//a.Logger.Errorln(err.Error())
|
|
||||||
ret.Encode(err.Error())
|
ret.Encode(err.Error())
|
||||||
} else if !success {
|
} else if !success {
|
||||||
ret.Encode("Something went wrong")
|
ret.Encode("Something went wrong")
|
||||||
|
|
@ -87,7 +100,6 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
err := tasks.DeleteSchedTask(p.ScheduledTask.Name)
|
err := tasks.DeleteSchedTask(p.ScheduledTask.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//a.Logger.Errorln(err.Error())
|
|
||||||
ret.Encode(err.Error())
|
ret.Encode(err.Error())
|
||||||
} else {
|
} else {
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
|
|
@ -99,8 +111,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func() {
|
go func() {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
tasks := tasks.ListSchedTasks()
|
tasks, _ := tasks.ListSchedTasks()
|
||||||
//a.Logger.Debugln(tasks)
|
|
||||||
ret.Encode(tasks)
|
ret.Encode(tasks)
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}()
|
}()
|
||||||
|
|
@ -110,8 +121,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
days, _ := strconv.Atoi(p.Data["days"])
|
days, _ := strconv.Atoi(p.Data["days"])
|
||||||
evtLog := events.GetEventLog(p.Data["logname"], days)
|
evtLog, _ := events.GetEventLog(p.Data["logname"], days)
|
||||||
//a.Logger.Debugln(evtLog)
|
|
||||||
ret.Encode(evtLog)
|
ret.Encode(evtLog)
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}(payload)
|
}(payload)
|
||||||
|
|
@ -120,7 +130,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func() {
|
go func() {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
procs := a.GetProcsRPC()
|
procs := system.GetProcsRPC()
|
||||||
//a.Logger.Debugln(procs)
|
//a.Logger.Debugln(procs)
|
||||||
ret.Encode(procs)
|
ret.Encode(procs)
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
|
|
@ -130,10 +140,9 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func(p *NatsMsg) {
|
go func(p *NatsMsg) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
err := KillProc(p.ProcPID)
|
err := system.KillProc(p.ProcPID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ret.Encode(err.Error())
|
ret.Encode(err.Error())
|
||||||
//a.Logger.Debugln(err.Error())
|
|
||||||
} else {
|
} else {
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
}
|
}
|
||||||
|
|
@ -143,13 +152,12 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
case "rawcmd":
|
case "rawcmd":
|
||||||
go func(p *NatsMsg) {
|
go func(p *NatsMsg) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
var resultData rmm.RawCMDResp
|
var resultData RawCMDResp
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
out, _ := CMDShell(p.Data["shell"], []string{}, p.Data["command"], p.Timeout, false)
|
out, _ := system.CMDShell(p.Data["shell"], []string{}, p.Data["command"], p.Timeout, false)
|
||||||
//a.Logger.Debugln(out)
|
|
||||||
if out[1] != "" {
|
if out[1] != "" {
|
||||||
ret.Encode(out[1])
|
ret.Encode(out[1])
|
||||||
resultData.Results = out[1]
|
resultData.Results = out[1]
|
||||||
|
|
@ -158,11 +166,11 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
resultData.Results = out[0]
|
resultData.Results = out[0]
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
opts := a.NewCMDOpts()
|
opts := system.NewCMDOpts()
|
||||||
opts.Shell = p.Data["shell"]
|
opts.Shell = p.Data["shell"]
|
||||||
opts.Command = p.Data["command"]
|
opts.Command = p.Data["command"]
|
||||||
opts.Timeout = time.Duration(p.Timeout)
|
opts.Timeout = time.Duration(p.Timeout)
|
||||||
out := a.CmdV2(opts)
|
out := system.CmdV2(opts)
|
||||||
tmp := ""
|
tmp := ""
|
||||||
if len(out.Stdout) > 0 {
|
if len(out.Stdout) > 0 {
|
||||||
tmp += out.Stdout
|
tmp += out.Stdout
|
||||||
|
|
@ -177,7 +185,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
|
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
if p.ID != 0 {
|
if p.ID != 0 {
|
||||||
a.rClient.R().SetBody(resultData).Patch(fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, a.AgentID))
|
api.Patch(resultData, fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, config.AgentID))
|
||||||
}
|
}
|
||||||
}(payload)
|
}(payload)
|
||||||
|
|
||||||
|
|
@ -185,8 +193,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func() {
|
go func() {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
svcs := a.GetServices()
|
svcs, _, _ := services.GetServices()
|
||||||
//a.Logger.Debugln(svcs)
|
|
||||||
ret.Encode(svcs)
|
ret.Encode(svcs)
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}()
|
}()
|
||||||
|
|
@ -195,8 +202,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func(p *NatsMsg) {
|
go func(p *NatsMsg) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
svc := a.GetServiceDetail(p.Data["name"])
|
svc := services.GetServiceDetail(p.Data["name"])
|
||||||
//a.Logger.Debugln(svc)
|
|
||||||
ret.Encode(svc)
|
ret.Encode(svc)
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}(payload)
|
}(payload)
|
||||||
|
|
@ -205,8 +211,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func(p *NatsMsg) {
|
go func(p *NatsMsg) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
retData := a.ControlService(p.Data["name"], p.Data["action"])
|
retData := services.ControlService(p.Data["name"], p.Data["action"])
|
||||||
//a.Logger.Debugln(retData)
|
|
||||||
ret.Encode(retData)
|
ret.Encode(retData)
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}(payload)
|
}(payload)
|
||||||
|
|
@ -215,8 +220,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func(p *NatsMsg) {
|
go func(p *NatsMsg) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
retData := a.EditService(p.Data["name"], p.Data["startType"])
|
retData := services.EditService(p.Data["name"], p.Data["startType"])
|
||||||
//a.Logger.Debugln(retData)
|
|
||||||
ret.Encode(retData)
|
ret.Encode(retData)
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}(payload)
|
}(payload)
|
||||||
|
|
@ -225,15 +229,14 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func(p *NatsMsg) {
|
go func(p *NatsMsg) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
var retData string
|
var retData string
|
||||||
var resultData rmm.RunScriptResp
|
var resultData RunScriptResp
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
stdout, stderr, retcode, err := system.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||||
resultData.ExecTime = time.Since(start).Seconds()
|
resultData.ExecTime = time.Since(start).Seconds()
|
||||||
resultData.ID = p.ID
|
resultData.ID = p.ID
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//a.Logger.Debugln(err)
|
|
||||||
retData = err.Error()
|
retData = err.Error()
|
||||||
resultData.Retcode = 1
|
resultData.Retcode = 1
|
||||||
resultData.Stderr = err.Error()
|
resultData.Stderr = err.Error()
|
||||||
|
|
@ -248,17 +251,17 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
if p.ID != 0 {
|
if p.ID != 0 {
|
||||||
results := map[string]interface{}{"script_results": resultData}
|
results := map[string]interface{}{"script_results": resultData}
|
||||||
a.rClient.R().SetBody(results).Patch(fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, a.AgentID))
|
api.Patch(results, fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, config.AgentID))
|
||||||
}
|
}
|
||||||
}(payload)
|
}(payload)
|
||||||
|
|
||||||
case "runscriptfull":
|
case "runscriptfull":
|
||||||
go func(p *NatsMsg) {
|
go func(p *NatsMsg) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
var retData rmm.RunScriptResp
|
var retData RunScriptResp
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
stdout, stderr, retcode, err := system.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||||
|
|
||||||
retData.ExecTime = time.Since(start).Seconds()
|
retData.ExecTime = time.Since(start).Seconds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -275,7 +278,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
if p.ID != 0 {
|
if p.ID != 0 {
|
||||||
results := map[string]interface{}{"script_results": retData}
|
results := map[string]interface{}{"script_results": retData}
|
||||||
a.rClient.R().SetBody(results).Patch(fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, a.AgentID))
|
api.Patch(results, fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, config.AgentID))
|
||||||
}
|
}
|
||||||
}(payload)
|
}(payload)
|
||||||
|
|
||||||
|
|
@ -286,8 +289,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
|
|
||||||
switch p.Data["mode"] {
|
switch p.Data["mode"] {
|
||||||
case "mesh":
|
case "mesh":
|
||||||
//a.Logger.Debugln("Recovering mesh")
|
mesh.RecoverMesh()
|
||||||
a.RecoverMesh()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
|
|
@ -297,8 +299,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func() {
|
go func() {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
sw := a.GetInstalledSoftware()
|
sw, _ := software.GetInstalledSoftware()
|
||||||
//a.Logger.Debugln(sw)
|
|
||||||
ret.Encode(sw)
|
ret.Encode(sw)
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}()
|
}()
|
||||||
|
|
@ -311,24 +312,21 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
CMD("shutdown.exe", []string{"/r", "/t", "5", "/f"}, 15, false)
|
system.CMD("shutdown.exe", []string{"/r", "/t", "5", "/f"}, 15, false)
|
||||||
} else {
|
} else {
|
||||||
opts := a.NewCMDOpts()
|
opts := system.NewCMDOpts()
|
||||||
opts.Command = "reboot"
|
opts.Command = "reboot"
|
||||||
a.CmdV2(opts)
|
system.CmdV2(opts)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
case "needsreboot":
|
case "needsreboot":
|
||||||
go func() {
|
go func() {
|
||||||
//a.Logger.Debugln("Checking if reboot needed")
|
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
out, err := a.SystemRebootRequired()
|
out, err := system.SystemRebootRequired()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//a.Logger.Debugln("Reboot needed:", out)
|
|
||||||
ret.Encode(out)
|
ret.Encode(out)
|
||||||
} else {
|
} else {
|
||||||
//a.Logger.Debugln("Error checking if reboot needed:", err)
|
|
||||||
ret.Encode(false)
|
ret.Encode(false)
|
||||||
}
|
}
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
|
|
@ -337,26 +335,22 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
go func() {
|
go func() {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
//a.Logger.Debugln("Getting sysinfo with WMI")
|
|
||||||
modes := []string{"agent-agentinfo", "agent-disks", "agent-wmi", "agent-publicip"}
|
modes := []string{"agent-agentinfo", "agent-disks", "agent-wmi", "agent-publicip"}
|
||||||
for _, m := range modes {
|
for _, m := range modes {
|
||||||
a.NatsMessage(nc, m)
|
service.NatsMessage(version, nc, m)
|
||||||
}
|
}
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}()
|
}()
|
||||||
case "wmi":
|
case "wmi":
|
||||||
go func() {
|
go func() {
|
||||||
//a.Logger.Debugln("Sending WMI")
|
service.NatsMessage(version, nc, "agent-wmi")
|
||||||
a.NatsMessage(nc, "agent-wmi")
|
|
||||||
}()
|
}()
|
||||||
case "cpuloadavg":
|
case "cpuloadavg":
|
||||||
go func() {
|
go func() {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
//a.Logger.Debugln("Getting CPU Load Avg")
|
loadAvg := system.GetCPULoadAvg()
|
||||||
loadAvg := a.GetCPULoadAvg()
|
|
||||||
//a.Logger.Debugln("CPU Load Avg:", loadAvg)
|
|
||||||
ret.Encode(loadAvg)
|
ret.Encode(loadAvg)
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}()
|
}()
|
||||||
|
|
@ -365,54 +359,49 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
if a.ChecksRunning() {
|
if checks.ChecksRunning() {
|
||||||
ret.Encode("busy")
|
ret.Encode("busy")
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
//a.Logger.Debugln("Checks are already running, please wait")
|
|
||||||
} else {
|
} else {
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
//a.Logger.Debugln("Running checks")
|
_, checkerr := system.CMD(system.GetProgramEXE(), []string{"-m", "runchecks"}, 600, false)
|
||||||
_, checkerr := CMD(a.EXE, []string{"-m", "runchecks"}, 600, false)
|
|
||||||
if checkerr != nil {
|
if checkerr != nil {
|
||||||
//a.Logger.Errorln("RPC RunChecks", checkerr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
//a.Logger.Debugln("Running checks")
|
checks.RunChecks(config.AgentID, true)
|
||||||
a.RunChecks(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
case "runtask":
|
case "runtask":
|
||||||
go func(p *NatsMsg) {
|
go func(p *NatsMsg) {
|
||||||
//a.Logger.Debugln("Running task")
|
ttasks.RunTask(p.TaskPK)
|
||||||
a.RunTask(p.TaskPK)
|
|
||||||
}(payload)
|
}(payload)
|
||||||
|
|
||||||
case "publicip":
|
case "publicip":
|
||||||
go func() {
|
go func() {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
ret.Encode(a.PublicIP())
|
ret.Encode(network.PublicIP(config.Proxy))
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
}()
|
}()
|
||||||
case "installpython":
|
case "installpython":
|
||||||
go a.GetPython(true)
|
go shared.GetPython(true)
|
||||||
case "installchoco":
|
case "installchoco":
|
||||||
go a.InstallChoco()
|
go choco.InstallChoco()
|
||||||
case "installwithchoco":
|
case "installwithchoco":
|
||||||
go func(p *NatsMsg) {
|
go func(p *NatsMsg) {
|
||||||
var resp []byte
|
var resp []byte
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
out, _ := a.InstallWithChoco(p.ChocoProgName)
|
out, _ := choco.InstallWithChoco(p.ChocoProgName)
|
||||||
results := map[string]string{"results": out}
|
results := map[string]string{"results": out}
|
||||||
url := fmt.Sprintf("/api/v3/%d/chocoresult/", p.PendingActionPK)
|
url := fmt.Sprintf("/api/v3/%d/chocoresult/", p.PendingActionPK)
|
||||||
a.rClient.R().SetBody(results).Patch(url)
|
api.Patch(results, url)
|
||||||
}(payload)
|
}(payload)
|
||||||
case "getwinupdates":
|
case "getwinupdates":
|
||||||
go func() {
|
go func() {
|
||||||
|
|
@ -421,7 +410,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
} else {
|
} else {
|
||||||
//a.Logger.Debugln("Checking for windows updates")
|
//a.Logger.Debugln("Checking for windows updates")
|
||||||
defer atomic.StoreUint32(&getWinUpdateLocker, 0)
|
defer atomic.StoreUint32(&getWinUpdateLocker, 0)
|
||||||
a.GetWinUpdates()
|
patching.GetUpdates()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
case "installwinupdates":
|
case "installwinupdates":
|
||||||
|
|
@ -431,7 +420,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
} else {
|
} else {
|
||||||
//a.Logger.Debugln("Installing windows updates", p.UpdateGUIDs)
|
//a.Logger.Debugln("Installing windows updates", p.UpdateGUIDs)
|
||||||
defer atomic.StoreUint32(&installWinUpdateLocker, 0)
|
defer atomic.StoreUint32(&installWinUpdateLocker, 0)
|
||||||
a.InstallUpdates(p.UpdateGUIDs)
|
patching.InstallUpdates(p.UpdateGUIDs)
|
||||||
}
|
}
|
||||||
}(payload)
|
}(payload)
|
||||||
case "agentupdate":
|
case "agentupdate":
|
||||||
|
|
@ -445,7 +434,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
} else {
|
} else {
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
a.AgentUpdate(p.Data["url"], p.Data["inno"], p.Data["version"])
|
tactical.AgentUpdate(p.Data["url"], p.Data["inno"], p.Data["version"])
|
||||||
atomic.StoreUint32(&agentUpdateLocker, 0)
|
atomic.StoreUint32(&agentUpdateLocker, 0)
|
||||||
nc.Flush()
|
nc.Flush()
|
||||||
nc.Close()
|
nc.Close()
|
||||||
|
|
@ -459,7 +448,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
ret.Encode("ok")
|
ret.Encode("ok")
|
||||||
msg.Respond(resp)
|
msg.Respond(resp)
|
||||||
a.AgentUninstall(p.Code)
|
tactical.AgentUninstall(p.Code)
|
||||||
nc.Flush()
|
nc.Flush()
|
||||||
nc.Close()
|
nc.Close()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
|
@ -475,3 +464,8 @@ func RunRPC(a *rmm.AgentConfig) {
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Start(version string, _ ksvc.Service) error {
|
||||||
|
go RunRPC(version)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,15 @@ type NatsMsg struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RawCMDResp struct {
|
||||||
|
Results string `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunScriptResp struct {
|
||||||
|
Stdout string `json:"stdout"`
|
||||||
|
Stderr string `json:"stderr"`
|
||||||
|
Retcode int `json:"retcode"`
|
||||||
|
ExecTime float64 `json:"execution_time"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,53 +7,53 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/disk"
|
"github.com/amidaware/rmmagent/agent/disk"
|
||||||
|
"github.com/amidaware/rmmagent/agent/network"
|
||||||
"github.com/amidaware/rmmagent/agent/services"
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
"github.com/amidaware/rmmagent/agent/system"
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/checks"
|
"github.com/amidaware/rmmagent/agent/tactical/checks"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/mesh"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
"github.com/amidaware/rmmagent/agent/wmi"
|
"github.com/amidaware/rmmagent/agent/wmi"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
trmm "github.com/wh1te909/trmm-shared"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var natsCheckin = []string{"agent-hello", "agent-agentinfo", "agent-disks", "agent-winsvc", "agent-publicip", "agent-wmi"}
|
var natsCheckin = []string{"agent-hello", "agent-agentinfo", "agent-disks", "agent-winsvc", "agent-publicip", "agent-wmi"}
|
||||||
|
|
||||||
func RunAsService(agentID string, version string) {
|
func RunAsService(version string) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go AgentSvc(version)
|
go AgentSvc(version)
|
||||||
go checks.CheckRunner(agentID)
|
go checks.CheckRunner(version)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func AgentSvc(version string) {
|
func AgentSvc(version string) {
|
||||||
config := tactical.NewAgentConfig()
|
config := config.NewAgentConfig()
|
||||||
go tactical.GetPython(false)
|
go shared.GetPython(false)
|
||||||
utils.CreateTRMMTempDir()
|
utils.CreateTRMMTempDir()
|
||||||
tactical.RunMigrations()
|
shared.RunMigrations()
|
||||||
|
|
||||||
sleepDelay := utils.RandRange(14, 22)
|
sleepDelay := utils.RandRange(14, 22)
|
||||||
//a.Logger.Debugf("AgentSvc() sleeping for %v seconds", sleepDelay)
|
|
||||||
time.Sleep(time.Duration(sleepDelay) * time.Second)
|
time.Sleep(time.Duration(sleepDelay) * time.Second)
|
||||||
|
opts := SetupNatsOptions()
|
||||||
opts := SetupNatsOptions(config.AgentID, config.Token)
|
|
||||||
server := fmt.Sprintf("tls://%s:4222", config.APIURL)
|
server := fmt.Sprintf("tls://%s:4222", config.APIURL)
|
||||||
nc, err := nats.Connect(server, opts...)
|
nc, err := nats.Connect(server, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//a.Logger.Fatalln("AgentSvc() nats.Connect()", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range natsCheckin {
|
for _, s := range natsCheckin {
|
||||||
NatsMessage(config.AgentID, version, nc, s)
|
NatsMessage(version, nc, s)
|
||||||
time.Sleep(time.Duration(utils.RandRange(100, 400)) * time.Millisecond)
|
time.Sleep(time.Duration(utils.RandRange(100, 400)) * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
go tactical.SyncMeshNodeID()
|
go mesh.SyncMeshNodeID()
|
||||||
|
|
||||||
time.Sleep(time.Duration(utils.RandRange(1, 3)) * time.Second)
|
time.Sleep(time.Duration(utils.RandRange(1, 3)) * time.Second)
|
||||||
AgentStartup(config.AgentID)
|
AgentStartup(config.AgentID)
|
||||||
tactical.SendSoftware()
|
shared.SendSoftware()
|
||||||
|
|
||||||
checkInHelloTicker := time.NewTicker(time.Duration(utils.RandRange(30, 60)) * time.Second)
|
checkInHelloTicker := time.NewTicker(time.Duration(utils.RandRange(30, 60)) * time.Second)
|
||||||
checkInAgentInfoTicker := time.NewTicker(time.Duration(utils.RandRange(200, 400)) * time.Second)
|
checkInAgentInfoTicker := time.NewTicker(time.Duration(utils.RandRange(200, 400)) * time.Second)
|
||||||
|
|
@ -67,29 +67,30 @@ func AgentSvc(version string) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-checkInHelloTicker.C:
|
case <-checkInHelloTicker.C:
|
||||||
NatsMessage(config.AgentID, version, nc, "agent-hello")
|
NatsMessage(version, nc, "agent-hello")
|
||||||
case <-checkInAgentInfoTicker.C:
|
case <-checkInAgentInfoTicker.C:
|
||||||
NatsMessage(config.AgentID, version, nc, "agent-agentinfo")
|
NatsMessage(version, nc, "agent-agentinfo")
|
||||||
case <-checkInWinSvcTicker.C:
|
case <-checkInWinSvcTicker.C:
|
||||||
NatsMessage(config.AgentID, version, nc, "agent-winsvc")
|
NatsMessage(version, nc, "agent-winsvc")
|
||||||
case <-checkInPubIPTicker.C:
|
case <-checkInPubIPTicker.C:
|
||||||
NatsMessage(config.AgentID, version, nc, "agent-publicip")
|
NatsMessage(version, nc, "agent-publicip")
|
||||||
case <-checkInDisksTicker.C:
|
case <-checkInDisksTicker.C:
|
||||||
NatsMessage(config.AgentID, version, nc, "agent-disks")
|
NatsMessage(version, nc, "agent-disks")
|
||||||
case <-checkInSWTicker.C:
|
case <-checkInSWTicker.C:
|
||||||
tactical.SendSoftware()
|
shared.SendSoftware()
|
||||||
case <-checkInWMITicker.C:
|
case <-checkInWMITicker.C:
|
||||||
NatsMessage(config.AgentID, version, nc, "agent-wmi")
|
NatsMessage(version, nc, "agent-wmi")
|
||||||
case <-syncMeshTicker.C:
|
case <-syncMeshTicker.C:
|
||||||
tactical.SyncMeshNodeID()
|
mesh.SyncMeshNodeID()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupNatsOptions(agentID string, token string) []nats.Option {
|
func SetupNatsOptions() []nats.Option {
|
||||||
|
config := config.NewAgentConfig()
|
||||||
opts := make([]nats.Option, 0)
|
opts := make([]nats.Option, 0)
|
||||||
opts = append(opts, nats.Name("TacticalRMM"))
|
opts = append(opts, nats.Name("TacticalRMM"))
|
||||||
opts = append(opts, nats.UserInfo(agentID, token))
|
opts = append(opts, nats.UserInfo(config.AgentID, config.Token))
|
||||||
opts = append(opts, nats.ReconnectWait(time.Second*5))
|
opts = append(opts, nats.ReconnectWait(time.Second*5))
|
||||||
opts = append(opts, nats.RetryOnFailedConnect(true))
|
opts = append(opts, nats.RetryOnFailedConnect(true))
|
||||||
opts = append(opts, nats.MaxReconnects(-1))
|
opts = append(opts, nats.MaxReconnects(-1))
|
||||||
|
|
@ -97,21 +98,23 @@ func SetupNatsOptions(agentID string, token string) []nats.Option {
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func NatsMessage(agentID string, version string, nc *nats.Conn, mode string) {
|
func NatsMessage(version string, nc *nats.Conn, mode string) {
|
||||||
|
config := config.NewAgentConfig()
|
||||||
var resp []byte
|
var resp []byte
|
||||||
var payload interface{}
|
var payload interface{}
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case "agent-hello":
|
case "agent-hello":
|
||||||
payload = trmm.CheckInNats{
|
payload = CheckInNats{
|
||||||
Agentid: agentID,
|
Agentid: config.AgentID,
|
||||||
Version: version,
|
Version: version,
|
||||||
}
|
}
|
||||||
case "agent-winsvc":
|
case "agent-winsvc":
|
||||||
payload = trmm.WinSvcNats{
|
svcs, _, _ := services.GetServices()
|
||||||
Agentid: agentID,
|
payload = WinSvcNats{
|
||||||
WinSvcs: services.GetServices(),
|
Agentid: config.AgentID,
|
||||||
|
WinSvcs: svcs,
|
||||||
}
|
}
|
||||||
case "agent-agentinfo":
|
case "agent-agentinfo":
|
||||||
osinfo := system.OsString()
|
osinfo := system.OsString()
|
||||||
|
|
@ -119,8 +122,8 @@ func NatsMessage(agentID string, version string, nc *nats.Conn, mode string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reboot = false
|
reboot = false
|
||||||
}
|
}
|
||||||
payload = trmm.AgentInfoNats{
|
payload = AgentInfoNats{
|
||||||
Agentid: agentID,
|
Agentid: config.AgentID,
|
||||||
Username: system.LoggedOnUser(),
|
Username: system.LoggedOnUser(),
|
||||||
Hostname: system.GetHostname(),
|
Hostname: system.GetHostname(),
|
||||||
OS: osinfo,
|
OS: osinfo,
|
||||||
|
|
@ -131,48 +134,47 @@ func NatsMessage(agentID string, version string, nc *nats.Conn, mode string) {
|
||||||
GoArch: runtime.GOARCH,
|
GoArch: runtime.GOARCH,
|
||||||
}
|
}
|
||||||
case "agent-wmi":
|
case "agent-wmi":
|
||||||
payload = trmm.WinWMINats{
|
wmiinfo, _ := wmi.GetWMIInfo()
|
||||||
Agentid: agentID,
|
payload = WinWMINats{
|
||||||
WMI: wmi.GetWMIInfo(),
|
Agentid: config.AgentID,
|
||||||
|
WMI: wmiinfo,
|
||||||
}
|
}
|
||||||
case "agent-disks":
|
case "agent-disks":
|
||||||
payload = trmm.WinDisksNats{
|
disks, _ := disk.GetDisks()
|
||||||
Agentid: agentID,
|
payload = WinDisksNats{
|
||||||
Disks: disk.GetDisks(),
|
Agentid: config.AgentID,
|
||||||
|
Disks: disks,
|
||||||
}
|
}
|
||||||
case "agent-publicip":
|
case "agent-publicip":
|
||||||
payload = trmm.PublicIPNats{
|
payload = PublicIPNats{
|
||||||
Agentid: agentID,
|
Agentid: config.AgentID,
|
||||||
PublicIP: a.PublicIP(),
|
PublicIP: network.PublicIP(config.Proxy),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//a.Logger.Debugln(mode, payload)
|
//a.Logger.Debugln(mode, payload)
|
||||||
ret.Encode(payload)
|
ret.Encode(payload)
|
||||||
nc.PublishRequest(a.AgentID, mode, resp)
|
nc.PublishRequest(config.AgentID, mode, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoNatsCheckIn() {
|
func DoNatsCheckIn(version string) {
|
||||||
opts := SetupNatsOptions()
|
opts := SetupNatsOptions()
|
||||||
server := fmt.Sprintf("tls://%s:4222", a.ApiURL)
|
server := fmt.Sprintf("tls://%s:4222", config.NewAgentConfig().APIURL)
|
||||||
nc, err := nats.Connect(server, opts...)
|
nc, err := nats.Connect(server, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//a.Logger.Errorln(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range natsCheckin {
|
for _, s := range natsCheckin {
|
||||||
time.Sleep(time.Duration(utils.RandRange(100, 400)) * time.Millisecond)
|
time.Sleep(time.Duration(utils.RandRange(100, 400)) * time.Millisecond)
|
||||||
NatsMessage(nc, s)
|
NatsMessage(version, nc, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
nc.Close()
|
nc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func AgentStartup(agentID string) {
|
func AgentStartup(agentID string) error {
|
||||||
url := "/api/v3/checkin/"
|
|
||||||
payload := map[string]interface{}{"agent_id": agentID}
|
payload := map[string]interface{}{"agent_id": agentID}
|
||||||
_, err := tactical.PostRequest(url, payload, 15)
|
err := api.PostPayload(payload, "/api/v3/checkin/")
|
||||||
if err != nil {
|
return err
|
||||||
//a.Logger.Debugln("AgentStartup()", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
43
agent/tactical/service/structs.go
Normal file
43
agent/tactical/service/structs.go
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/amidaware/rmmagent/agent/disk"
|
||||||
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WinSvcNats struct {
|
||||||
|
Agentid string `json:"agent_id"`
|
||||||
|
WinSvcs []services.Service `json:"services"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckInNats struct {
|
||||||
|
Agentid string `json:"agent_id"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentInfoNats struct {
|
||||||
|
Agentid string `json:"agent_id"`
|
||||||
|
Username string `json:"logged_in_username"`
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
OS string `json:"operating_system"`
|
||||||
|
Platform string `json:"plat"`
|
||||||
|
TotalRAM float64 `json:"total_ram"`
|
||||||
|
BootTime int64 `json:"boot_time"`
|
||||||
|
RebootNeeded bool `json:"needs_reboot"`
|
||||||
|
GoArch string `json:"goarch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WinWMINats struct {
|
||||||
|
Agentid string `json:"agent_id"`
|
||||||
|
WMI interface{} `json:"wmi"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WinDisksNats struct {
|
||||||
|
Agentid string `json:"agent_id"`
|
||||||
|
Disks []disk.Disk `json:"disks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublicIPNats struct {
|
||||||
|
Agentid string `json:"agent_id"`
|
||||||
|
PublicIP string `json:"public_ip"`
|
||||||
|
}
|
||||||
19
agent/tactical/shared/shared.go
Normal file
19
agent/tactical/shared/shared.go
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/amidaware/rmmagent/agent/software"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SendSoftware() error {
|
||||||
|
config := config.NewAgentConfig()
|
||||||
|
sw, _ := software.GetInstalledSoftware()
|
||||||
|
payload := map[string]interface{}{"agent_id": config.AgentID, "software": sw}
|
||||||
|
err := api.PostPayload(payload, "/api/v3/software/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
70
agent/tactical/shared/shared_windows.go
Normal file
70
agent/tactical/shared/shared_windows.go
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPython(force bool) {
|
||||||
|
if utils.FileExists(system.GetPythonBin()) && !force {
|
||||||
|
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(system.GetProgramDirectory(), folder)
|
||||||
|
pyZip := filepath.Join(system.GetProgramDirectory(), archZip)
|
||||||
|
defer os.Remove(pyZip)
|
||||||
|
|
||||||
|
if force {
|
||||||
|
os.RemoveAll(pyFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := config.NewAgentConfig()
|
||||||
|
rClient := resty.New()
|
||||||
|
rClient.SetTimeout(20 * time.Minute)
|
||||||
|
rClient.SetRetryCount(10)
|
||||||
|
rClient.SetRetryWaitTime(1 * time.Minute)
|
||||||
|
rClient.SetRetryMaxWaitTime(15 * time.Minute)
|
||||||
|
if len(config.Proxy) > 0 {
|
||||||
|
rClient.SetProxy(config.Proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://github.com/amidaware/rmmagent/releases/download/v2.0.0/%s", archZip)
|
||||||
|
r, err := rClient.R().SetOutput(pyZip).Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.IsError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = utils.Unzip(pyZip, system.GetProgramDirectory())
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunMigrations() {
|
||||||
|
for _, i := range []string{"nssm.exe", "nssm-x86.exe"} {
|
||||||
|
nssm := filepath.Join(system.GetProgramDirectory(), i)
|
||||||
|
if utils.FileExists(nssm) {
|
||||||
|
os.Remove(nssm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
agent/tactical/structs.go
Normal file
1
agent/tactical/structs.go
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
package tactical
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
package tactical
|
package tactical
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,2 @@
|
||||||
package tactical
|
package tactical_test
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestSyncMeshNodeID(t *testing.T) {
|
|
||||||
agentConfig := NewAgentConfig()
|
|
||||||
if agentConfig.AgentID == "" {
|
|
||||||
t.Fatal("Could not get AgentID")
|
|
||||||
}
|
|
||||||
|
|
||||||
result := SyncMeshNodeID()
|
|
||||||
if !result {
|
|
||||||
t.Fatal("SyncMeshNodeID resulted in error")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Log("Synced mesh node id")
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -14,13 +13,10 @@ import (
|
||||||
"github.com/amidaware/rmmagent/agent/services"
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
"github.com/amidaware/rmmagent/agent/system"
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/rpc"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tasks"
|
"github.com/amidaware/rmmagent/agent/tasks"
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
rmm "github.com/amidaware/rmmagent/shared"
|
rmm "github.com/amidaware/rmmagent/shared"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/kardianos/service"
|
|
||||||
trmm "github.com/wh1te909/trmm-shared"
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
)
|
)
|
||||||
|
|
@ -37,9 +33,6 @@ func AgentUpdate(url, inno, version string) {
|
||||||
system.KillHungUpdates()
|
system.KillHungUpdates()
|
||||||
CleanupAgentUpdates()
|
CleanupAgentUpdates()
|
||||||
updater := filepath.Join(system.GetProgramDirectory(), inno)
|
updater := filepath.Join(system.GetProgramDirectory(), inno)
|
||||||
//a.Logger.Infof("Agent updating from %s to %s", a.Version, version)
|
|
||||||
//a.Logger.Infoln("Downloading agent update from", url)
|
|
||||||
|
|
||||||
config := config.NewAgentConfig()
|
config := config.NewAgentConfig()
|
||||||
rClient := resty.New()
|
rClient := resty.New()
|
||||||
rClient.SetCloseConnection(true)
|
rClient.SetCloseConnection(true)
|
||||||
|
|
@ -51,20 +44,17 @@ func AgentUpdate(url, inno, version string) {
|
||||||
|
|
||||||
r, err := rClient.R().SetOutput(updater).Get(url)
|
r, err := rClient.R().SetOutput(updater).Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//a.Logger.Errorln(err)
|
|
||||||
system.CMD("net", []string{"start", services.WinSvcName}, 10, false)
|
system.CMD("net", []string{"start", services.WinSvcName}, 10, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.IsError() {
|
if r.IsError() {
|
||||||
//a.Logger.Errorln("Download failed with status code", r.StatusCode())
|
|
||||||
system.CMD("net", []string{"start", services.WinSvcName}, 10, false)
|
system.CMD("net", []string{"start", services.WinSvcName}, 10, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "tacticalrmm")
|
dir, err := ioutil.TempDir("", "tacticalrmm")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//a.Logger.Errorln("Agentupdate create tempdir:", err)
|
|
||||||
system.CMD("net", []string{"start", services.WinSvcName}, 10, false)
|
system.CMD("net", []string{"start", services.WinSvcName}, 10, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +74,6 @@ func CleanupAgentUpdates() {
|
||||||
pd := filepath.Join(os.Getenv("ProgramFiles"), system.ProgFilesName)
|
pd := filepath.Join(os.Getenv("ProgramFiles"), system.ProgFilesName)
|
||||||
cderr := os.Chdir(pd)
|
cderr := os.Chdir(pd)
|
||||||
if cderr != nil {
|
if cderr != nil {
|
||||||
//a.Logger.Errorln(cderr)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +86,6 @@ func CleanupAgentUpdates() {
|
||||||
|
|
||||||
cderr = os.Chdir(os.Getenv("TMP"))
|
cderr = os.Chdir(os.Getenv("TMP"))
|
||||||
if cderr != nil {
|
if cderr != nil {
|
||||||
//a.Logger.Errorln(cderr)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,122 +123,3 @@ func GetUninstallExe() string {
|
||||||
|
|
||||||
return "unins000.exe"
|
return "unins000.exe"
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunMigrations cleans up unused stuff from older agents
|
|
||||||
func RunMigrations() {
|
|
||||||
for _, i := range []string{"nssm.exe", "nssm-x86.exe"} {
|
|
||||||
nssm := filepath.Join(system.GetProgramDirectory(), i)
|
|
||||||
if trmm.FileExists(nssm) {
|
|
||||||
os.Remove(nssm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func installMesh(meshbin, exe, proxy string) (string, error) {
|
|
||||||
var meshNodeID string
|
|
||||||
meshInstallArgs := []string{"-fullinstall"}
|
|
||||||
if len(proxy) > 0 {
|
|
||||||
meshProxy := fmt.Sprintf("--WebProxy=%s", proxy)
|
|
||||||
meshInstallArgs = append(meshInstallArgs, meshProxy)
|
|
||||||
}
|
|
||||||
|
|
||||||
//a.Logger.Debugln("Mesh install args:", meshInstallArgs)
|
|
||||||
meshOut, meshErr := system.CMD(meshbin, meshInstallArgs, int(90), false)
|
|
||||||
if meshErr != nil {
|
|
||||||
fmt.Println(meshOut[0])
|
|
||||||
fmt.Println(meshOut[1])
|
|
||||||
fmt.Println(meshErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(meshOut)
|
|
||||||
//a.Logger.Debugln("Sleeping for 5")
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
meshSuccess := false
|
|
||||||
|
|
||||||
for !meshSuccess {
|
|
||||||
//a.Logger.Debugln("Getting mesh node id")
|
|
||||||
pMesh, pErr := system.CMD(exe, []string{"-nodeid"}, int(30), false)
|
|
||||||
if pErr != nil {
|
|
||||||
//a.Logger.Errorln(pErr)
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if pMesh[1] != "" {
|
|
||||||
//a.Logger.Errorln(pMesh[1])
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
meshNodeID = utils.StripAll(pMesh[0])
|
|
||||||
//a.Logger.Debugln("Node id:", meshNodeID)
|
|
||||||
if strings.Contains(strings.ToLower(meshNodeID), "not defined") {
|
|
||||||
//a.Logger.Errorln(meshNodeID)
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
meshSuccess = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return meshNodeID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Start(_ service.Service) error {
|
|
||||||
go rpc.RunRPC(NewAgentConfig())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPython(force bool) {
|
|
||||||
if trmm.FileExists(system.GetPythonBin()) && !force {
|
|
||||||
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(system.GetProgramDirectory(), folder)
|
|
||||||
pyZip := filepath.Join(system.GetProgramDirectory(), archZip)
|
|
||||||
//a.Logger.Debugln(pyZip)
|
|
||||||
//a.Logger.Debugln(a.PyBin)
|
|
||||||
defer os.Remove(pyZip)
|
|
||||||
|
|
||||||
if force {
|
|
||||||
os.RemoveAll(pyFolder)
|
|
||||||
}
|
|
||||||
|
|
||||||
config := NewAgentConfig()
|
|
||||||
rClient := resty.New()
|
|
||||||
rClient.SetTimeout(20 * time.Minute)
|
|
||||||
rClient.SetRetryCount(10)
|
|
||||||
rClient.SetRetryWaitTime(1 * time.Minute)
|
|
||||||
rClient.SetRetryMaxWaitTime(15 * time.Minute)
|
|
||||||
if len(config.Proxy) > 0 {
|
|
||||||
rClient.SetProxy(config.Proxy)
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("https://github.com/amidaware/rmmagent/releases/download/v2.0.0/%s", archZip)
|
|
||||||
//a.Logger.Debugln(url)
|
|
||||||
r, err := rClient.R().SetOutput(pyZip).Get(url)
|
|
||||||
if err != nil {
|
|
||||||
//a.Logger.Errorln("Unable to download py3.zip from github.", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if r.IsError() {
|
|
||||||
//a.Logger.Errorln("Unable to download py3.zip from github. Status code", r.StatusCode())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = utils.Unzip(pyZip, system.GetProgramDirectory())
|
|
||||||
if err != nil {
|
|
||||||
//a.Logger.Errorln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
25
agent/tactical/tasks/structs.go
Normal file
25
agent/tactical/tasks/structs.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package tasks
|
||||||
|
|
||||||
|
type AutomatedTask struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
TaskActions []TaskAction `json:"task_actions"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
ContinueOnError bool `json:"continue_on_error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskResult struct {
|
||||||
|
Stdout string `json:"stdout"`
|
||||||
|
Stderr string `json:"stderr"`
|
||||||
|
RetCode int `json:"retcode"`
|
||||||
|
ExecTime float64 `json:"execution_time"`
|
||||||
|
}
|
||||||
95
agent/tactical/tasks/tasks.go
Normal file
95
agent/tactical/tasks/tasks.go
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunTask(id int) error {
|
||||||
|
config := config.NewAgentConfig()
|
||||||
|
data := AutomatedTask{}
|
||||||
|
url := fmt.Sprintf("/api/v3/%d/%s/taskrunner/", id, config.AgentID)
|
||||||
|
r1, gerr := api.Get(url)
|
||||||
|
if gerr != nil {
|
||||||
|
return gerr
|
||||||
|
}
|
||||||
|
|
||||||
|
if r1.IsError() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(r1.Body(), &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
payload := TaskResult{}
|
||||||
|
// loop through all task actions
|
||||||
|
for _, action := range data.TaskActions {
|
||||||
|
action_start := time.Now()
|
||||||
|
if action.ActionType == "script" {
|
||||||
|
stdout, stderr, retcode, err := system.RunScript(action.Code, action.Shell, action.Args, action.Timeout)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 := system.CMDShell(action.Shell, []string{}, action.Command, action.Timeout, false)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.ExecTime = time.Since(start).Seconds()
|
||||||
|
perr := api.Patch(payload, url)
|
||||||
|
if perr != nil {
|
||||||
|
return perr
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
8
main.go
8
main.go
|
|
@ -14,17 +14,17 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/amidaware/rmmagent/agent"
|
||||||
|
"github.com/kardianos/service"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"github.com/amidaware/rmmagent/agent"
|
|
||||||
"github.com/kardianos/service"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "2.0.4"
|
version = "development"
|
||||||
log = logrus.New()
|
log = logrus.New()
|
||||||
logFile *os.File
|
logFile *os.File
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue