testing and generate
This commit is contained in:
parent
9743d7ee22
commit
3841aee4b2
24 changed files with 1087 additions and 610 deletions
39
agent/tactical/install/install_unix.go
Normal file
39
agent/tactical/install/install_unix.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package install
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
"log"
|
||||
)
|
||||
|
||||
func CheckExistingAndRemove(silent bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateAgentConfig(baseurl, agentid, apiurl, token, agentpk, cert, proxy, meshdir string) {
|
||||
viper.SetConfigType("json")
|
||||
viper.Set("baseurl", baseurl)
|
||||
viper.Set("agentid", agentid)
|
||||
viper.Set("apiurl", apiurl)
|
||||
viper.Set("token", token)
|
||||
viper.Set("agentpk", agentpk)
|
||||
viper.Set("cert", cert)
|
||||
viper.Set("proxy", proxy)
|
||||
viper.Set("meshdir", meshdir)
|
||||
viper.SetConfigPermissions(0660)
|
||||
configLocation := "/etc/tacticalagent"
|
||||
|
||||
err := viper.SafeWriteConfigAs(configLocation)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln("createAgentConfig", err)
|
||||
}
|
||||
}
|
||||
|
||||
func DisableSleepHibernate() {}
|
||||
|
||||
func EnablePing() {}
|
||||
|
||||
func EnableRDP() {}
|
||||
349
agent/tactical/install/install_windows.go
Normal file
349
agent/tactical/install/install_windows.go
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
package install
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/amidaware/rmmagent/agent/patching"
|
||||
"github.com/amidaware/rmmagent/agent/services"
|
||||
"github.com/amidaware/rmmagent/agent/system"
|
||||
"github.com/amidaware/rmmagent/agent/tactical"
|
||||
"github.com/amidaware/rmmagent/agent/tactical/mesh"
|
||||
"github.com/amidaware/rmmagent/agent/tactical/service"
|
||||
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
||||
"github.com/amidaware/rmmagent/agent/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/gonutz/w32/v2"
|
||||
ksvc "github.com/kardianos/service"
|
||||
"github.com/shirou/gopsutil/host"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
const winSvcName = "tacticalrmm"
|
||||
|
||||
func Install(i *Installer) error {
|
||||
CheckExistingAndRemove(i.Silent)
|
||||
i.Headers = map[string]string{
|
||||
"content-type": "application/json",
|
||||
"Authorization": fmt.Sprintf("Token %s", i.Token),
|
||||
}
|
||||
|
||||
AgentID := GenerateAgentID()
|
||||
u, err := url.Parse(i.RMM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if u.Scheme != "https" && u.Scheme != "http" {
|
||||
return errors.New("Invalid URL (must contain https or http)")
|
||||
}
|
||||
|
||||
// will match either ipv4 , or ipv4:port
|
||||
var ipPort = regexp.MustCompile(`[0-9]+(?:\.[0-9]+){3}(:[0-9]+)?`)
|
||||
|
||||
// if ipv4:port, strip the port to get ip for salt master
|
||||
if ipPort.MatchString(u.Host) && strings.Contains(u.Host, ":") {
|
||||
i.SaltMaster = strings.Split(u.Host, ":")[0]
|
||||
} else if strings.Contains(u.Host, ":") {
|
||||
i.SaltMaster = strings.Split(u.Host, ":")[0]
|
||||
} else {
|
||||
i.SaltMaster = u.Host
|
||||
}
|
||||
|
||||
terr := utils.TestTCP(fmt.Sprintf("%s:4222", i.SaltMaster))
|
||||
if terr != nil {
|
||||
return fmt.Errorf("ERROR: Either port 4222 TCP is not open on your RMM, or nats.service is not running.\n\n%s", terr.Error())
|
||||
}
|
||||
|
||||
baseURL := u.Scheme + "://" + u.Host
|
||||
iClient := resty.New()
|
||||
iClient.SetCloseConnection(true)
|
||||
iClient.SetTimeout(15 * time.Second)
|
||||
iClient.SetHeaders(i.Headers)
|
||||
|
||||
// set proxy if applicable
|
||||
if len(i.Proxy) > 0 {
|
||||
iClient.SetProxy(i.Proxy)
|
||||
}
|
||||
|
||||
creds, cerr := iClient.R().Get(fmt.Sprintf("%s/api/v3/installer/", baseURL))
|
||||
if cerr != nil {
|
||||
return cerr
|
||||
}
|
||||
|
||||
if creds.StatusCode() == 401 {
|
||||
return errors.New("Installer token has expired. Please generate a new one.")
|
||||
}
|
||||
|
||||
verPayload := map[string]string{"version": i.Version}
|
||||
iVersion, ierr := iClient.R().SetBody(verPayload).Post(fmt.Sprintf("%s/api/v3/installer/", baseURL))
|
||||
if ierr != nil {
|
||||
return ierr
|
||||
}
|
||||
|
||||
if iVersion.StatusCode() != 200 {
|
||||
return errors.New(DjangoStringResp(iVersion.String()))
|
||||
}
|
||||
|
||||
rClient := resty.New()
|
||||
rClient.SetCloseConnection(true)
|
||||
rClient.SetTimeout(i.Timeout * time.Second)
|
||||
// set rest knox headers
|
||||
rClient.SetHeaders(i.Headers)
|
||||
|
||||
// set local cert if applicable
|
||||
if len(i.Cert) > 0 {
|
||||
if !utils.FileExists(i.Cert) {
|
||||
return fmt.Errorf("%s does not exist", i.Cert)
|
||||
}
|
||||
|
||||
rClient.SetRootCertificate(i.Cert)
|
||||
}
|
||||
|
||||
if len(i.Proxy) > 0 {
|
||||
rClient.SetProxy(i.Proxy)
|
||||
}
|
||||
|
||||
var arch string
|
||||
switch runtime.GOARCH {
|
||||
case "x86_64":
|
||||
arch = "64"
|
||||
case "x86":
|
||||
arch = "32"
|
||||
}
|
||||
|
||||
var installerMeshSystemBin string
|
||||
if len(i.MeshDir) > 0 {
|
||||
installerMeshSystemBin = filepath.Join(i.MeshDir, "MeshAgent.exe")
|
||||
} else {
|
||||
installerMeshSystemBin = mesh.GetMeshBinLocation()
|
||||
}
|
||||
|
||||
var meshNodeID string
|
||||
|
||||
if runtime.GOOS == "windows" && !i.NoMesh {
|
||||
meshPath := filepath.Join(shared.GetProgramDirectory(), "meshagent.exe")
|
||||
if i.LocalMesh == "" {
|
||||
payload := map[string]string{"arch": arch, "plat": runtime.GOOS}
|
||||
r, err := rClient.R().SetBody(payload).SetOutput(meshPath).Post(fmt.Sprintf("%s/api/v3/meshexe/", baseURL))
|
||||
if err != nil {
|
||||
}
|
||||
if r.StatusCode() != 200 {
|
||||
}
|
||||
} else {
|
||||
err := copyFile(i.LocalMesh, meshPath)
|
||||
if err != nil {
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
meshNodeID, err = mesh.InstallMesh(meshPath, installerMeshSystemBin, i.Proxy)
|
||||
if err != nil {
|
||||
}
|
||||
}
|
||||
|
||||
if len(i.MeshNodeID) > 0 {
|
||||
meshNodeID = i.MeshNodeID
|
||||
}
|
||||
|
||||
// add agent
|
||||
host, _ := host.Info()
|
||||
agentPayload := map[string]interface{}{
|
||||
"agent_id": AgentID,
|
||||
"hostname": host.Hostname,
|
||||
"site": i.SiteID,
|
||||
"monitoring_type": i.AgentType,
|
||||
"mesh_node_id": meshNodeID,
|
||||
"description": i.Description,
|
||||
"goarch": runtime.GOARCH,
|
||||
"plat": runtime.GOOS,
|
||||
}
|
||||
|
||||
r, err := rClient.R().SetBody(agentPayload).SetResult(&NewAgentResp{}).Post(fmt.Sprintf("%s/api/v3/newagent/", baseURL))
|
||||
if err != nil {
|
||||
}
|
||||
if r.StatusCode() != 200 {
|
||||
}
|
||||
|
||||
agentPK := r.Result().(*NewAgentResp).AgentPK
|
||||
agentToken := r.Result().(*NewAgentResp).Token
|
||||
CreateAgentConfig(baseURL, AgentID, i.SaltMaster, agentToken, strconv.Itoa(agentPK), i.Cert, i.Proxy, i.MeshDir)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
// check in once
|
||||
service.DoNatsCheckIn(i.Version)
|
||||
service.SendSoftware()
|
||||
utils.CreateTRMMTempDir()
|
||||
patching.PatchMgmnt(true)
|
||||
|
||||
svcConf := &ksvc.Config{
|
||||
Executable: shared.GetProgramBin(),
|
||||
Name: winSvcName,
|
||||
DisplayName: "TacticalRMM Agent Service",
|
||||
Arguments: []string{"-m", "svc"},
|
||||
Description: "TacticalRMM Agent Service",
|
||||
Option: ksvc.KeyValue{
|
||||
"StartType": "automatic",
|
||||
"OnFailure": "restart",
|
||||
"OnFailureDelayDuration": "5s",
|
||||
"OnFailureResetPeriod": 10,
|
||||
},
|
||||
}
|
||||
|
||||
err = service.InstallService(winSvcName, service.IService{}, svcConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
out := services.ControlService(winSvcName, "start")
|
||||
if !out.Success {
|
||||
return errors.New(out.ErrorMsg)
|
||||
}
|
||||
|
||||
system.AddDefenderExclusions()
|
||||
if i.Power {
|
||||
system.DisableSleepHibernate()
|
||||
}
|
||||
|
||||
if i.Ping {
|
||||
system.EnablePing()
|
||||
}
|
||||
|
||||
if i.RDP {
|
||||
system.EnableRDP()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateAgentID creates and returns a unique agent id
|
||||
func GenerateAgentID() string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
b := make([]rune, 40)
|
||||
for i := range b {
|
||||
b[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// DjangoStringResp removes double quotes from django rest api resp
|
||||
func DjangoStringResp(resp string) string {
|
||||
return strings.Trim(resp, `"`)
|
||||
}
|
||||
|
||||
func CreateAgentConfig(baseurl, agentid, apiurl, token, agentpk, cert, proxy, meshdir string) error {
|
||||
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer k.Close()
|
||||
err = k.SetStringValue("BaseURL", baseurl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = k.SetStringValue("AgentID", agentid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = k.SetStringValue("ApiURL", apiurl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = k.SetStringValue("Token", token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = k.SetStringValue("AgentPK", agentpk)
|
||||
if err != nil {
|
||||
}
|
||||
|
||||
if len(cert) > 0 {
|
||||
err = k.SetStringValue("Cert", cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(proxy) > 0 {
|
||||
err = k.SetStringValue("Proxy", proxy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(meshdir) > 0 {
|
||||
err = k.SetStringValue("MeshDir", meshdir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckExistingAndRemove(silent bool) {
|
||||
hasReg := false
|
||||
_, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
||||
|
||||
if err == nil {
|
||||
hasReg = true
|
||||
}
|
||||
if hasReg {
|
||||
tacUninst := filepath.Join(shared.GetProgramDirectory(), tactical.GetUninstallExe())
|
||||
tacUninstArgs := [2]string{tacUninst, "/VERYSILENT"}
|
||||
|
||||
window := w32.GetForegroundWindow()
|
||||
if !silent && window != 0 {
|
||||
var handle w32.HWND
|
||||
msg := "Existing installation found\nClick OK to remove, then re-run the installer.\nClick Cancel to abort."
|
||||
action := w32.MessageBox(handle, msg, "Tactical RMM", w32.MB_OKCANCEL|w32.MB_ICONWARNING)
|
||||
if action == w32.IDOK {
|
||||
tactical.AgentUninstall("foo")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Existing installation found and must be removed before attempting to reinstall.")
|
||||
fmt.Println("Run the following command to uninstall, and then re-run this installer.")
|
||||
fmt.Printf(`"%s" %s `, tacUninstArgs[0], tacUninstArgs[1])
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
31
agent/tactical/install/structs.go
Normal file
31
agent/tactical/install/structs.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package install
|
||||
|
||||
import "time"
|
||||
|
||||
type Installer struct {
|
||||
Headers map[string]string
|
||||
RMM string
|
||||
ClientID int
|
||||
SiteID int
|
||||
Description string
|
||||
AgentType string
|
||||
Power bool
|
||||
RDP bool
|
||||
Ping bool
|
||||
Token string
|
||||
LocalMesh string
|
||||
Cert string
|
||||
Proxy string
|
||||
Timeout time.Duration
|
||||
SaltMaster string
|
||||
Silent bool
|
||||
NoMesh bool
|
||||
MeshDir string
|
||||
MeshNodeID string
|
||||
Version string
|
||||
}
|
||||
|
||||
type NewAgentResp struct {
|
||||
AgentPK int `json:"pk"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue