This commit is contained in:
wh1te909 2020-10-05 14:35:16 -07:00
commit de82595181
11 changed files with 810 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.exe
*.db
*.log
*.7z
tacticalrmm

25
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,25 @@
{
"go.useLanguageServer": true,
"[go]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
// Optional: Disable snippets, as they conflict with completion ranking.
"editor.snippetSuggestions": "none",
},
"[go.mod]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
},
"gopls": {
// Add parameter placeholders when completing a function.
"usePlaceholders": true,
// If true, enable additional analyses with staticcheck.
// Warning: This will significantly increase memory usage.
"staticcheck": true,
}
}

254
agent/agent_windows.go Normal file
View File

@ -0,0 +1,254 @@
// Package agent todo change this
package agent
import (
"database/sql"
"fmt"
"os"
"path/filepath"
"runtime"
ps "github.com/elastic/go-sysinfo"
_ "github.com/mattn/go-sqlite3"
svc "github.com/shirou/gopsutil/winservices"
"github.com/sirupsen/logrus"
)
// WindowsAgent struct
type WindowsAgent struct {
DB
Host
ProgramDir string
SystemDrive string
SaltCall string
Nssm string
SaltMinion string
SaltInstaller string
MeshInstaller string
Headers map[string]string
Logger *logrus.Logger
Version string
}
// New __init__
func New(logger *logrus.Logger, version string) *WindowsAgent {
host, _ := ps.Host()
info := host.Info()
pd := filepath.Join(os.Getenv("ProgramFiles"), "TacticalAgent")
dbFile := filepath.Join(pd, "agentdb.db")
sd := os.Getenv("SystemDrive")
sc := filepath.Join(sd, "\\salt\\salt-call.bat")
nssm, mesh, saltexe, saltinstaller := ArchInfo(pd)
db := LoadDB(dbFile, logger)
headers := make(map[string]string)
if len(db.Token) > 0 {
headers["Content-Type"] = "application/json"
headers["Authorization"] = fmt.Sprintf("Token %s", db.Token)
}
return &WindowsAgent{
DB: DB{
db.Server,
db.AgentID,
db.MeshNodeID,
db.Token,
db.AgentPK,
db.SaltMaster,
db.SaltID,
db.Cert,
},
Host: Host{
Hostname: info.Hostname,
Arch: info.Architecture,
Timezone: info.Timezone,
},
ProgramDir: pd,
SystemDrive: sd,
SaltCall: sc,
Nssm: nssm,
SaltMinion: saltexe,
SaltInstaller: saltinstaller,
MeshInstaller: mesh,
Headers: headers,
Logger: logger,
Version: version,
}
}
// LoadDB loads database info called during agent init
func LoadDB(file string, logger *logrus.Logger) *DB {
if !FileExists(file) {
return &DB{}
}
db, err := sql.Open("sqlite3", file)
if err != nil {
logger.Fatalln(err)
}
defer db.Close()
rows, err := db.
Query("select server, agentid, mesh_node_id, token, agentpk, salt_master, salt_id, cert from agentstorage")
if err != nil {
logger.Fatalln(err)
}
defer rows.Close()
var (
server string
agentid string
meshid string
token string
pk int32
saltmaster string
saltid string
cert *string
)
for rows.Next() {
err = rows.
Scan(&server, &agentid, &meshid, &token, &pk, &saltmaster, &saltid, &cert)
if err != nil {
logger.Fatalln(err)
}
}
var ret string
if cert != nil {
ret = *cert
}
err = rows.Err()
if err != nil {
logger.Fatalln(err)
}
return &DB{server, agentid, meshid, token, pk, saltmaster, saltid, ret}
}
// ArchInfo returns arch specific filenames and urls
func ArchInfo(programDir string) (nssm, mesh, saltexe, saltinstaller string) {
baseURL := "https://github.com/wh1te909/winagent/raw/master/bin/"
switch runtime.GOARCH {
case "amd64":
nssm = filepath.Join(programDir, "nssm.exe")
mesh = "meshagent.exe"
saltexe = baseURL + "salt-minion-setup.exe"
saltinstaller = "salt-minion-setup.exe"
case "386":
nssm = filepath.Join(programDir, "nssm-x86.exe")
mesh = "meshagent-x86.exe"
saltexe = baseURL + "salt-minion-setup-x86.exe"
saltinstaller = "salt-minion-setup-x86.exe"
}
return
}
// OSInfo returns os names formatted
func OSInfo() (plat, osFullName string) {
host, _ := ps.Host()
info := host.Info()
os := info.OS
var arch string
switch info.Architecture {
case "x86_64":
arch = "64 bit"
case "x86":
arch = "32 bit"
}
plat = os.Platform
osFullName = fmt.Sprintf("%s, %s (build %s)", os.Name, arch, os.Build)
return
}
// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicecontrollerstatus?view=dotnet-plat-ext-3.1
func serviceStatusText(num uint32) string {
switch num {
case 1:
return "stopped"
case 2:
return "start_pending"
case 3:
return "stop_pending"
case 4:
return "running"
case 5:
return "continue_pending"
case 6:
return "pause_pending"
case 7:
return "paused"
default:
return "unknown"
}
}
// WindowsService holds windows service info
type WindowsService struct {
Name string `json:"name"`
Status string `json:"status"`
DisplayName string `json:"display_name"`
BinPath string `json:"binpath"`
Description string `json:"description"`
Username string `json:"username"`
PID uint32 `json:"pid"`
}
// GetServices returns a list of windows services
func (a *WindowsAgent) GetServices() []WindowsService {
ret := make([]WindowsService, 0)
services, err := svc.ListServices()
if err != nil {
a.Logger.Debugln(err)
return ret
}
for _, s := range services {
srv, err := svc.NewService(s.Name)
if err == nil {
derr := srv.GetServiceDetail()
conf, qerr := srv.QueryServiceConfig()
if derr == nil && qerr == nil {
winsvc := WindowsService{
Name: s.Name,
Status: serviceStatusText(uint32(srv.Status.State)),
DisplayName: conf.DisplayName,
BinPath: conf.BinaryPathName,
Description: conf.Description,
Username: conf.ServiceStartName,
PID: uint32(srv.Status.Pid),
}
ret = append(ret, winsvc)
} else {
if derr != nil {
a.Logger.Debugln(derr)
}
if qerr != nil {
a.Logger.Debugln(qerr)
}
}
}
}
return ret
}
//RecoverSalt recovers salt minion
func (a *WindowsAgent) RecoverSalt() {
a.Logger.Debugln("Attempting salt recovery on", a.Hostname)
// TODO
}
//RecoverMesh recovers mesh agent
func (a *WindowsAgent) RecoverMesh() {
a.Logger.Debugln("Attempting mesh recovery on", a.Hostname)
// TODO
}
//RecoverCMD runs shell recovery command
func (a *WindowsAgent) RecoverCMD(cmd string) {
a.Logger.Debugln("Attempting shell recovery on", a.Hostname)
// TODO
}

20
agent/host.go Normal file
View File

@ -0,0 +1,20 @@
package agent
// Host struct
type Host struct {
Hostname string `json:"hostname"`
Arch string `json:"arch"`
Timezone string `json:"timezone"`
}
// DB sqlite database stores RMM and agent info
type DB struct {
Server string
AgentID string
MeshNodeID string
Token string
AgentPK int32
SaltMaster string
SaltID string
Cert string
}

119
agent/service_windows.go Normal file
View File

@ -0,0 +1,119 @@
package agent
import (
"encoding/json"
"math/rand"
"strings"
"time"
)
//HelloPost post
type HelloPost struct {
Agentid string `json:"agent_id"`
Hostname string `json:"hostname"`
OS string `json:"operating_system"`
TotalRAM float64 `json:"total_ram"`
Platform string `json:"plat"`
Version string `json:"version"`
BootTime int64 `json:"boot_time"`
SaltVersion string `json:"salt_ver"`
}
//HelloPatch patch
type HelloPatch struct {
Agentid string `json:"agent_id"`
Services []WindowsService `json:"services"`
PublicIP string `json:"public_ip"`
Disks string
Username string
Version string `json:"version"`
BootTime int64 `json:"boot_time"`
}
// RunAsService tacticalagent windows nssm service
func (a *WindowsAgent) RunAsService() {
a.Logger.Infoln("Agent service started")
var data map[string]interface{}
var sleep int
url := a.Server + "/api/v2/hello/"
plat, osinfo := OSInfo()
postPayload := HelloPost{
Agentid: a.AgentID,
Hostname: a.Hostname,
OS: osinfo,
TotalRAM: TotalRAM(),
Platform: plat,
Version: a.Version,
BootTime: BootTime(),
SaltVersion: a.GetProgramVersion("salt minion"),
}
request1 := &APIRequest{
URL: url,
Method: "POST",
Payload: postPayload,
Headers: a.Headers,
Timeout: 15,
}
_, err := MakeRequest(request1)
if err != nil {
a.Logger.Debugln(err)
}
time.Sleep(3 * time.Second)
for {
patchPayload := HelloPatch{
Agentid: a.AgentID,
Services: a.GetServices(),
PublicIP: "todo",
Disks: "todo",
Username: "todo",
Version: a.Version,
BootTime: BootTime(),
}
request2 := &APIRequest{
URL: url,
Method: "PATCH",
Payload: patchPayload,
Headers: a.Headers,
Timeout: 15,
}
r, err := MakeRequest(request2)
if err != nil {
a.Logger.Debugln(err)
} else {
ret := strings.Trim(r.String(), `"`)
if len(ret) > 0 && ret != "ok" {
if err := json.Unmarshal(r.Body(), &data); err != nil {
a.Logger.Debugln(err)
} else {
if action, ok := data["recovery"].(string); ok {
switch action {
case "salt":
go a.RecoverSalt()
case "mesh":
go a.RecoverMesh()
case "command":
if cmd, ok := data["cmd"].(string); ok {
go a.RecoverCMD(cmd)
}
}
}
}
}
}
sleep = randRange(30, 120)
time.Sleep(time.Duration(sleep) * time.Second)
}
}
func randRange(min, max int) int {
rand.Seed(time.Now().UnixNano())
return rand.Intn(max-min) + min
}

View File

@ -0,0 +1,124 @@
package agent
import (
"fmt"
"strings"
"time"
so "github.com/iamacarpet/go-win64api/shared"
"golang.org/x/sys/windows/registry"
)
// GetProgramVersion loops through the registry for software
// and if found, returns its version
func (a *WindowsAgent) GetProgramVersion(name string) string {
sw, err := InstalledSoftwareList()
if err != nil {
a.Logger.Debugf("%s\r\n", err.Error())
return "0.0.0"
}
var lowerName string
for _, s := range sw {
lowerName = strings.ToLower(s.Name())
if strings.Contains(lowerName, name) {
return s.Version()
}
}
return "0.0.0"
}
func InstalledSoftwareList() ([]so.Software, error) {
sw32, err := getSoftwareList(`SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall`, "X32")
if err != nil {
return nil, err
}
return sw32, nil
}
// https://github.com/iamacarpet/go-win64api/blob/master/software.go
func getSoftwareList(baseKey string, arch string) ([]so.Software, error) {
k, err := registry.OpenKey(registry.LOCAL_MACHINE, baseKey, registry.QUERY_VALUE|registry.ENUMERATE_SUB_KEYS)
if err != nil {
return nil, fmt.Errorf("Error reading from registry: %s", err.Error())
}
defer k.Close()
swList := make([]so.Software, 0)
subkeys, err := k.ReadSubKeyNames(-1)
if err != nil {
return nil, fmt.Errorf("Error reading subkey list from registry: %s", err.Error())
}
for _, sw := range subkeys {
sk, err := registry.OpenKey(registry.LOCAL_MACHINE, baseKey+`\`+sw, registry.QUERY_VALUE)
if err != nil {
return nil, fmt.Errorf("Error reading from registry (subkey %s): %s", sw, err.Error())
}
dn, _, err := sk.GetStringValue("DisplayName")
if err == nil {
swv := so.Software{DisplayName: dn, Arch: arch}
dv, _, err := sk.GetStringValue("DisplayVersion")
if err == nil {
swv.DisplayVersion = dv
}
pub, _, err := sk.GetStringValue("Publisher")
if err == nil {
swv.Publisher = pub
}
id, _, err := sk.GetStringValue("InstallDate")
if err == nil {
swv.InstallDate, _ = time.Parse("20060102", id)
}
es, _, err := sk.GetIntegerValue("EstimatedSize")
if err == nil {
swv.EstimatedSize = es
}
cont, _, err := sk.GetStringValue("Contact")
if err == nil {
swv.Contact = cont
}
hlp, _, err := sk.GetStringValue("HelpLink")
if err == nil {
swv.HelpLink = hlp
}
isource, _, err := sk.GetStringValue("InstallSource")
if err == nil {
swv.InstallSource = isource
}
ilocaction, _, err := sk.GetStringValue("InstallLocation")
if err == nil {
swv.InstallLocation = ilocaction
}
ustring, _, err := sk.GetStringValue("UninstallString")
if err == nil {
swv.UninstallString = ustring
}
mver, _, err := sk.GetIntegerValue("VersionMajor")
if err == nil {
swv.VersionMajor = mver
}
mnver, _, err := sk.GetIntegerValue("VersionMinor")
if err == nil {
swv.VersionMinor = mnver
}
swList = append(swList, swv)
}
}
return swList, nil
}

View File

@ -0,0 +1,26 @@
package agent
import (
"strings"
wapi "github.com/iamacarpet/go-win64api"
)
// GetProgramVersion loops through the registry for software
// and if found, returns its version
func (a *WindowsAgent) GetProgramVersion(name string) string {
sw, err := wapi.InstalledSoftwareList()
if err != nil {
a.Logger.Debugf("%s\r\n", err.Error())
return "0.0.0"
}
var lowerName string
for _, s := range sw {
lowerName = strings.ToLower(s.Name())
if strings.Contains(lowerName, name) {
return s.Version()
}
}
return "0.0.0"
}

101
agent/utils.go Normal file
View File

@ -0,0 +1,101 @@
package agent
import (
"fmt"
"math"
"math/rand"
"os"
"path/filepath"
"runtime"
"time"
ps "github.com/elastic/go-sysinfo"
"github.com/go-resty/resty/v2"
)
var client = resty.New()
// APIRequest struct
type APIRequest struct {
URL string
Method string
Payload interface{}
Headers map[string]string
Timeout time.Duration
LocalCert string
}
// MakeRequest creates an api request to the RMM
func MakeRequest(r *APIRequest) (*resty.Response, error) {
client.SetCloseConnection(true)
client.SetHeaders(r.Headers)
client.SetTimeout(r.Timeout * time.Second)
if len(r.LocalCert) > 0 {
client.SetRootCertificate(r.LocalCert)
}
var resp *resty.Response
var err error
switch r.Method {
case "GET":
resp, err = client.R().Get(r.URL)
case "POST":
resp, err = client.R().SetBody(r.Payload).Post(r.URL)
case "PATCH":
resp, err = client.R().SetBody(r.Payload).Patch(r.URL)
case "PUT":
resp, err = client.R().SetBody(r.Payload).Put(r.URL)
}
if err != nil {
return &resty.Response{}, err
}
return resp, nil
}
// GenerateAgentID creates and returns a unique agent id
func GenerateAgentID(hostname string) string {
rand.Seed(time.Now().UnixNano())
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, 35)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b) + "-" + hostname
}
// ShowVersionInfo prints basic debugging info
func ShowVersionInfo(ver string) {
fmt.Println("Tactical RMM Agent:", ver)
fmt.Println("Arch:", runtime.GOARCH)
fmt.Println("Go version:", runtime.Version())
if runtime.GOOS == "windows" {
fmt.Println("Program Directory:", filepath.Join(os.Getenv("ProgramFiles"), "TacticalAgent"))
}
}
// FileExists checks whether a file exists
func FileExists(path string) bool {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// TotalRAM returns total RAM in GB
func TotalRAM() float64 {
host, _ := ps.Host()
mem, _ := host.Memory()
return math.Ceil(float64(mem.Total) / 1073741824.0)
}
// BootTime returns system boot time as a unix timestamp
func BootTime() int64 {
host, _ := ps.Host()
info := host.Info()
return info.BootTime.Unix()
}

15
go.mod Normal file
View File

@ -0,0 +1,15 @@
module github.com/wh1te909/rmmagent
go 1.15
require (
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d
github.com/elastic/go-sysinfo v1.4.0
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/go-resty/resty/v2 v2.3.0
github.com/iamacarpet/go-win64api v0.0.0-20200715182619-8cbc936e1a5a
github.com/mattn/go-sqlite3 v1.14.3
github.com/shirou/gopsutil v2.20.9-0.20200919012454-c5b7357407cb+incompatible
github.com/sirupsen/logrus v1.7.0
golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c
)

49
go.sum Normal file
View File

@ -0,0 +1,49 @@
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elastic/go-sysinfo v1.4.0 h1:LUnK6TNOuy8JEByuDzTAQH3iQ6bIywy55+Z+QlKNSWk=
github.com/elastic/go-sysinfo v1.4.0/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0=
github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY=
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
github.com/iamacarpet/go-win64api v0.0.0-20200715182619-8cbc936e1a5a h1:PotlHkUHYTVAjZTW1HbXw43luragnCAI0XzyZ5Tek3k=
github.com/iamacarpet/go-win64api v0.0.0-20200715182619-8cbc936e1a5a/go.mod h1:oGJx9dz0Ny7HC7U55RZ0Smd6N9p3hXP/+hOFtuYrAxM=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-sqlite3 v1.14.3 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/shirou/gopsutil v2.20.9-0.20200919012454-c5b7357407cb+incompatible h1:7dLbOHoySZBvGTAvf6lUgEb1o4cIj6n8FUpMjpLAX+0=
github.com/shirou/gopsutil v2.20.9-0.20200919012454-c5b7357407cb+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200622182413-4b0db7f3f76b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c h1:38q6VNPWR010vN82/SB121GujZNIfAUb4YttE2rhGuc=
golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=

72
main.go Normal file
View File

@ -0,0 +1,72 @@
package main
// cross compile from linux for windows
// apt install build-essential gcc-multilib gcc-mingw-w64-x86-64 gcc-mingw-w64-i686
// 64 bit: CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 go build -o tacticalrmm.exe
// 32 bit: CGO_ENABLED=1 CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ GOOS=windows GOARCH=386 go build -o tacticalrmm-x86.exe
// building 32 bit from windows from git bash
// env CGO_ENABLED=1 CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ GOARCH=386 go build -o tacticalrmm-x86.exe
import (
"flag"
"os"
"github.com/sirupsen/logrus"
"github.com/wh1te909/rmmagent/agent"
)
var (
version = "1.0.0"
log = logrus.New()
logFile *os.File
)
func main() {
ver := flag.Bool("version", false, "Prints version")
mode := flag.String("m", "", "The mode to run")
logLevel := flag.String("log", "INFO", "The log level")
logTo := flag.String("logto", "file", "Where to log to")
flag.Parse()
if *ver {
agent.ShowVersionInfo(version)
return
}
if len(os.Args) == 1 {
// TODO show agent status
return
}
setupLogging(logLevel, logTo)
defer logFile.Close()
a := *agent.New(log, version)
switch *mode {
case "install":
log.SetOutput(os.Stdout)
// TODO
return
case "winagentsvc":
a.RunAsService()
default:
// TODO
}
}
func setupLogging(level *string, to *string) {
ll, err := logrus.ParseLevel(*level)
if err != nil {
ll = logrus.InfoLevel
}
log.SetLevel(ll)
if *to == "stdout" {
log.SetOutput(os.Stdout)
} else {
logFile, _ = os.OpenFile("agent.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
log.SetOutput(logFile)
}
}