diff --git a/agent/agent_windows.go b/agent/agent_windows.go index f88c312..c4ae4b9 100644 --- a/agent/agent_windows.go +++ b/agent/agent_windows.go @@ -674,6 +674,32 @@ func (a *WindowsAgent) RecoverCMD(command string) { _, _ = CMDShell([]string{}, command, 18000, true) } +func (a *WindowsAgent) LocalSaltCall(saltfunc string, args []string, timeout int) ([]byte, error) { + var outb, errb bytes.Buffer + var bytesErr []byte + largs := len(args) + saltArgs := make([]string, 0) + + saltArgs = []string{saltfunc} + + if largs > 0 { + saltArgs = append(saltArgs, args...) + } + + saltArgs = append(saltArgs, "--local", fmt.Sprintf("--timeout=%d", timeout)) + + cmd := exec.Command(a.SaltCall, saltArgs...) + cmd.Stdout = &outb + cmd.Stderr = &errb + + err := cmd.Run() + if err != nil { + a.Logger.Debugln(err) + return bytesErr, err + } + return outb.Bytes(), nil +} + // ShowStatus prints windows service status // If called from an interactive desktop, pops up a message box // Otherwise prints to the console diff --git a/agent/patches_windows.go b/agent/patches_windows.go new file mode 100644 index 0000000..a6382c2 --- /dev/null +++ b/agent/patches_windows.go @@ -0,0 +1,114 @@ +package agent + +import ( + "encoding/json" + "fmt" +) + +type WindowsUpdate struct { + PatchID int64 `json:"id"` + KB string `json:"kb"` + GUID string `json:"guid"` +} + +type WindowsUpdates []WindowsUpdate + +type SaltDownloadRet struct { + Updates interface{} `json:"Updates"` + Message string `json:"Message"` + Success bool `json:"Success"` +} + +type SaltInstallRet struct { + Updates interface{} `json:"Updates"` + Message string `json:"Message"` + Success bool `json:"Success"` + NeedsReboot bool `json:"NeedsReboot"` +} + +type SaltUpdateOutput struct { + SaltDownload SaltDownloadRet `json:"Download"` + SaltInstall SaltInstallRet `json:"Install"` +} + +type LocalSaltUpdate struct { + Local SaltUpdateOutput `json:"local"` +} + +func (a *WindowsAgent) InstallPatches() { + data := WindowsUpdates{} + url := fmt.Sprintf("%s/api/v3/%s/winupdater/", a.Server, a.AgentID) + + r := &APIRequest{ + URL: url, + Method: "GET", + Headers: a.Headers, + Timeout: 30, + LocalCert: a.DB.Cert, + Debug: a.Debug, + } + + r1, err := r.MakeRequest() + if err != nil { + a.Logger.Debugln(err) + return + } + + if r1.IsError() { + a.Logger.Debugln("Install Patches:", r1.String()) + return + } + + if err := json.Unmarshal(r1.Body(), &data); err != nil { + a.Logger.Debugln(err) + return + } + + // no patches + if len(data) == 0 { + return + } + + saltdata := LocalSaltUpdate{} + + for _, patch := range data { + out, err := a.LocalSaltCall("win_wua.get", []string{patch.KB, "download=True", "install=True"}, 7200) + if err != nil { + a.Logger.Debugln(err) + continue + } + + if err := json.Unmarshal(out, &saltdata); err != nil { + a.Logger.Debugln(err) + continue + } + + a.Logger.Infoln(saltdata) + payload := map[string]string{"agent_id": a.AgentID, "kb": patch.KB} + + if saltdata.Local.SaltInstall.Updates == "Nothing to install" { + payload["results"] = "alreadyinstalled" + } else { + if saltdata.Local.SaltInstall.Success { + payload["results"] = "success" + } else { + payload["results"] = "failed" + } + } + + r := &APIRequest{ + URL: url, + Headers: a.Headers, + Method: "PATCH", + Payload: payload, + Timeout: 30, + LocalCert: a.DB.Cert, + Debug: a.Debug, + } + + _, rerr := r.MakeRequest() + if rerr != nil { + a.Logger.Debugln(rerr) + } + } +} diff --git a/main.go b/main.go index b755590..594081d 100644 --- a/main.go +++ b/main.go @@ -67,6 +67,8 @@ func main() { a.RecoverSalt() case "recovermesh": a.RecoverMesh() + case "winupdater": + a.InstallPatches() case "taskrunner": if len(os.Args) < 5 || *taskPK == 0 { return