From f5e2e01986863f8f9f86cc1f68a97b8f2d703821 Mon Sep 17 00:00:00 2001 From: redanthrax Date: Fri, 24 Jun 2022 15:58:34 -0700 Subject: [PATCH] splitting install - adding mesh support --- agent/agent_unix.go | 48 ++++++- agent/install.go | 228 -------------------------------- agent/install_darwin.go | 276 +++++++++++++++++++++++++++++++++++++++ agent/install_unix.go | 196 +++++++++++++++++++++++++++ agent/install_windows.go | 216 ++++++++++++++++++++++++++++++ 5 files changed, 734 insertions(+), 230 deletions(-) create mode 100644 agent/install_darwin.go diff --git a/agent/agent_unix.go b/agent/agent_unix.go index 12d96a8..7fc4553 100644 --- a/agent/agent_unix.go +++ b/agent/agent_unix.go @@ -478,8 +478,52 @@ func (a *Agent) GetWinUpdates() {} func (a *Agent) InstallUpdates(guids []string) {} -func (a *Agent) installMesh(meshbin, exe, proxy string) (string, error) { - return "not implemented", nil +func (a *Agent) installMesh(meshbin, bin, 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 := 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 := CMD(bin, []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 = 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 CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool) (output [2]string, e error) { diff --git a/agent/install.go b/agent/install.go index 7086514..f7ff56e 100644 --- a/agent/install.go +++ b/agent/install.go @@ -12,19 +12,9 @@ https://license.tacticalrmm.com package agent import ( - "fmt" "io" - "net/url" "os" - "path/filepath" - "regexp" - "runtime" - "strconv" - "strings" "time" - - "github.com/go-resty/resty/v2" - trmm "github.com/wh1te909/trmm-shared" ) type Installer struct { @@ -49,225 +39,7 @@ type Installer struct { MeshNodeID string } -func (a *Agent) Install(i *Installer) { - a.checkExistingAndRemove(i.Silent) - i.Headers = map[string]string{ - "content-type": "application/json", - "Authorization": fmt.Sprintf("Token %s", i.Token), - } - a.AgentID = GenerateAgentID() - a.Logger.Debugln("Agent ID:", a.AgentID) - - u, err := url.Parse(i.RMM) - if err != nil { - a.installerMsg(err.Error(), "error", i.Silent) - } - - if u.Scheme != "https" && u.Scheme != "http" { - a.installerMsg("Invalid URL (must contain https or http)", "error", i.Silent) - } - - // 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 - } - - a.Logger.Debugln("API:", i.SaltMaster) - - terr := TestTCP(fmt.Sprintf("%s:4222", i.SaltMaster)) - if terr != nil { - a.installerMsg(fmt.Sprintf("ERROR: Either port 4222 TCP is not open on your RMM, or nats.service is not running.\n\n%s", terr.Error()), "error", i.Silent) - } - - baseURL := u.Scheme + "://" + u.Host - a.Logger.Debugln("Base URL:", baseURL) - - iClient := resty.New() - iClient.SetCloseConnection(true) - iClient.SetTimeout(15 * time.Second) - iClient.SetDebug(a.Debug) - iClient.SetHeaders(i.Headers) - - // set proxy if applicable - if len(i.Proxy) > 0 { - a.Logger.Infoln("Using proxy:", i.Proxy) - iClient.SetProxy(i.Proxy) - } - - creds, cerr := iClient.R().Get(fmt.Sprintf("%s/api/v3/installer/", baseURL)) - if cerr != nil { - a.installerMsg(cerr.Error(), "error", i.Silent) - } - if creds.StatusCode() == 401 { - a.installerMsg("Installer token has expired. Please generate a new one.", "error", i.Silent) - } - - verPayload := map[string]string{"version": a.Version} - iVersion, ierr := iClient.R().SetBody(verPayload).Post(fmt.Sprintf("%s/api/v3/installer/", baseURL)) - if ierr != nil { - a.installerMsg(ierr.Error(), "error", i.Silent) - } - if iVersion.StatusCode() != 200 { - a.installerMsg(DjangoStringResp(iVersion.String()), "error", i.Silent) - } - - rClient := resty.New() - rClient.SetCloseConnection(true) - rClient.SetTimeout(i.Timeout * time.Second) - rClient.SetDebug(a.Debug) - // set rest knox headers - rClient.SetHeaders(i.Headers) - - // set local cert if applicable - if len(i.Cert) > 0 { - if !trmm.FileExists(i.Cert) { - a.installerMsg(fmt.Sprintf("%s does not exist", i.Cert), "error", i.Silent) - } - rClient.SetRootCertificate(i.Cert) - } - - if len(i.Proxy) > 0 { - rClient.SetProxy(i.Proxy) - } - - var installerMeshSystemEXE string - if len(i.MeshDir) > 0 { - installerMeshSystemEXE = filepath.Join(i.MeshDir, "MeshAgent.exe") - } else { - installerMeshSystemEXE = a.MeshSystemBin - } - - var meshNodeID string - - if runtime.GOOS == "windows" && !i.NoMesh { - mesh := filepath.Join(a.ProgramDir, a.MeshInstaller) - if i.LocalMesh == "" { - a.Logger.Infoln("Downloading mesh agent...") - payload := map[string]string{"goarch": a.GoArch, "plat": a.Platform} - r, err := rClient.R().SetBody(payload).SetOutput(mesh).Post(fmt.Sprintf("%s/api/v3/meshexe/", baseURL)) - if err != nil { - a.installerMsg(fmt.Sprintf("Failed to download mesh agent: %s", err.Error()), "error", i.Silent) - } - if r.StatusCode() != 200 { - a.installerMsg(fmt.Sprintf("Unable to download the mesh agent from the RMM. %s", r.String()), "error", i.Silent) - } - } else { - err := copyFile(i.LocalMesh, mesh) - if err != nil { - a.installerMsg(err.Error(), "error", i.Silent) - } - } - - a.Logger.Infoln("Installing mesh agent...") - a.Logger.Debugln("Mesh agent:", mesh) - time.Sleep(1 * time.Second) - - meshNodeID, err = a.installMesh(mesh, installerMeshSystemEXE, i.Proxy) - if err != nil { - a.installerMsg(fmt.Sprintf("Failed to install mesh agent: %s", err.Error()), "error", i.Silent) - } - } - - if len(i.MeshNodeID) > 0 { - meshNodeID = i.MeshNodeID - } - - a.Logger.Infoln("Adding agent to dashboard") - // add agent - type NewAgentResp struct { - AgentPK int `json:"pk"` - Token string `json:"token"` - } - agentPayload := map[string]interface{}{ - "agent_id": a.AgentID, - "hostname": a.Hostname, - "site": i.SiteID, - "monitoring_type": i.AgentType, - "mesh_node_id": meshNodeID, - "description": i.Description, - "goarch": a.GoArch, - "plat": a.Platform, - } - - r, err := rClient.R().SetBody(agentPayload).SetResult(&NewAgentResp{}).Post(fmt.Sprintf("%s/api/v3/newagent/", baseURL)) - if err != nil { - a.installerMsg(err.Error(), "error", i.Silent) - } - if r.StatusCode() != 200 { - a.installerMsg(r.String(), "error", i.Silent) - } - - agentPK := r.Result().(*NewAgentResp).AgentPK - agentToken := r.Result().(*NewAgentResp).Token - - a.Logger.Debugln("Agent token:", agentToken) - a.Logger.Debugln("Agent PK:", agentPK) - - createAgentConfig(baseURL, a.AgentID, i.SaltMaster, agentToken, strconv.Itoa(agentPK), i.Cert, i.Proxy, i.MeshDir) - time.Sleep(1 * time.Second) - // refresh our agent with new values - a = New(a.Logger, a.Version) - a.Logger.Debugf("%+v\n", a) - - // set new headers, no longer knox auth...use agent auth - rClient.SetHeaders(a.Headers) - - time.Sleep(3 * time.Second) - // check in once - a.DoNatsCheckIn() - - if runtime.GOOS == "windows" { - // send software api - a.SendSoftware() - - a.Logger.Debugln("Creating temp dir") - a.CreateTRMMTempDir() - - a.Logger.Debugln("Disabling automatic windows updates") - a.PatchMgmnt(true) - - a.Logger.Infoln("Installing service...") - err := a.InstallService() - if err != nil { - a.installerMsg(err.Error(), "error", i.Silent) - } - - time.Sleep(1 * time.Second) - a.Logger.Infoln("Starting service...") - out := a.ControlService(winSvcName, "start") - if !out.Success { - a.installerMsg(out.ErrorMsg, "error", i.Silent) - } - - a.Logger.Infoln("Adding windows defender exclusions") - a.addDefenderExlusions() - - if i.Power { - a.Logger.Infoln("Disabling sleep/hibernate...") - DisableSleepHibernate() - } - - if i.Ping { - a.Logger.Infoln("Enabling ping...") - EnablePing() - } - - if i.RDP { - a.Logger.Infoln("Enabling RDP...") - EnableRDP() - } - } - - a.installerMsg("Installation was successfull!\nAllow a few minutes for the agent to properly display in the RMM", "info", i.Silent) -} func copyFile(src, dst string) error { in, err := os.Open(src) diff --git a/agent/install_darwin.go b/agent/install_darwin.go new file mode 100644 index 0000000..740b25a --- /dev/null +++ b/agent/install_darwin.go @@ -0,0 +1,276 @@ +//go:build !windows +// +build !windows + +package agent + +import ( + "fmt" + "log" + "net/url" + "path/filepath" + "regexp" + "runtime" + "strconv" + "strings" + "time" + + "github.com/go-resty/resty/v2" + "github.com/spf13/viper" + trmm "github.com/wh1te909/trmm-shared" +) + +const ( + etcConfig = "/etc/tacticalagent" +) + +func (a *Agent) Install(i *Installer) { + a.checkExistingAndRemove(i.Silent) + i.Headers = map[string]string{ + "content-type": "application/json", + "Authorization": fmt.Sprintf("Token %s", i.Token), + } + + a.AgentID = GenerateAgentID() + a.Logger.Debugln("Agent ID:", a.AgentID) + u, err := url.Parse(i.RMM) + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + + if u.Scheme != "https" && u.Scheme != "http" { + a.installerMsg("Invalid URL (must contain https or http)", "error", i.Silent) + } + + // 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 + } + + a.Logger.Debugln("API:", i.SaltMaster) + terr := TestTCP(fmt.Sprintf("%s:4222", i.SaltMaster)) + if terr != nil { + a.installerMsg(fmt.Sprintf("ERROR: Either port 4222 TCP is not open on your RMM, or nats.service is not running.\n\n%s", terr.Error()), "error", i.Silent) + } + + baseURL := u.Scheme + "://" + u.Host + a.Logger.Debugln("Base URL:", baseURL) + + iClient := resty.New() + iClient.SetCloseConnection(true) + iClient.SetTimeout(15 * time.Second) + iClient.SetDebug(a.Debug) + iClient.SetHeaders(i.Headers) + + // set proxy if applicable + if len(i.Proxy) > 0 { + a.Logger.Infoln("Using proxy:", i.Proxy) + iClient.SetProxy(i.Proxy) + } + + creds, cerr := iClient.R().Get(fmt.Sprintf("%s/api/v3/installer/", baseURL)) + if cerr != nil { + a.installerMsg(cerr.Error(), "error", i.Silent) + } + if creds.StatusCode() == 401 { + a.installerMsg("Installer token has expired. Please generate a new one.", "error", i.Silent) + } + + verPayload := map[string]string{"version": a.Version} + iVersion, ierr := iClient.R().SetBody(verPayload).Post(fmt.Sprintf("%s/api/v3/installer/", baseURL)) + if ierr != nil { + a.installerMsg(ierr.Error(), "error", i.Silent) + } + if iVersion.StatusCode() != 200 { + a.installerMsg(DjangoStringResp(iVersion.String()), "error", i.Silent) + } + + rClient := resty.New() + rClient.SetCloseConnection(true) + rClient.SetTimeout(i.Timeout * time.Second) + rClient.SetDebug(a.Debug) + // set rest knox headers + rClient.SetHeaders(i.Headers) + + // set local cert if applicable + if len(i.Cert) > 0 { + if !trmm.FileExists(i.Cert) { + a.installerMsg(fmt.Sprintf("%s does not exist", i.Cert), "error", i.Silent) + } + rClient.SetRootCertificate(i.Cert) + } + + if len(i.Proxy) > 0 { + rClient.SetProxy(i.Proxy) + } + + var installerMeshSystemEXE string + if len(i.MeshDir) > 0 { + installerMeshSystemEXE = filepath.Join(i.MeshDir, "MeshAgent.exe") + } else { + installerMeshSystemEXE = a.MeshSystemBin + } + + var meshNodeID string + + if runtime.GOOS == "windows" && !i.NoMesh { + mesh := filepath.Join(a.ProgramDir, a.MeshInstaller) + if i.LocalMesh == "" { + a.Logger.Infoln("Downloading mesh agent...") + payload := map[string]string{"goarch": a.GoArch, "plat": a.Platform} + r, err := rClient.R().SetBody(payload).SetOutput(mesh).Post(fmt.Sprintf("%s/api/v3/meshexe/", baseURL)) + if err != nil { + a.installerMsg(fmt.Sprintf("Failed to download mesh agent: %s", err.Error()), "error", i.Silent) + } + if r.StatusCode() != 200 { + a.installerMsg(fmt.Sprintf("Unable to download the mesh agent from the RMM. %s", r.String()), "error", i.Silent) + } + } else { + err := copyFile(i.LocalMesh, mesh) + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + } + + a.Logger.Infoln("Installing mesh agent...") + a.Logger.Debugln("Mesh agent:", mesh) + time.Sleep(1 * time.Second) + + meshNodeID, err = a.installMesh(mesh, installerMeshSystemEXE, i.Proxy) + if err != nil { + a.installerMsg(fmt.Sprintf("Failed to install mesh agent: %s", err.Error()), "error", i.Silent) + } + } + + if len(i.MeshNodeID) > 0 { + meshNodeID = i.MeshNodeID + } + + a.Logger.Infoln("Adding agent to dashboard") + // add agent + type NewAgentResp struct { + AgentPK int `json:"pk"` + Token string `json:"token"` + } + agentPayload := map[string]interface{}{ + "agent_id": a.AgentID, + "hostname": a.Hostname, + "site": i.SiteID, + "monitoring_type": i.AgentType, + "mesh_node_id": meshNodeID, + "description": i.Description, + "goarch": a.GoArch, + "plat": a.Platform, + } + + r, err := rClient.R().SetBody(agentPayload).SetResult(&NewAgentResp{}).Post(fmt.Sprintf("%s/api/v3/newagent/", baseURL)) + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + if r.StatusCode() != 200 { + a.installerMsg(r.String(), "error", i.Silent) + } + + agentPK := r.Result().(*NewAgentResp).AgentPK + agentToken := r.Result().(*NewAgentResp).Token + + a.Logger.Debugln("Agent token:", agentToken) + a.Logger.Debugln("Agent PK:", agentPK) + + createAgentConfig(baseURL, a.AgentID, i.SaltMaster, agentToken, strconv.Itoa(agentPK), i.Cert, i.Proxy, i.MeshDir) + time.Sleep(1 * time.Second) + // refresh our agent with new values + a = New(a.Logger, a.Version) + a.Logger.Debugf("%+v\n", a) + + // set new headers, no longer knox auth...use agent auth + rClient.SetHeaders(a.Headers) + + time.Sleep(3 * time.Second) + // check in once + a.DoNatsCheckIn() + + if runtime.GOOS == "windows" { + // send software api + a.SendSoftware() + + a.Logger.Debugln("Creating temp dir") + a.CreateTRMMTempDir() + + a.Logger.Debugln("Disabling automatic windows updates") + a.PatchMgmnt(true) + + a.Logger.Infoln("Installing service...") + err := a.InstallService() + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + + time.Sleep(1 * time.Second) + a.Logger.Infoln("Starting service...") + out := a.ControlService(winSvcName, "start") + if !out.Success { + a.installerMsg(out.ErrorMsg, "error", i.Silent) + } + + a.Logger.Infoln("Adding windows defender exclusions") + a.addDefenderExlusions() + + if i.Power { + a.Logger.Infoln("Disabling sleep/hibernate...") + DisableSleepHibernate() + } + + if i.Ping { + a.Logger.Infoln("Enabling ping...") + EnablePing() + } + + if i.RDP { + a.Logger.Infoln("Enabling RDP...") + EnableRDP() + } + } + + a.installerMsg("Installation was successfull!\nAllow a few minutes for the agent to properly display in the RMM", "info", i.Silent) +} + +func (a *Agent) checkExistingAndRemove(silent bool) {} + +func (a *Agent) installerMsg(msg, alert string, silent bool) { + if alert == "error" { + a.Logger.Fatalln(msg) + } else { + a.Logger.Info(msg) + } +} + +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) + err := viper.SafeWriteConfigAs(etcConfig) + if err != nil { + log.Fatalln("createAgentConfig", err) + } +} + +func (a *Agent) addDefenderExlusions() {} + +func DisableSleepHibernate() {} + +func EnablePing() {} + +func EnableRDP() {} \ No newline at end of file diff --git a/agent/install_unix.go b/agent/install_unix.go index ee7654e..052d141 100644 --- a/agent/install_unix.go +++ b/agent/install_unix.go @@ -15,14 +15,210 @@ https://license.tacticalrmm.com package agent import ( + "fmt" "log" + "net/url" + "path/filepath" + "regexp" + "strconv" + "strings" + "time" + + "github.com/go-resty/resty/v2" "github.com/spf13/viper" + trmm "github.com/wh1te909/trmm-shared" ) const ( etcConfig = "/etc/tacticalagent" ) +func (a *Agent) Install(i *Installer) { + a.checkExistingAndRemove(i.Silent) + + i.Headers = map[string]string{ + "content-type": "application/json", + "Authorization": fmt.Sprintf("Token %s", i.Token), + } + a.AgentID = GenerateAgentID() + a.Logger.Debugln("Agent ID:", a.AgentID) + + u, err := url.Parse(i.RMM) + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + + if u.Scheme != "https" && u.Scheme != "http" { + a.installerMsg("Invalid URL (must contain https or http)", "error", i.Silent) + } + + // 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 + } + + a.Logger.Debugln("API:", i.SaltMaster) + + terr := TestTCP(fmt.Sprintf("%s:4222", i.SaltMaster)) + if terr != nil { + a.installerMsg(fmt.Sprintf("ERROR: Either port 4222 TCP is not open on your RMM, or nats.service is not running.\n\n%s", terr.Error()), "error", i.Silent) + } + + baseURL := u.Scheme + "://" + u.Host + a.Logger.Debugln("Base URL:", baseURL) + + iClient := resty.New() + iClient.SetCloseConnection(true) + iClient.SetTimeout(15 * time.Second) + iClient.SetDebug(a.Debug) + iClient.SetHeaders(i.Headers) + + // set proxy if applicable + if len(i.Proxy) > 0 { + a.Logger.Infoln("Using proxy:", i.Proxy) + iClient.SetProxy(i.Proxy) + } + + creds, cerr := iClient.R().Get(fmt.Sprintf("%s/api/v3/installer/", baseURL)) + if cerr != nil { + a.installerMsg(cerr.Error(), "error", i.Silent) + } + if creds.StatusCode() == 401 { + a.installerMsg("Installer token has expired. Please generate a new one.", "error", i.Silent) + } + + verPayload := map[string]string{"version": a.Version} + iVersion, ierr := iClient.R().SetBody(verPayload).Post(fmt.Sprintf("%s/api/v3/installer/", baseURL)) + if ierr != nil { + a.installerMsg(ierr.Error(), "error", i.Silent) + } + if iVersion.StatusCode() != 200 { + a.installerMsg(DjangoStringResp(iVersion.String()), "error", i.Silent) + } + + rClient := resty.New() + rClient.SetCloseConnection(true) + rClient.SetTimeout(i.Timeout * time.Second) + rClient.SetDebug(a.Debug) + // set rest knox headers + rClient.SetHeaders(i.Headers) + + // set local cert if applicable + if len(i.Cert) > 0 { + if !trmm.FileExists(i.Cert) { + a.installerMsg(fmt.Sprintf("%s does not exist", i.Cert), "error", i.Silent) + } + rClient.SetRootCertificate(i.Cert) + } + + if len(i.Proxy) > 0 { + rClient.SetProxy(i.Proxy) + } + + var installerMeshSystemBin string + if len(i.MeshDir) > 0 { + installerMeshSystemBin = filepath.Join(i.MeshDir, "meshagent") + } else { + installerMeshSystemBin = a.MeshSystemBin + } + + var meshNodeID string + mesh := a.MeshSystemBin + if i.LocalMesh == "" { + a.Logger.Infoln("Downloading mesh agent...") + payload := map[string]string{"goarch": a.GoArch, "plat": a.Platform} + r, err := rClient.R().SetBody(payload).SetOutput(mesh).Post(fmt.Sprintf("%s/api/v3/meshexe/", baseURL)) + if err != nil { + a.installerMsg(fmt.Sprintf("Failed to download mesh agent: %s", err.Error()), "error", i.Silent) + } + if r.StatusCode() != 200 { + a.installerMsg(fmt.Sprintf("Unable to download the mesh agent from the RMM. %s", r.String()), "error", i.Silent) + } + } else { + err := copyFile(i.LocalMesh, mesh) + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + } + + a.Logger.Infoln("Installing mesh agent...") + a.Logger.Debugln("Mesh agent:", mesh) + time.Sleep(1 * time.Second) + meshNodeID, err = a.installMesh(mesh, installerMeshSystemBin, i.Proxy) + if err != nil { + a.installerMsg(fmt.Sprintf("Failed to install mesh agent: %s", err.Error()), "error", i.Silent) + } + + if len(i.MeshNodeID) > 0 { + meshNodeID = i.MeshNodeID + } + + a.Logger.Infoln("Adding agent to dashboard") + // add agent + type NewAgentResp struct { + AgentPK int `json:"pk"` + Token string `json:"token"` + } + agentPayload := map[string]interface{}{ + "agent_id": a.AgentID, + "hostname": a.Hostname, + "site": i.SiteID, + "monitoring_type": i.AgentType, + "mesh_node_id": meshNodeID, + "description": i.Description, + "goarch": a.GoArch, + "plat": a.Platform, + } + + r, err := rClient.R().SetBody(agentPayload).SetResult(&NewAgentResp{}).Post(fmt.Sprintf("%s/api/v3/newagent/", baseURL)) + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + if r.StatusCode() != 200 { + a.installerMsg(r.String(), "error", i.Silent) + } + + agentPK := r.Result().(*NewAgentResp).AgentPK + agentToken := r.Result().(*NewAgentResp).Token + a.Logger.Debugln("Agent token:", agentToken) + a.Logger.Debugln("Agent PK:", agentPK) + createAgentConfig(baseURL, a.AgentID, i.SaltMaster, agentToken, strconv.Itoa(agentPK), i.Cert, i.Proxy, i.MeshDir) + time.Sleep(1 * time.Second) + // refresh our agent with new values + a = New(a.Logger, a.Version) + a.Logger.Debugf("%+v\n", a) + // set new headers, no longer knox auth...use agent auth + rClient.SetHeaders(a.Headers) + time.Sleep(3 * time.Second) + // check in once + a.DoNatsCheckIn() + // send software api + a.SendSoftware() + a.Logger.Debugln("Creating temp dir") + a.CreateTRMMTempDir() + a.Logger.Infoln("Installing service...") + err = a.InstallService() + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + + time.Sleep(1 * time.Second) + a.Logger.Infoln("Starting service...") + out := a.ControlService(winSvcName, "start") + if !out.Success { + a.installerMsg(out.ErrorMsg, "error", i.Silent) + } + + a.installerMsg("Installation was successfull!\nAllow a few minutes for the agent to properly display in the RMM", "info", i.Silent) +} + func (a *Agent) checkExistingAndRemove(silent bool) {} func (a *Agent) installerMsg(msg, alert string, silent bool) { diff --git a/agent/install_windows.go b/agent/install_windows.go index 3f8b630..9980cab 100644 --- a/agent/install_windows.go +++ b/agent/install_windows.go @@ -21,6 +21,222 @@ import ( "golang.org/x/sys/windows/registry" ) +func (a *Agent) Install(i *Installer) { + a.checkExistingAndRemove(i.Silent) + + i.Headers = map[string]string{ + "content-type": "application/json", + "Authorization": fmt.Sprintf("Token %s", i.Token), + } + a.AgentID = GenerateAgentID() + a.Logger.Debugln("Agent ID:", a.AgentID) + + u, err := url.Parse(i.RMM) + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + + if u.Scheme != "https" && u.Scheme != "http" { + a.installerMsg("Invalid URL (must contain https or http)", "error", i.Silent) + } + + // 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 + } + + a.Logger.Debugln("API:", i.SaltMaster) + + terr := TestTCP(fmt.Sprintf("%s:4222", i.SaltMaster)) + if terr != nil { + a.installerMsg(fmt.Sprintf("ERROR: Either port 4222 TCP is not open on your RMM, or nats.service is not running.\n\n%s", terr.Error()), "error", i.Silent) + } + + baseURL := u.Scheme + "://" + u.Host + a.Logger.Debugln("Base URL:", baseURL) + + iClient := resty.New() + iClient.SetCloseConnection(true) + iClient.SetTimeout(15 * time.Second) + iClient.SetDebug(a.Debug) + iClient.SetHeaders(i.Headers) + + // set proxy if applicable + if len(i.Proxy) > 0 { + a.Logger.Infoln("Using proxy:", i.Proxy) + iClient.SetProxy(i.Proxy) + } + + creds, cerr := iClient.R().Get(fmt.Sprintf("%s/api/v3/installer/", baseURL)) + if cerr != nil { + a.installerMsg(cerr.Error(), "error", i.Silent) + } + if creds.StatusCode() == 401 { + a.installerMsg("Installer token has expired. Please generate a new one.", "error", i.Silent) + } + + verPayload := map[string]string{"version": a.Version} + iVersion, ierr := iClient.R().SetBody(verPayload).Post(fmt.Sprintf("%s/api/v3/installer/", baseURL)) + if ierr != nil { + a.installerMsg(ierr.Error(), "error", i.Silent) + } + if iVersion.StatusCode() != 200 { + a.installerMsg(DjangoStringResp(iVersion.String()), "error", i.Silent) + } + + rClient := resty.New() + rClient.SetCloseConnection(true) + rClient.SetTimeout(i.Timeout * time.Second) + rClient.SetDebug(a.Debug) + // set rest knox headers + rClient.SetHeaders(i.Headers) + + // set local cert if applicable + if len(i.Cert) > 0 { + if !trmm.FileExists(i.Cert) { + a.installerMsg(fmt.Sprintf("%s does not exist", i.Cert), "error", i.Silent) + } + rClient.SetRootCertificate(i.Cert) + } + + if len(i.Proxy) > 0 { + rClient.SetProxy(i.Proxy) + } + + var installerMeshSystemEXE string + if len(i.MeshDir) > 0 { + installerMeshSystemEXE = filepath.Join(i.MeshDir, "MeshAgent.exe") + } else { + installerMeshSystemEXE = a.MeshSystemBin + } + + var meshNodeID string + + mesh := filepath.Join(a.ProgramDir, a.MeshInstaller) + if i.LocalMesh == "" { + a.Logger.Infoln("Downloading mesh agent...") + payload := map[string]string{"goarch": a.GoArch, "plat": a.Platform} + r, err := rClient.R().SetBody(payload).SetOutput(mesh).Post(fmt.Sprintf("%s/api/v3/meshexe/", baseURL)) + if err != nil { + a.installerMsg(fmt.Sprintf("Failed to download mesh agent: %s", err.Error()), "error", i.Silent) + } + if r.StatusCode() != 200 { + a.installerMsg(fmt.Sprintf("Unable to download the mesh agent from the RMM. %s", r.String()), "error", i.Silent) + } + } else { + err := copyFile(i.LocalMesh, mesh) + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + } + + a.Logger.Infoln("Installing mesh agent...") + a.Logger.Debugln("Mesh agent:", mesh) + time.Sleep(1 * time.Second) + + meshNodeID, err = a.installMesh(mesh, installerMeshSystemEXE, i.Proxy) + if err != nil { + a.installerMsg(fmt.Sprintf("Failed to install mesh agent: %s", err.Error()), "error", i.Silent) + } + + if len(i.MeshNodeID) > 0 { + meshNodeID = i.MeshNodeID + } + + a.Logger.Infoln("Adding agent to dashboard") + // add agent + type NewAgentResp struct { + AgentPK int `json:"pk"` + Token string `json:"token"` + } + agentPayload := map[string]interface{}{ + "agent_id": a.AgentID, + "hostname": a.Hostname, + "site": i.SiteID, + "monitoring_type": i.AgentType, + "mesh_node_id": meshNodeID, + "description": i.Description, + "goarch": a.GoArch, + "plat": a.Platform, + } + + r, err := rClient.R().SetBody(agentPayload).SetResult(&NewAgentResp{}).Post(fmt.Sprintf("%s/api/v3/newagent/", baseURL)) + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + if r.StatusCode() != 200 { + a.installerMsg(r.String(), "error", i.Silent) + } + + agentPK := r.Result().(*NewAgentResp).AgentPK + agentToken := r.Result().(*NewAgentResp).Token + + a.Logger.Debugln("Agent token:", agentToken) + a.Logger.Debugln("Agent PK:", agentPK) + + createAgentConfig(baseURL, a.AgentID, i.SaltMaster, agentToken, strconv.Itoa(agentPK), i.Cert, i.Proxy, i.MeshDir) + time.Sleep(1 * time.Second) + // refresh our agent with new values + a = New(a.Logger, a.Version) + a.Logger.Debugf("%+v\n", a) + + // set new headers, no longer knox auth...use agent auth + rClient.SetHeaders(a.Headers) + + time.Sleep(3 * time.Second) + // check in once + a.DoNatsCheckIn() + + // send software api + a.SendSoftware() + + a.Logger.Debugln("Creating temp dir") + a.CreateTRMMTempDir() + + a.Logger.Debugln("Disabling automatic windows updates") + a.PatchMgmnt(true) + + a.Logger.Infoln("Installing service...") + err := a.InstallService() + if err != nil { + a.installerMsg(err.Error(), "error", i.Silent) + } + + time.Sleep(1 * time.Second) + a.Logger.Infoln("Starting service...") + out := a.ControlService(winSvcName, "start") + if !out.Success { + a.installerMsg(out.ErrorMsg, "error", i.Silent) + } + + a.Logger.Infoln("Adding windows defender exclusions") + a.addDefenderExlusions() + + if i.Power { + a.Logger.Infoln("Disabling sleep/hibernate...") + DisableSleepHibernate() + } + + if i.Ping { + a.Logger.Infoln("Enabling ping...") + EnablePing() + } + + if i.RDP { + a.Logger.Infoln("Enabling RDP...") + EnableRDP() + } + + a.installerMsg("Installation was successfull!\nAllow a few minutes for the agent to properly display in the RMM", "info", i.Silent) +} + func createAgentConfig(baseurl, agentid, apiurl, token, agentpk, cert, proxy, meshdir string) { k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS) if err != nil {