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
|
||||
```
|
||||
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
|
||||
```
|
||||
$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
|
||||
|
|
|
|||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
|
|
@ -59,3 +65,68 @@ func GetUpdates() (PackageList, error) {
|
|||
// 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"`
|
||||
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 (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/amidaware/rmmagent/agent/utils"
|
||||
"github.com/gonutz/w32/v2"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
|
|
@ -162,3 +164,136 @@ func serviceStartType(num uint32) string {
|
|||
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"`
|
||||
DelayedAutoStart bool `json:"autodelay"`
|
||||
}
|
||||
|
||||
type WinSvcResp struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorMsg string `json:"errormsg"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,4 +12,13 @@ type CmdOptions struct {
|
|||
Detached bool
|
||||
}
|
||||
|
||||
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"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/amidaware/rmmagent/agent/utils"
|
||||
ps "github.com/elastic/go-sysinfo"
|
||||
gocmd "github.com/go-cmd/cmd"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
gops "github.com/shirou/gopsutil/v3/process"
|
||||
)
|
||||
|
||||
type CmdStatus struct {
|
||||
|
|
@ -196,3 +199,68 @@ func BootTime() int64 {
|
|||
info := host.Info()
|
||||
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 {
|
||||
_, err := restyC.R().SetBody(payload).Post("/api/v3/syncmesh/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import "github.com/amidaware/rmmagent/agent/events"
|
||||
|
||||
type CheckInfo struct {
|
||||
AgentPK int `json:"agent"`
|
||||
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 (
|
||||
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||
"github.com/amidaware/rmmagent/agent/utils"
|
||||
)
|
||||
|
||||
func SyncMeshNodeID(agentID string) error {
|
||||
func SyncMeshNodeID() error {
|
||||
config := config.NewAgentConfig()
|
||||
id, err := GetMeshNodeID()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -13,7 +15,7 @@ func SyncMeshNodeID(agentID string) error {
|
|||
|
||||
payload := MeshNodeID{
|
||||
Func: "syncmesh",
|
||||
Agentid: agentID,
|
||||
Agentid: config.AgentID,
|
||||
NodeID: utils.StripAll(id),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ package mesh
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/amidaware/rmmagent/agent/system"
|
||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||
|
|
@ -71,7 +73,7 @@ func getMeshBinLocation() string {
|
|||
return MeshSysBin
|
||||
}
|
||||
|
||||
func installMesh(meshbin, exe, proxy string) (string, error) {
|
||||
func InstallMesh(meshbin, exe, proxy string) (string, error) {
|
||||
var meshNodeID string
|
||||
meshInstallArgs := []string{"-fullinstall"}
|
||||
if len(proxy) > 0 {
|
||||
|
|
@ -79,7 +81,6 @@ func installMesh(meshbin, exe, proxy string) (string, error) {
|
|||
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])
|
||||
|
|
@ -88,30 +89,24 @@ func installMesh(meshbin, exe, proxy string) (string, error) {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -121,3 +116,10 @@ func installMesh(meshbin, exe, proxy string) (string, error) {
|
|||
|
||||
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"
|
||||
"time"
|
||||
|
||||
"github.com/amidaware/rmmagent/agent/choco"
|
||||
"github.com/amidaware/rmmagent/agent/events"
|
||||
"github.com/amidaware/rmmagent/agent/network"
|
||||
"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/shared"
|
||||
ttasks "github.com/amidaware/rmmagent/agent/tactical/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"
|
||||
"github.com/ugorji/go/codec"
|
||||
)
|
||||
|
||||
func RunRPC(a *rmm.AgentConfig) {
|
||||
//a.Logger.Infoln("Agent service started")
|
||||
go service.RunAsService()
|
||||
var (
|
||||
agentUpdateLocker uint32
|
||||
getWinUpdateLocker uint32
|
||||
installWinUpdateLocker uint32
|
||||
)
|
||||
|
||||
func RunRPC(version string) {
|
||||
config := config.NewAgentConfig()
|
||||
go service.RunAsService(version)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
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...)
|
||||
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 mh codec.MsgpackHandle
|
||||
mh.RawToString = true
|
||||
|
||||
dec := codec.NewDecoderBytes(msg.Data, &mh)
|
||||
if err := dec.Decode(&payload); err != nil {
|
||||
//a.Logger.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +62,6 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func() {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
//a.Logger.Debugln("pong")
|
||||
ret.Encode("pong")
|
||||
msg.Respond(resp)
|
||||
}()
|
||||
|
|
@ -57,7 +72,6 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
err := patching.PatchMgmnt(p.PatchMgmt)
|
||||
if err != nil {
|
||||
//a.Logger.Errorln("PatchMgmnt:", err.Error())
|
||||
ret.Encode(err.Error())
|
||||
} else {
|
||||
ret.Encode("ok")
|
||||
|
|
@ -71,7 +85,6 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
success, err := tasks.CreateSchedTask(p.ScheduledTask)
|
||||
if err != nil {
|
||||
//a.Logger.Errorln(err.Error())
|
||||
ret.Encode(err.Error())
|
||||
} else if !success {
|
||||
ret.Encode("Something went wrong")
|
||||
|
|
@ -87,7 +100,6 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
err := tasks.DeleteSchedTask(p.ScheduledTask.Name)
|
||||
if err != nil {
|
||||
//a.Logger.Errorln(err.Error())
|
||||
ret.Encode(err.Error())
|
||||
} else {
|
||||
ret.Encode("ok")
|
||||
|
|
@ -99,8 +111,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func() {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
tasks := tasks.ListSchedTasks()
|
||||
//a.Logger.Debugln(tasks)
|
||||
tasks, _ := tasks.ListSchedTasks()
|
||||
ret.Encode(tasks)
|
||||
msg.Respond(resp)
|
||||
}()
|
||||
|
|
@ -110,8 +121,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
days, _ := strconv.Atoi(p.Data["days"])
|
||||
evtLog := events.GetEventLog(p.Data["logname"], days)
|
||||
//a.Logger.Debugln(evtLog)
|
||||
evtLog, _ := events.GetEventLog(p.Data["logname"], days)
|
||||
ret.Encode(evtLog)
|
||||
msg.Respond(resp)
|
||||
}(payload)
|
||||
|
|
@ -120,7 +130,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func() {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
procs := a.GetProcsRPC()
|
||||
procs := system.GetProcsRPC()
|
||||
//a.Logger.Debugln(procs)
|
||||
ret.Encode(procs)
|
||||
msg.Respond(resp)
|
||||
|
|
@ -130,10 +140,9 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func(p *NatsMsg) {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
err := KillProc(p.ProcPID)
|
||||
err := system.KillProc(p.ProcPID)
|
||||
if err != nil {
|
||||
ret.Encode(err.Error())
|
||||
//a.Logger.Debugln(err.Error())
|
||||
} else {
|
||||
ret.Encode("ok")
|
||||
}
|
||||
|
|
@ -143,13 +152,12 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
case "rawcmd":
|
||||
go func(p *NatsMsg) {
|
||||
var resp []byte
|
||||
var resultData rmm.RawCMDResp
|
||||
var resultData RawCMDResp
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
out, _ := CMDShell(p.Data["shell"], []string{}, p.Data["command"], p.Timeout, false)
|
||||
//a.Logger.Debugln(out)
|
||||
out, _ := system.CMDShell(p.Data["shell"], []string{}, p.Data["command"], p.Timeout, false)
|
||||
if out[1] != "" {
|
||||
ret.Encode(out[1])
|
||||
resultData.Results = out[1]
|
||||
|
|
@ -158,11 +166,11 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
resultData.Results = out[0]
|
||||
}
|
||||
default:
|
||||
opts := a.NewCMDOpts()
|
||||
opts := system.NewCMDOpts()
|
||||
opts.Shell = p.Data["shell"]
|
||||
opts.Command = p.Data["command"]
|
||||
opts.Timeout = time.Duration(p.Timeout)
|
||||
out := a.CmdV2(opts)
|
||||
out := system.CmdV2(opts)
|
||||
tmp := ""
|
||||
if len(out.Stdout) > 0 {
|
||||
tmp += out.Stdout
|
||||
|
|
@ -177,7 +185,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
|
||||
msg.Respond(resp)
|
||||
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)
|
||||
|
||||
|
|
@ -185,8 +193,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func() {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
svcs := a.GetServices()
|
||||
//a.Logger.Debugln(svcs)
|
||||
svcs, _, _ := services.GetServices()
|
||||
ret.Encode(svcs)
|
||||
msg.Respond(resp)
|
||||
}()
|
||||
|
|
@ -195,8 +202,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func(p *NatsMsg) {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
svc := a.GetServiceDetail(p.Data["name"])
|
||||
//a.Logger.Debugln(svc)
|
||||
svc := services.GetServiceDetail(p.Data["name"])
|
||||
ret.Encode(svc)
|
||||
msg.Respond(resp)
|
||||
}(payload)
|
||||
|
|
@ -205,8 +211,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func(p *NatsMsg) {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
retData := a.ControlService(p.Data["name"], p.Data["action"])
|
||||
//a.Logger.Debugln(retData)
|
||||
retData := services.ControlService(p.Data["name"], p.Data["action"])
|
||||
ret.Encode(retData)
|
||||
msg.Respond(resp)
|
||||
}(payload)
|
||||
|
|
@ -215,8 +220,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func(p *NatsMsg) {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
retData := a.EditService(p.Data["name"], p.Data["startType"])
|
||||
//a.Logger.Debugln(retData)
|
||||
retData := services.EditService(p.Data["name"], p.Data["startType"])
|
||||
ret.Encode(retData)
|
||||
msg.Respond(resp)
|
||||
}(payload)
|
||||
|
|
@ -225,15 +229,14 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func(p *NatsMsg) {
|
||||
var resp []byte
|
||||
var retData string
|
||||
var resultData rmm.RunScriptResp
|
||||
var resultData RunScriptResp
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
start := time.Now()
|
||||
stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||
stdout, stderr, retcode, err := system.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||
resultData.ExecTime = time.Since(start).Seconds()
|
||||
resultData.ID = p.ID
|
||||
|
||||
if err != nil {
|
||||
//a.Logger.Debugln(err)
|
||||
retData = err.Error()
|
||||
resultData.Retcode = 1
|
||||
resultData.Stderr = err.Error()
|
||||
|
|
@ -248,17 +251,17 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
msg.Respond(resp)
|
||||
if p.ID != 0 {
|
||||
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)
|
||||
|
||||
case "runscriptfull":
|
||||
go func(p *NatsMsg) {
|
||||
var resp []byte
|
||||
var retData rmm.RunScriptResp
|
||||
var retData RunScriptResp
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
start := time.Now()
|
||||
stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||
stdout, stderr, retcode, err := system.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||
|
||||
retData.ExecTime = time.Since(start).Seconds()
|
||||
if err != nil {
|
||||
|
|
@ -275,7 +278,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
msg.Respond(resp)
|
||||
if p.ID != 0 {
|
||||
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)
|
||||
|
||||
|
|
@ -286,8 +289,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
|
||||
switch p.Data["mode"] {
|
||||
case "mesh":
|
||||
//a.Logger.Debugln("Recovering mesh")
|
||||
a.RecoverMesh()
|
||||
mesh.RecoverMesh()
|
||||
}
|
||||
|
||||
ret.Encode("ok")
|
||||
|
|
@ -297,8 +299,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func() {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
sw := a.GetInstalledSoftware()
|
||||
//a.Logger.Debugln(sw)
|
||||
sw, _ := software.GetInstalledSoftware()
|
||||
ret.Encode(sw)
|
||||
msg.Respond(resp)
|
||||
}()
|
||||
|
|
@ -311,24 +312,21 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
ret.Encode("ok")
|
||||
msg.Respond(resp)
|
||||
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 {
|
||||
opts := a.NewCMDOpts()
|
||||
opts := system.NewCMDOpts()
|
||||
opts.Command = "reboot"
|
||||
a.CmdV2(opts)
|
||||
system.CmdV2(opts)
|
||||
}
|
||||
}()
|
||||
case "needsreboot":
|
||||
go func() {
|
||||
//a.Logger.Debugln("Checking if reboot needed")
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
out, err := a.SystemRebootRequired()
|
||||
out, err := system.SystemRebootRequired()
|
||||
if err == nil {
|
||||
//a.Logger.Debugln("Reboot needed:", out)
|
||||
ret.Encode(out)
|
||||
} else {
|
||||
//a.Logger.Debugln("Error checking if reboot needed:", err)
|
||||
ret.Encode(false)
|
||||
}
|
||||
msg.Respond(resp)
|
||||
|
|
@ -337,26 +335,22 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
go func() {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
//a.Logger.Debugln("Getting sysinfo with WMI")
|
||||
modes := []string{"agent-agentinfo", "agent-disks", "agent-wmi", "agent-publicip"}
|
||||
for _, m := range modes {
|
||||
a.NatsMessage(nc, m)
|
||||
service.NatsMessage(version, nc, m)
|
||||
}
|
||||
ret.Encode("ok")
|
||||
msg.Respond(resp)
|
||||
}()
|
||||
case "wmi":
|
||||
go func() {
|
||||
//a.Logger.Debugln("Sending WMI")
|
||||
a.NatsMessage(nc, "agent-wmi")
|
||||
service.NatsMessage(version, nc, "agent-wmi")
|
||||
}()
|
||||
case "cpuloadavg":
|
||||
go func() {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
//a.Logger.Debugln("Getting CPU Load Avg")
|
||||
loadAvg := a.GetCPULoadAvg()
|
||||
//a.Logger.Debugln("CPU Load Avg:", loadAvg)
|
||||
loadAvg := system.GetCPULoadAvg()
|
||||
ret.Encode(loadAvg)
|
||||
msg.Respond(resp)
|
||||
}()
|
||||
|
|
@ -365,54 +359,49 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
if runtime.GOOS == "windows" {
|
||||
if a.ChecksRunning() {
|
||||
if checks.ChecksRunning() {
|
||||
ret.Encode("busy")
|
||||
msg.Respond(resp)
|
||||
//a.Logger.Debugln("Checks are already running, please wait")
|
||||
} else {
|
||||
ret.Encode("ok")
|
||||
msg.Respond(resp)
|
||||
//a.Logger.Debugln("Running checks")
|
||||
_, checkerr := CMD(a.EXE, []string{"-m", "runchecks"}, 600, false)
|
||||
_, checkerr := system.CMD(system.GetProgramEXE(), []string{"-m", "runchecks"}, 600, false)
|
||||
if checkerr != nil {
|
||||
//a.Logger.Errorln("RPC RunChecks", checkerr)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Encode("ok")
|
||||
msg.Respond(resp)
|
||||
//a.Logger.Debugln("Running checks")
|
||||
a.RunChecks(true)
|
||||
checks.RunChecks(config.AgentID, true)
|
||||
}
|
||||
|
||||
}()
|
||||
case "runtask":
|
||||
go func(p *NatsMsg) {
|
||||
//a.Logger.Debugln("Running task")
|
||||
a.RunTask(p.TaskPK)
|
||||
ttasks.RunTask(p.TaskPK)
|
||||
}(payload)
|
||||
|
||||
case "publicip":
|
||||
go func() {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
ret.Encode(a.PublicIP())
|
||||
ret.Encode(network.PublicIP(config.Proxy))
|
||||
msg.Respond(resp)
|
||||
}()
|
||||
case "installpython":
|
||||
go a.GetPython(true)
|
||||
go shared.GetPython(true)
|
||||
case "installchoco":
|
||||
go a.InstallChoco()
|
||||
go choco.InstallChoco()
|
||||
case "installwithchoco":
|
||||
go func(p *NatsMsg) {
|
||||
var resp []byte
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
ret.Encode("ok")
|
||||
msg.Respond(resp)
|
||||
out, _ := a.InstallWithChoco(p.ChocoProgName)
|
||||
out, _ := choco.InstallWithChoco(p.ChocoProgName)
|
||||
results := map[string]string{"results": out}
|
||||
url := fmt.Sprintf("/api/v3/%d/chocoresult/", p.PendingActionPK)
|
||||
a.rClient.R().SetBody(results).Patch(url)
|
||||
api.Patch(results, url)
|
||||
}(payload)
|
||||
case "getwinupdates":
|
||||
go func() {
|
||||
|
|
@ -421,7 +410,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
} else {
|
||||
//a.Logger.Debugln("Checking for windows updates")
|
||||
defer atomic.StoreUint32(&getWinUpdateLocker, 0)
|
||||
a.GetWinUpdates()
|
||||
patching.GetUpdates()
|
||||
}
|
||||
}()
|
||||
case "installwinupdates":
|
||||
|
|
@ -431,7 +420,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
} else {
|
||||
//a.Logger.Debugln("Installing windows updates", p.UpdateGUIDs)
|
||||
defer atomic.StoreUint32(&installWinUpdateLocker, 0)
|
||||
a.InstallUpdates(p.UpdateGUIDs)
|
||||
patching.InstallUpdates(p.UpdateGUIDs)
|
||||
}
|
||||
}(payload)
|
||||
case "agentupdate":
|
||||
|
|
@ -445,7 +434,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
} else {
|
||||
ret.Encode("ok")
|
||||
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)
|
||||
nc.Flush()
|
||||
nc.Close()
|
||||
|
|
@ -459,7 +448,7 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
ret.Encode("ok")
|
||||
msg.Respond(resp)
|
||||
a.AgentUninstall(p.Code)
|
||||
tactical.AgentUninstall(p.Code)
|
||||
nc.Flush()
|
||||
nc.Close()
|
||||
os.Exit(0)
|
||||
|
|
@ -475,3 +464,8 @@ func RunRPC(a *rmm.AgentConfig) {
|
|||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func Start(version string, _ ksvc.Service) error {
|
||||
go RunRPC(version)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,4 +17,16 @@ type NatsMsg struct {
|
|||
PatchMgmt bool `json:"patch_mgmt"`
|
||||
ID int `json:"id"`
|
||||
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"
|
||||
|
||||
"github.com/amidaware/rmmagent/agent/disk"
|
||||
"github.com/amidaware/rmmagent/agent/network"
|
||||
"github.com/amidaware/rmmagent/agent/services"
|
||||
"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/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/wmi"
|
||||
"github.com/nats-io/nats.go"
|
||||
"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"}
|
||||
|
||||
func RunAsService(agentID string, version string) {
|
||||
func RunAsService(version string) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go AgentSvc(version)
|
||||
go checks.CheckRunner(agentID)
|
||||
go checks.CheckRunner(version)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func AgentSvc(version string) {
|
||||
config := tactical.NewAgentConfig()
|
||||
go tactical.GetPython(false)
|
||||
config := config.NewAgentConfig()
|
||||
go shared.GetPython(false)
|
||||
utils.CreateTRMMTempDir()
|
||||
tactical.RunMigrations()
|
||||
|
||||
shared.RunMigrations()
|
||||
sleepDelay := utils.RandRange(14, 22)
|
||||
//a.Logger.Debugf("AgentSvc() sleeping for %v seconds", sleepDelay)
|
||||
time.Sleep(time.Duration(sleepDelay) * time.Second)
|
||||
|
||||
opts := SetupNatsOptions(config.AgentID, config.Token)
|
||||
opts := SetupNatsOptions()
|
||||
server := fmt.Sprintf("tls://%s:4222", config.APIURL)
|
||||
nc, err := nats.Connect(server, opts...)
|
||||
if err != nil {
|
||||
//a.Logger.Fatalln("AgentSvc() nats.Connect()", err)
|
||||
}
|
||||
|
||||
for _, s := range natsCheckin {
|
||||
NatsMessage(config.AgentID, version, nc, s)
|
||||
NatsMessage(version, nc, s)
|
||||
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)
|
||||
AgentStartup(config.AgentID)
|
||||
tactical.SendSoftware()
|
||||
shared.SendSoftware()
|
||||
|
||||
checkInHelloTicker := time.NewTicker(time.Duration(utils.RandRange(30, 60)) * time.Second)
|
||||
checkInAgentInfoTicker := time.NewTicker(time.Duration(utils.RandRange(200, 400)) * time.Second)
|
||||
|
|
@ -67,29 +67,30 @@ func AgentSvc(version string) {
|
|||
for {
|
||||
select {
|
||||
case <-checkInHelloTicker.C:
|
||||
NatsMessage(config.AgentID, version, nc, "agent-hello")
|
||||
NatsMessage(version, nc, "agent-hello")
|
||||
case <-checkInAgentInfoTicker.C:
|
||||
NatsMessage(config.AgentID, version, nc, "agent-agentinfo")
|
||||
NatsMessage(version, nc, "agent-agentinfo")
|
||||
case <-checkInWinSvcTicker.C:
|
||||
NatsMessage(config.AgentID, version, nc, "agent-winsvc")
|
||||
NatsMessage(version, nc, "agent-winsvc")
|
||||
case <-checkInPubIPTicker.C:
|
||||
NatsMessage(config.AgentID, version, nc, "agent-publicip")
|
||||
NatsMessage(version, nc, "agent-publicip")
|
||||
case <-checkInDisksTicker.C:
|
||||
NatsMessage(config.AgentID, version, nc, "agent-disks")
|
||||
NatsMessage(version, nc, "agent-disks")
|
||||
case <-checkInSWTicker.C:
|
||||
tactical.SendSoftware()
|
||||
shared.SendSoftware()
|
||||
case <-checkInWMITicker.C:
|
||||
NatsMessage(config.AgentID, version, nc, "agent-wmi")
|
||||
NatsMessage(version, nc, "agent-wmi")
|
||||
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 = 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.RetryOnFailedConnect(true))
|
||||
opts = append(opts, nats.MaxReconnects(-1))
|
||||
|
|
@ -97,21 +98,23 @@ func SetupNatsOptions(agentID string, token string) []nats.Option {
|
|||
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 payload interface{}
|
||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||
|
||||
switch mode {
|
||||
case "agent-hello":
|
||||
payload = trmm.CheckInNats{
|
||||
Agentid: agentID,
|
||||
payload = CheckInNats{
|
||||
Agentid: config.AgentID,
|
||||
Version: version,
|
||||
}
|
||||
case "agent-winsvc":
|
||||
payload = trmm.WinSvcNats{
|
||||
Agentid: agentID,
|
||||
WinSvcs: services.GetServices(),
|
||||
svcs, _, _ := services.GetServices()
|
||||
payload = WinSvcNats{
|
||||
Agentid: config.AgentID,
|
||||
WinSvcs: svcs,
|
||||
}
|
||||
case "agent-agentinfo":
|
||||
osinfo := system.OsString()
|
||||
|
|
@ -119,8 +122,8 @@ func NatsMessage(agentID string, version string, nc *nats.Conn, mode string) {
|
|||
if err != nil {
|
||||
reboot = false
|
||||
}
|
||||
payload = trmm.AgentInfoNats{
|
||||
Agentid: agentID,
|
||||
payload = AgentInfoNats{
|
||||
Agentid: config.AgentID,
|
||||
Username: system.LoggedOnUser(),
|
||||
Hostname: system.GetHostname(),
|
||||
OS: osinfo,
|
||||
|
|
@ -131,48 +134,47 @@ func NatsMessage(agentID string, version string, nc *nats.Conn, mode string) {
|
|||
GoArch: runtime.GOARCH,
|
||||
}
|
||||
case "agent-wmi":
|
||||
payload = trmm.WinWMINats{
|
||||
Agentid: agentID,
|
||||
WMI: wmi.GetWMIInfo(),
|
||||
wmiinfo, _ := wmi.GetWMIInfo()
|
||||
payload = WinWMINats{
|
||||
Agentid: config.AgentID,
|
||||
WMI: wmiinfo,
|
||||
}
|
||||
case "agent-disks":
|
||||
payload = trmm.WinDisksNats{
|
||||
Agentid: agentID,
|
||||
Disks: disk.GetDisks(),
|
||||
disks, _ := disk.GetDisks()
|
||||
payload = WinDisksNats{
|
||||
Agentid: config.AgentID,
|
||||
Disks: disks,
|
||||
}
|
||||
case "agent-publicip":
|
||||
payload = trmm.PublicIPNats{
|
||||
Agentid: agentID,
|
||||
PublicIP: a.PublicIP(),
|
||||
payload = PublicIPNats{
|
||||
Agentid: config.AgentID,
|
||||
PublicIP: network.PublicIP(config.Proxy),
|
||||
}
|
||||
}
|
||||
|
||||
//a.Logger.Debugln(mode, payload)
|
||||
ret.Encode(payload)
|
||||
nc.PublishRequest(a.AgentID, mode, resp)
|
||||
nc.PublishRequest(config.AgentID, mode, resp)
|
||||
}
|
||||
|
||||
func DoNatsCheckIn() {
|
||||
func DoNatsCheckIn(version string) {
|
||||
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...)
|
||||
if err != nil {
|
||||
//a.Logger.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, s := range natsCheckin {
|
||||
time.Sleep(time.Duration(utils.RandRange(100, 400)) * time.Millisecond)
|
||||
NatsMessage(nc, s)
|
||||
NatsMessage(version, nc, s)
|
||||
}
|
||||
|
||||
nc.Close()
|
||||
}
|
||||
|
||||
func AgentStartup(agentID string) {
|
||||
url := "/api/v3/checkin/"
|
||||
func AgentStartup(agentID string) error {
|
||||
payload := map[string]interface{}{"agent_id": agentID}
|
||||
_, err := tactical.PostRequest(url, payload, 15)
|
||||
if err != nil {
|
||||
//a.Logger.Debugln("AgentStartup()", err)
|
||||
}
|
||||
err := api.PostPayload(payload, "/api/v3/checkin/")
|
||||
return 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -14,13 +13,10 @@ import (
|
|||
"github.com/amidaware/rmmagent/agent/services"
|
||||
"github.com/amidaware/rmmagent/agent/system"
|
||||
"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/utils"
|
||||
rmm "github.com/amidaware/rmmagent/shared"
|
||||
"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/registry"
|
||||
)
|
||||
|
|
@ -37,9 +33,6 @@ func AgentUpdate(url, inno, version string) {
|
|||
system.KillHungUpdates()
|
||||
CleanupAgentUpdates()
|
||||
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()
|
||||
rClient := resty.New()
|
||||
rClient.SetCloseConnection(true)
|
||||
|
|
@ -51,20 +44,17 @@ func AgentUpdate(url, inno, version string) {
|
|||
|
||||
r, err := rClient.R().SetOutput(updater).Get(url)
|
||||
if err != nil {
|
||||
//a.Logger.Errorln(err)
|
||||
system.CMD("net", []string{"start", services.WinSvcName}, 10, false)
|
||||
return
|
||||
}
|
||||
|
||||
if r.IsError() {
|
||||
//a.Logger.Errorln("Download failed with status code", r.StatusCode())
|
||||
system.CMD("net", []string{"start", services.WinSvcName}, 10, false)
|
||||
return
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "tacticalrmm")
|
||||
if err != nil {
|
||||
//a.Logger.Errorln("Agentupdate create tempdir:", err)
|
||||
system.CMD("net", []string{"start", services.WinSvcName}, 10, false)
|
||||
return
|
||||
}
|
||||
|
|
@ -84,7 +74,6 @@ func CleanupAgentUpdates() {
|
|||
pd := filepath.Join(os.Getenv("ProgramFiles"), system.ProgFilesName)
|
||||
cderr := os.Chdir(pd)
|
||||
if cderr != nil {
|
||||
//a.Logger.Errorln(cderr)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +86,6 @@ func CleanupAgentUpdates() {
|
|||
|
||||
cderr = os.Chdir(os.Getenv("TMP"))
|
||||
if cderr != nil {
|
||||
//a.Logger.Errorln(cderr)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -135,122 +123,3 @@ func GetUninstallExe() string {
|
|||
|
||||
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 (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/amidaware/rmmagent/agent"
|
||||
"github.com/kardianos/service"
|
||||
"github.com/sirupsen/logrus"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"github.com/amidaware/rmmagent/agent"
|
||||
"github.com/kardianos/service"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "2.0.4"
|
||||
version = "development"
|
||||
log = logrus.New()
|
||||
logFile *os.File
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue