зеркало из https://github.com/mozilla/mig.git
[minor] remove upgrade module and additional references to module
This commit is contained in:
Родитель
cf76532726
Коммит
bbd4b8d2e4
|
@ -140,23 +140,6 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"module": "upgrade",
|
||||
"parameters": {
|
||||
"to_version": "b9536d2-201403031435",
|
||||
"location": "https://download.mig.example.net/mig-agent-b9536d2-201403031435",
|
||||
"checksum": "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab"
|
||||
}
|
||||
},
|
||||
{
|
||||
"module": "process",
|
||||
"parameters": {
|
||||
"look for running process that belongs to rootkits": [
|
||||
"/usr/libexec/rootkitd",
|
||||
"/opt/rootkit/stealth_dangerous"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"module": "agentdestroy",
|
||||
"parameters": {
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"Name": "Upgrade a specific agent",
|
||||
"Description": {
|
||||
"Author": "Julien Vehent",
|
||||
"Email": "jvehent@mozilla.com",
|
||||
"Revision": 201408261000
|
||||
},
|
||||
"Target": "agents.environment->>'os' = 'linux' AND agents.environment->>'arch' = 'amd64'",
|
||||
"Operations": [
|
||||
{
|
||||
"Module": "upgrade",
|
||||
"Parameters": {
|
||||
"linux/amd64": {
|
||||
"to_version": "16eb58b-201404021544",
|
||||
"location": "http://localhost/mig/bin/linux/amd64/mig-agent",
|
||||
"checksum": "31fccc576635a29e0a27bbf7416d4f32a0ebaee892475e14708641c0a3620b03"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"SyntaxVersion": 2
|
||||
}
|
1
agent.go
1
agent.go
|
@ -10,7 +10,6 @@ import "time"
|
|||
|
||||
const (
|
||||
AgtStatusOnline string = "online"
|
||||
AgtStatusUpgraded string = "upgraded"
|
||||
AgtStatusDestroyed string = "destroyed"
|
||||
AgtStatusOffline string = "offline"
|
||||
AgtStatusIdle string = "idle"
|
||||
|
|
|
@ -15,6 +15,5 @@ import (
|
|||
_ "mig.ninja/mig/modules/pkg"
|
||||
_ "mig.ninja/mig/modules/scribe"
|
||||
_ "mig.ninja/mig/modules/timedrift"
|
||||
//_ "mig/modules/upgrade"
|
||||
//_ "mig/modules/example"
|
||||
)
|
||||
|
|
|
@ -74,7 +74,7 @@ The following search parameters are available, per search type:
|
|||
- investigatorname=<str>search agents that ran an action signed by investigator named <str>
|
||||
- version=<str> search agents by version <str>
|
||||
- status=<str> search agents with a given status amongst:
|
||||
online, upgraded, destroyed, offline, idle
|
||||
online, destroyed, offline, idle
|
||||
* investigator:
|
||||
- name=<str> search investigators by name
|
||||
- before=<rfc3339> search investigators created or modified before <rfc3339 date>
|
||||
|
|
|
@ -15,6 +15,5 @@ import (
|
|||
_ "mig.ninja/mig/modules/pkg"
|
||||
_ "mig.ninja/mig/modules/scribe"
|
||||
_ "mig.ninja/mig/modules/timedrift"
|
||||
//_ "mig/modules/upgrade"
|
||||
//_ "mig/modules/example"
|
||||
)
|
||||
|
|
|
@ -15,6 +15,5 @@ import (
|
|||
_ "mig.ninja/mig/modules/pkg"
|
||||
_ "mig.ninja/mig/modules/scribe"
|
||||
_ "mig.ninja/mig/modules/timedrift"
|
||||
//_ "mig/modules/upgrade"
|
||||
//_ "mig/modules/example"
|
||||
)
|
||||
|
|
|
@ -130,7 +130,7 @@ func (db *DB) InsertAgent(agt mig.Agent, useTx *sql.Tx) (err error) {
|
|||
}
|
||||
|
||||
// UpdateAgentHeartbeat updates the heartbeat timestamp of an agent in the database
|
||||
// unless the agent has been marked as destroyed or upgraded
|
||||
// unless the agent has been marked as destroyed
|
||||
func (db *DB) UpdateAgentHeartbeat(agt mig.Agent) (err error) {
|
||||
_, err = db.c.Exec(`UPDATE agents SET status=$1, heartbeattime=$2 WHERE id=$3`,
|
||||
mig.AgtStatusOnline, agt.HeartBeatTS, agt.ID)
|
||||
|
@ -296,16 +296,6 @@ func (db *DB) ActiveAgentsByTarget(target string) (agents []mig.Agent, err error
|
|||
return
|
||||
}
|
||||
|
||||
// MarkAgentUpgraded updated the status of an agent in the database
|
||||
func (db *DB) MarkAgentUpgraded(agent mig.Agent) (err error) {
|
||||
_, err = db.c.Exec(`UPDATE agents SET status=$1 WHERE id=$2`,
|
||||
mig.AgtStatusUpgraded, agent.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to mark agent as upgraded in database: '%v'", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarkAgentDestroyed updated the status and destructiontime of an agent in the database
|
||||
func (db *DB) MarkAgentDestroyed(agent mig.Agent) (err error) {
|
||||
agent.DestructionTime = time.Now()
|
||||
|
|
|
@ -853,7 +853,7 @@ GET /api/v1/search
|
|||
Status depends on the type. Below are the available statuses per type:
|
||||
|
||||
- `action`: pending, scheduled, preparing, invalid, inflight, completed
|
||||
- `agent`: online, upgraded, destroyed, offline, idle
|
||||
- `agent`: online, destroyed, offline, idle
|
||||
- `command`: prepared, sent, success, timeout, cancelled, expired, failed
|
||||
- `investigator`: active, disabled
|
||||
|
||||
|
|
|
@ -134,7 +134,6 @@ The list of modules imported in the agent is maintained in
|
|||
_ "mig/modules/file"
|
||||
_ "mig/modules/netstat"
|
||||
_ "mig/modules/timedrift"
|
||||
//_ "mig/modules/upgrade"
|
||||
_ "mig/modules/ping"
|
||||
)
|
||||
|
||||
|
|
|
@ -15,6 +15,5 @@ import (
|
|||
_ "mig.ninja/mig/modules/pkg"
|
||||
_ "mig.ninja/mig/modules/scribe"
|
||||
_ "mig.ninja/mig/modules/timedrift"
|
||||
//_ "mig/modules/upgrade"
|
||||
//_ "mig/modules/example"
|
||||
)
|
||||
|
|
|
@ -1,330 +0,0 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
|
||||
|
||||
// The upgrade module is used to download and install a new version of the
|
||||
// mig-agent. It retrieves a binary from an HTTP location, validates its
|
||||
// checksum. Verifies that the binary version is different from the currently
|
||||
// running version. Install the binary and run it.
|
||||
//
|
||||
// At the end of the run, two mig-agent will be running on the same endpoint,
|
||||
// and the scheduler will take care of killing one of them. This module does
|
||||
// not attempt to kill the current mig-agent, in case the new one does not
|
||||
// connect properly.
|
||||
package upgrade /* import "mig.ninja/mig/modules/upgrade" */
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/kardianos/osext"
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mig.ninja/mig/modules"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
type module struct {
|
||||
}
|
||||
|
||||
func (m *module) NewRun() modules.Runner {
|
||||
return new(run)
|
||||
}
|
||||
|
||||
func init() {
|
||||
modules.Register("upgrade", new(module))
|
||||
}
|
||||
|
||||
type run struct {
|
||||
Parameters params
|
||||
Results modules.Result
|
||||
}
|
||||
|
||||
// JSON sample:
|
||||
// {
|
||||
// "module": "upgrade",
|
||||
// "parameters": {
|
||||
// "to_version": "201403031435-b9536d2",
|
||||
// "location": "https://download.mig.example.net/mig-agent-b9536d2-201403031435",
|
||||
// "checksum": "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab"
|
||||
// }
|
||||
// }
|
||||
type params map[string]map[string]string
|
||||
|
||||
type elements struct {
|
||||
OldPID int `json:"oldpid"`
|
||||
}
|
||||
|
||||
var stats statistics
|
||||
|
||||
type statistics struct {
|
||||
DownloadTime string `json:"downloadtime"`
|
||||
DownloadSize int64 `json:"downloadsize"`
|
||||
}
|
||||
|
||||
func (r run) ValidateParameters() (err error) {
|
||||
locre := regexp.MustCompile(`^https?://`)
|
||||
checksumre := regexp.MustCompile(`^[a-zA-Z0-9]{64}$`)
|
||||
for k, v := range r.Parameters {
|
||||
if v["to_version"] == "" {
|
||||
return fmt.Errorf("In %s, parameter 'to_version' is empty. Expecting version.", k, v["to_version"])
|
||||
}
|
||||
if !locre.MatchString(v["location"]) {
|
||||
return fmt.Errorf("In %s, parameter 'location' with value '%s' is invalid. Expecting URL.", k, v["location"])
|
||||
}
|
||||
if !checksumre.MatchString(v["checksum"]) {
|
||||
return fmt.Errorf("In %s, parameter 'checksum' with value '%s' is invalid. Expecting SHA256 checksum.", k, v["checksum"])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r run) Run(in modules.ModuleReader) (out string) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e))
|
||||
r.Results.Success = false
|
||||
buf, _ := json.Marshal(r.Results)
|
||||
out = string(buf[:])
|
||||
}
|
||||
}()
|
||||
err := modules.ReadInputParameters(in, &r.Parameters)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = r.ValidateParameters()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Extract the parameters that apply to this OS and Arch
|
||||
key := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
el, ok := r.Parameters[key]
|
||||
if !ok {
|
||||
panic("no upgrade instruction found for " + key)
|
||||
}
|
||||
|
||||
// Verify that the version we're told to upgrade to isn't the current one
|
||||
cversion, err := getCurrentVersion()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if cversion == el["to_version"] {
|
||||
panic("Agent is already running version " + cversion)
|
||||
}
|
||||
|
||||
// Download new agent binary from provided location
|
||||
binfd, err := downloadBinary(el["location"])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Verify checksum of the binary
|
||||
err = verifyChecksum(binfd, el["checksum"])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// grab the path before closing the file descriptor
|
||||
binPath := binfd.Name()
|
||||
|
||||
err = binfd.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Dry run of the binary to verify that the version is correct
|
||||
// but also that it can run without error
|
||||
err = verifyVersion(binPath, el["to_version"])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Move the binary of the new agent from tmp, to the correct destination
|
||||
agentBinPath, err := moveBinary(binPath, el["to_version"])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Launch the new agent and exit the module
|
||||
cmd := exec.Command(agentBinPath, "-u")
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
out = r.buildResults()
|
||||
return
|
||||
}
|
||||
|
||||
// Run the agent binary to obtain the current version
|
||||
func getCurrentVersion() (cversion string, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("getCurrentVersion() -> %v", e)
|
||||
}
|
||||
}()
|
||||
bin, err := osext.Executable()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
out, err := exec.Command(bin, "-V").Output()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(out) < 2 {
|
||||
panic("Failed to retrieve agent version.")
|
||||
}
|
||||
cversion = string(out[:len(out)-1])
|
||||
return
|
||||
}
|
||||
|
||||
// downloadBinary retrieves the data from a location and saves it to a temp file
|
||||
func downloadBinary(loc string) (tmpfd *os.File, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("downloadBinary() -> %v", e)
|
||||
}
|
||||
}()
|
||||
tmpfd, err = ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
start := time.Now()
|
||||
resp, err := http.Get(loc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
stats.DownloadSize, err = io.Copy(tmpfd, resp.Body)
|
||||
stats.DownloadTime = time.Since(start).String()
|
||||
resp.Body.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// verifyChecksum computes the hash of a file and compares it
|
||||
// to a checksum. If comparison fails, it returns an error.
|
||||
func verifyChecksum(fd *os.File, checksum string) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("verifyChecksum() -> %v", e)
|
||||
}
|
||||
}()
|
||||
var h hash.Hash
|
||||
h = sha256.New()
|
||||
buf := make([]byte, 4096)
|
||||
var offset int64 = 0
|
||||
for {
|
||||
block, err := fd.ReadAt(buf, offset)
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
if block == 0 {
|
||||
break
|
||||
}
|
||||
h.Write(buf[:block])
|
||||
offset += int64(block)
|
||||
}
|
||||
hexhash := fmt.Sprintf("%x", h.Sum(nil))
|
||||
if hexhash != checksum {
|
||||
return fmt.Errorf("Checksum validation failed. Got '%s', Expected '%s'.",
|
||||
hexhash, checksum)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// verifyVersion runs a binary and compares the returned version
|
||||
func verifyVersion(binPath, expectedVersion string) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("verifyVersion() -> %v", e)
|
||||
}
|
||||
}()
|
||||
os.Chmod(binPath, 0500)
|
||||
out, err := exec.Command(binPath, "-V").Output()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
binVersion := string(out[:len(out)-1])
|
||||
if binVersion != expectedVersion {
|
||||
return fmt.Errorf("Version mismatch. Got '%s', Expected '%s'.",
|
||||
binVersion, expectedVersion)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func moveBinary(binPath, version string) (linkloc string, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("moveBinary() -> %v", e)
|
||||
}
|
||||
}()
|
||||
var target string
|
||||
switch runtime.GOOS {
|
||||
case "linux", "darwin", "freebsd", "openbsd", "netbsd":
|
||||
target = fmt.Sprintf("/sbin/mig-agent-%s", version)
|
||||
linkloc = "/sbin/mig-agent"
|
||||
case "windows":
|
||||
target = fmt.Sprintf("C:\\Program Files\\mig\\mig-agent-%s.exe", version)
|
||||
linkloc = "C:\\Program Files\\mig\\mig-agent.exe"
|
||||
default:
|
||||
err = fmt.Errorf("'%s' isn't a supported OS", runtime.GOOS)
|
||||
return
|
||||
}
|
||||
// copy the file (rename may not work if we're crossing partitions)
|
||||
srcfd, err := os.Open(binPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dstfd, err := os.Create(target)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(dstfd, srcfd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
srcfd.Close()
|
||||
dstfd.Close()
|
||||
err = os.Remove(binPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = os.Chmod(target, 0750)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// don't fail on removal of existing link, it may not exist
|
||||
os.Remove(linkloc)
|
||||
// create a symlink
|
||||
err = os.Symlink(target, linkloc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// buildResults transforms the ConnectedIPs map into a Results
|
||||
// map that is serialized in JSON and returned as a string
|
||||
func (r run) buildResults() string {
|
||||
var el elements
|
||||
el.OldPID = os.Getppid()
|
||||
r.Results.Elements = el
|
||||
if len(r.Results.Errors) == 0 {
|
||||
r.Results.Success = true
|
||||
}
|
||||
r.Results.Statistics = stats
|
||||
jsonOutput, err := json.Marshal(r.Results)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(jsonOutput[:])
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Contributor: Dustin J. Mitchell <dustin@mozilla.com>
|
||||
|
||||
package upgrade /* import "mig.ninja/mig/modules/upgrade" */
|
||||
|
||||
import (
|
||||
"mig.ninja/mig/testutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegistration(t *testing.T) {
|
||||
testutil.CheckModuleRegistration(t, "upgrade")
|
||||
}
|
Загрузка…
Ссылка в новой задаче