mirror of https://github.com/wh1te909/rmmagent.git
init
This commit is contained in:
commit
de82595181
|
@ -0,0 +1,5 @@
|
|||
*.exe
|
||||
*.db
|
||||
*.log
|
||||
*.7z
|
||||
tacticalrmm
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -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=
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue