Пример #1
0
// For mig.RunnerResult r, get the results associated with this job from the
// API.
func getResults(r mig.RunnerResult) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("getResults() -> %v", e)
		}
	}()

	mlog("fetching results for %v/%.0f", r.EntityName, r.Action.ID)

	cli, err := client.NewClient(ctx.ClientConf, "mig-runner-results")
	if err != nil {
		panic(err)
	}
	results, err := cli.FetchActionResults(r.Action)
	if err != nil {
		panic(err)
	}
	mlog("%v/%.0f: fetched results, %v commands returned", r.EntityName, r.Action.ID, len(results))

	// Store the results
	outpath, err := getResultsStoragePath(r.EntityName)
	if err != nil {
		panic(err)
	}
	outpath = path.Join(outpath, fmt.Sprintf("%.0f.json", r.Action.ID))
	fd, err := os.Create(outpath)
	if err != nil {
		panic(err)
	}
	defer fd.Close()

	r.Commands = results
	buf, err := json.Marshal(r)
	if err != nil {
		panic(err)
	}
	_, err = fd.Write(buf)
	if err != nil {
		panic(err)
	}
	mlog("wrote results for %.0f to %v", r.Action.ID, outpath)

	// If a plugin has been configured on the result set, call the
	// plugin on the data.
	if r.UsePlugin != "" {
		err = runPlugin(r)
		if err != nil {
			panic(err)
		}
	} else {
		mlog("no output plugin for %.0f, skipping plugin processing", r.Action.ID)
	}

	return nil
}
Пример #2
0
func main() {
	var cconf client.Configuration

	if len(os.Args) != 2 {
		fmt.Printf("usage: %v [- | indicator_post_url]\n", os.Args[0])
		os.Exit(1)
	}
	postOut = os.Args[1]

	dbg := os.Getenv("DEBUG")
	if dbg != "" {
		noVerify = true
	}

	confpath := path.Join(client.FindHomedir(), ".migrc")
	cconf, err := client.ReadConfiguration(confpath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
	cconf, err = client.ReadEnvConfiguration(cconf)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	cli, err := client.NewClient(cconf, "migindicators")
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	agents, err := cli.EvaluateAgentTarget(agentTarget)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
	var indparam slib.IndicatorParams
	for _, agt := range agents {
		ind, err := agentToIndicator(agt)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error converting agent to indicator: %v", err)
			continue
		}
		indparam.Indicators = append(indparam.Indicators, ind)
	}
	err = sendIndicators(indparam)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}
Пример #3
0
func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "%s - Search for MIG Agents\n"+
			"Usage: %s name='some.agent.example.net' OR name='some.other.agent.example.com'\n",
			os.Args[0], os.Args[0])
		flag.PrintDefaults()
	}
	var err error
	homedir := client.FindHomedir()
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var showversion = flag.Bool("V", false, "Show build version and exit")
	flag.Parse()

	if *showversion {
		fmt.Println(mig.Version)
		os.Exit(0)
	}

	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "agent-search-"+mig.Version)
	if err != nil {
		panic(err)
	}
	agents, err := cli.EvaluateAgentTarget(strings.Join(flag.Args(), " "))
	if err != nil {
		panic(err)
	}
	fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; operator; ident; publicip; addresses")
	for _, agt := range agents {
		operator := "unknown"
		if _, ok := agt.Tags.(map[string]interface{})["operator"]; ok {
			operator = agt.Tags.(map[string]interface{})["operator"].(string)
		}
		fmt.Printf("\"%s\"; \"%.0f\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"; \"%d\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"\n",
			agt.Name, agt.ID, agt.Status, agt.Version, agt.Mode, agt.Env.OS, agt.Env.Arch, agt.PID, agt.StartTime.Format(time.RFC3339),
			agt.HeartBeatTS.Format(time.RFC3339), operator, agt.Env.Ident, agt.Env.PublicIP, agt.Env.Addresses)
	}
}
Пример #4
0
func main() {
	var (
		conf                                           client.Configuration
		cli                                            client.Client
		err                                            error
		op                                             mig.Operation
		a                                              mig.Action
		migrc, show, render, target, expiration, afile string
		verbose                                        bool
		modargs                                        []string
		run                                            interface{}
	)
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "%v\n", e)
		}
	}()
	homedir := client.FindHomedir()
	fs := flag.NewFlagSet("mig flag", flag.ContinueOnError)
	fs.Usage = continueOnFlagError
	fs.StringVar(&migrc, "c", homedir+"/.migrc", "alternative configuration file")
	fs.StringVar(&show, "show", "found", "type of results to show")
	fs.StringVar(&render, "render", "text", "results rendering mode")
	fs.StringVar(&target, "t", fmt.Sprintf("status='%s' AND mode='daemon'", mig.AgtStatusOnline), "action target")
	fs.StringVar(&expiration, "e", "300s", "expiration")
	fs.StringVar(&afile, "i", "/path/to/file", "Load action from file")
	fs.BoolVar(&verbose, "v", false, "Enable verbose output")

	// if first argument is missing, or is help, print help
	// otherwise, pass the remainder of the arguments to the module for parsing
	// this client is agnostic to module parameters
	if len(os.Args) < 2 || os.Args[1] == "help" || os.Args[1] == "-h" || os.Args[1] == "--help" {
		usage()
	}

	if len(os.Args) < 2 || os.Args[1] == "-V" {
		fmt.Println(version)
		os.Exit(0)
	}

	// when reading the action from a file, go directly to launch
	if os.Args[1] == "-i" {
		err = fs.Parse(os.Args[1:])
		if err != nil {
			panic(err)
		}
		if afile == "/path/to/file" {
			panic("-i flag must take an action file path as argument")
		}
		a, err = mig.ActionFromFile(afile)
		if err != nil {
			panic(err)
		}
		fmt.Fprintf(os.Stderr, "[info] launching action from file, all flags are ignored\n")
		goto readytolaunch
	}

	// arguments parsing works as follow:
	// * os.Args[1] must contain the name of the module to launch. we first verify
	//   that a module exist for this name and then continue parsing
	// * os.Args[2:] contains both global options and module parameters. We parse the
	//   whole []string to extract global options, and module parameters will be left
	//   unparsed in fs.Args()
	// * fs.Args() with the module parameters is passed as a string to the module parser
	//   which will return a module operation to store in the action
	op.Module = os.Args[1]
	if _, ok := modules.Available[op.Module]; !ok {
		panic("Unknown module " + op.Module)
	}

	// -- Ugly hack Warning --
	// Parse() will fail on the first flag that is not defined, but in our case module flags
	// are defined in the module packages and not in this program. Therefore, the flag parse error
	// is expected. Unfortunately, Parse() writes directly to stderr and displays the error to
	// the user, which confuses them. The right fix would be to prevent Parse() from writing to
	// stderr, since that's really the job of the calling program, but in the meantime we work around
	// it by redirecting stderr to null before calling Parse(), and put it back to normal afterward.
	// for ref, issue is at https://github.com/golang/go/blob/master/src/flag/flag.go#L793
	fs.SetOutput(os.NewFile(uintptr(87592), os.DevNull))
	err = fs.Parse(os.Args[2:])
	fs.SetOutput(nil)
	if err != nil {
		// ignore the flag not defined error, which is expected because
		// module parameters are defined in modules and not in main
		if len(err.Error()) > 30 && err.Error()[0:29] == "flag provided but not defined" {
			// requeue the parameter that failed
			modargs = append(modargs, err.Error()[31:])
		} else {
			// if it's another error, panic
			panic(err)
		}
	}
	for _, arg := range fs.Args() {
		modargs = append(modargs, arg)
	}
	run = modules.Available[op.Module].NewRun()
	if _, ok := run.(modules.HasParamsParser); !ok {
		fmt.Fprintf(os.Stderr, "[error] module '%s' does not support command line invocation\n", op.Module)
		os.Exit(2)
	}
	op.Parameters, err = run.(modules.HasParamsParser).ParamsParser(modargs)
	if err != nil || op.Parameters == nil {
		panic(err)
	}
	// If running against the local target, don't post the action to the MIG API
	// but run it locally instead.
	if target == "local" {
		msg, err := modules.MakeMessage(modules.MsgClassParameters, op.Parameters)
		if err != nil {
			panic(err)
		}
		out := run.(modules.Runner).Run(bytes.NewBuffer(msg))
		if len(out) == 0 {
			panic("got empty results, run failed")
		}
		if _, ok := run.(modules.HasResultsPrinter); ok {
			var modres modules.Result
			err := json.Unmarshal([]byte(out), &modres)
			if err != nil {
				panic(err)
			}
			outRes, err := run.(modules.HasResultsPrinter).PrintResults(modres, true)
			if err != nil {
				panic(err)
			}
			for _, resLine := range outRes {
				fmt.Println(resLine)
			}
		} else {
			out = fmt.Sprintf("%s\n", out)
		}
		os.Exit(0)
	}

	a.Operations = append(a.Operations, op)

	for _, arg := range os.Args[1:] {
		a.Name += arg + " "
	}
	a.Target = target

readytolaunch:
	// instanciate an API client
	conf, err = client.ReadConfiguration(migrc)
	if err != nil {
		panic(err)
	}
	cli, err = client.NewClient(conf, "cmd-"+version)
	if err != nil {
		panic(err)
	}

	if verbose {
		cli.EnableDebug()
	}

	// set the validity 60 second in the past to deal with clock skew
	a.ValidFrom = time.Now().Add(-60 * time.Second).UTC()
	period, err := time.ParseDuration(expiration)
	if err != nil {
		panic(err)
	}
	a.ExpireAfter = a.ValidFrom.Add(period)
	// add extra 60 seconds taken for clock skew
	a.ExpireAfter = a.ExpireAfter.Add(60 * time.Second).UTC()

	asig, err := cli.SignAction(a)
	if err != nil {
		panic(err)
	}
	a = asig

	// evaluate target before launch, give a change to cancel before going out to agents
	agents, err := cli.EvaluateAgentTarget(a.Target)
	if err != nil {
		panic(err)
	}
	fmt.Fprintf(os.Stderr, "\x1b[33m%d agents will be targeted. ctrl+c to cancel. launching in \x1b[0m", len(agents))
	for i := 5; i > 0; i-- {
		time.Sleep(1 * time.Second)
		fmt.Fprintf(os.Stderr, "\x1b[33m%d\x1b[0m ", i)
	}
	fmt.Fprintf(os.Stderr, "\x1b[33mGO\n\x1b[0m")

	// launch and follow
	a, err = cli.PostAction(a)
	if err != nil {
		panic(err)
	}
	c := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		err = cli.FollowAction(a)
		if err != nil {
			panic(err)
		}
		done <- true
	}()
	select {
	case <-c:
		fmt.Fprintf(os.Stderr, "stop following action. agents may still be running. printing available results:\n")
		goto printresults
	case <-done:
		goto printresults
	}
printresults:
	err = cli.PrintActionResults(a, show, render)
	if err != nil {
		panic(err)
	}
}
Пример #5
0
func main() {
	var err error
	defer func() {
		if e := recover(); e != nil {
			fmt.Printf("FATAL: %v\n", e)
		}
	}()
	homedir := client.FindHomedir()
	var Usage = func() {
		fmt.Fprintf(os.Stderr,
			"Mozilla InvestiGator Action Generator\n"+
				"usage: %s -i <input file>\n\n"+
				"Command line to generate and sign MIG Actions.\n"+
				"Configuration is read from ~/.migrc by default.\n\n"+
				"Options:\n",
			os.Args[0])
		flag.PrintDefaults()
	}

	// command line options
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var pretty = flag.Bool("p", false, "Print signed action in pretty JSON format")
	var urlencode = flag.Bool("urlencode", false, "URL Encode marshalled JSON before printing it (implies '-p')")
	var file = flag.String("i", "/path/to/file", "Load action from file")
	var target = flag.String("t", "some.target.example.net", "Set the target of the action")
	var validfrom = flag.String("validfrom", "now", "(optional) set an ISO8601 date the action will be valid from. If unset, use 'now'.")
	var expireafter = flag.String("expireafter", "30m", "(optional) set a validity duration for the action. If unset, use '30m'.")
	var nolaunch = flag.Bool("nolaunch", false, "Don't launch the action. Print it and exit. (implies '-p')")
	var showversion = flag.Bool("V", false, "Show build version and exit")
	flag.Parse()

	if *showversion {
		fmt.Println(version)
		os.Exit(0)
	}

	if *nolaunch {
		*pretty = true
	}

	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "generator-"+version)
	if err != nil {
		panic(err)
	}

	// We need a file to load the action from
	if *file == "/path/to/file" {
		fmt.Println("ERROR: Missing action file")
		Usage()
		os.Exit(1)
	}
	a, err := mig.ActionFromFile(*file)
	if err != nil {
		panic(err)
	}

	// set the dates
	if *validfrom == "now" {
		// for immediate execution, set validity one minute in the past
		a.ValidFrom = time.Now().Add(-60 * time.Second).UTC()
	} else {
		a.ValidFrom, err = time.Parse(time.RFC3339, *validfrom)
		if err != nil {
			panic(err)
		}
	}
	period, err := time.ParseDuration(*expireafter)
	if err != nil {
		log.Fatal(err)
	}
	a.ExpireAfter = a.ValidFrom.Add(period)

	if *target != "some.target.example.net" {
		a.Target = *target
	}

	asig, err := cli.SignAction(a)
	if err != nil {
		panic(err)
	}
	a = asig

	// if asked, pretty print the action
	var jsonAction []byte
	if *pretty {
		jsonAction, err = json.MarshalIndent(a, "", "\t")
		fmt.Printf("%s\n", jsonAction)
	} else {
		jsonAction, err = json.Marshal(a)
	}
	if err != nil {
		panic(err)
	}

	// if asked, url encode the action before marshaling it
	actionstr := string(jsonAction)
	if *urlencode {
		strJsonAction := string(jsonAction)
		actionstr = url.QueryEscape(strJsonAction)
		if *pretty {
			fmt.Println(actionstr)
		}
	}

	if !*nolaunch {
		a2, err := cli.PostAction(a)
		if err != nil {
			panic(err)
		}

		fmt.Printf("Successfully launched action %.0f\n", a2.ID)
	}
}
Пример #6
0
func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `%s <query> - Search for MIG Agents
Usage: %s "name='some.agent.example.net' OR name='some.other.agent.example.com'"

A search query is a SQL WHERE condition. It can filter on any field present in
the MIG Agents table.
	     Column      |           Type
	-----------------+-------------------------
	 id              | numeric
	 name            | character varying(2048)
	 queueloc        | character varying(2048)
	 mode            | character varying(2048)
	 version         | character varying(2048)
	 pid             | integer
	 starttime       | timestamp with time zone
	 destructiontime | timestamp with time zone
	 heartbeattime   | timestamp with time zone
	 status          | character varying(255)
	 environment     | json
	 tags            | json

The "environment" and "tags" fields are free JSON fields and can be queried using
Postgresql's JSON querying syntax.

Below is an example of environment document:
	{
	    "addresses": [
		"172.21.0.3/20",
		"fe80::3602:86ff:fe2b:6fdd/64"
	    ],
	    "arch": "amd64",
	    "ident": "Debian testing-updates sid",
	    "init": "upstart",
	    "isproxied": false,
	    "os": "linux",
	    "publicip": "172.21.0.3"
	}

Below is an example of tags document:
	{"operator":"linuxwall"}

EXAMPLE QUERIES
---------------

Agent name "myserver.example.net"
  $ mig-agent-search "name='myserver.example.net'"

All Linux agents:
  $ mig-agent-search "environment->>'os'='linux'"

Ubuntu agents running 32 bits
  $ mig-agent-search "environment->>'ident' LIKE 'Ubuntu%%' AND environment->>'arch'='386'

MacOS agents in datacenter SCL3
  $ mig-agent-search "environment->>'os'='darwin' AND name LIKE '%%\.scl3\.%%'

Agents with uptime greater than 30 days
  $ mig-agent-search "starttime < NOW() - INTERVAL '30 days'"

Linux agents in checkin mode that are currently idle but woke up in the last hour
  $ mig-agent-search "mode='checkin' AND environment->>'os'='linux' AND status='idle' AND starttime > NOW() - INTERVAL '1 hour'"

Agents operated by team "opsec"
  $ mig-agent-search "tags->>'operator'='opsec'"

Command line flags:
`,
			os.Args[0], os.Args[0])
		flag.PrintDefaults()
	}
	var err error
	homedir := client.FindHomedir()
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var showversion = flag.Bool("V", false, "Show build version and exit")
	flag.Parse()

	if *showversion {
		fmt.Println(mig.Version)
		os.Exit(0)
	}

	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "agent-search-"+mig.Version)
	if err != nil {
		panic(err)
	}
	agents, err := cli.EvaluateAgentTarget(strings.Join(flag.Args(), " "))
	if err != nil {
		panic(err)
	}
	fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; tags; environment")
	for _, agt := range agents {
		tags, err := json.Marshal(agt.Tags)
		if err != nil {
			panic(err)
		}
		env, err := json.Marshal(agt.Env)
		if err != nil {
			panic(err)
		}
		fmt.Printf("%s; %.0f; %s; %s; %s; %s; %s; %d; %s; %s; %s; %s\n",
			agt.Name, agt.ID, agt.Status, agt.Version, agt.Mode, agt.Env.OS, agt.Env.Arch, agt.PID, agt.StartTime.Format(time.RFC3339),
			agt.HeartBeatTS.Format(time.RFC3339), tags, env)
	}
}
Пример #7
0
func main() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", e)
			os.Exit(1)
		}
	}()

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `%s <query> - Search for MIG Agents
Usage: %s -p "console style query" | -t "target style query"

The -p or -t flag must be specified.

CONSOLE MODE QUERY
------------------

The console mode query allows specific of a query string as would be passed
in mig-console using "search agent". It returns all matching agents.

EXAMPLE CONSOLE MODE QUERIES
----------------------------

All online agents:
  $ mig-agent-search -p "status=online"

All agents regardless of status:
  $ mig-agent-search -p "status=%%"

See the output of "search help" in mig-console for additional information on
how to format these queries.

TARGET MODE QUERY
-----------------

The target mode query allows specification of an agent targeting string as would
be passed to the -t flag using MIG command line. This evaluates agents using the
targeting string as the command line would, returning matching agents.

A search query is a SQL WHERE condition. It can filter on any field present in
the MIG Agents table.
	     Column      |           Type
	-----------------+-------------------------
	 id              | numeric
	 name            | character varying(2048)
	 queueloc        | character varying(2048)
	 mode            | character varying(2048)
	 version         | character varying(2048)
	 pid             | integer
	 starttime       | timestamp with time zone
	 destructiontime | timestamp with time zone
	 heartbeattime   | timestamp with time zone
	 status          | character varying(255)
	 environment     | json
	 tags            | json

The "environment" and "tags" fields are free JSON fields and can be queried using
Postgresql's JSON querying syntax.

Below is an example of environment document:
	{
	    "addresses": [
		"172.21.0.3/20",
		"fe80::3602:86ff:fe2b:6fdd/64"
	    ],
	    "arch": "amd64",
	    "ident": "Debian testing-updates sid",
	    "init": "upstart",
	    "isproxied": false,
	    "os": "linux",
	    "publicip": "172.21.0.3"
	}

Below is an example of tags document:
	{"operator":"linuxwall"}

EXAMPLE TARGET MODE QUERIES
---------------------------

Agent name "myserver.example.net"
  $ mig-agent-search -t "name='myserver.example.net'"

All Linux agents:
  $ mig-agent-search -t "environment->>'os'='linux'"

Ubuntu agents running 32 bits
  $ mig-agent-search -t "environment->>'ident' LIKE 'Ubuntu%%' AND environment->>'arch'='386'"

MacOS agents in datacenter SCL3
  $ mig-agent-search -t "environment->>'os'='darwin' AND name LIKE '%%\.scl3\.%%'"

Agents with uptime greater than 30 days
  $ mig-agent-search -t "starttime < NOW() - INTERVAL '30 days'"

Linux agents in checkin mode that are currently idle but woke up in the last hour
  $ mig-agent-search -t "mode='checkin' AND environment->>'os'='linux' AND status='idle' AND starttime > NOW() - INTERVAL '1 hour'"

Agents operated by team "opsec"
  $ mig-agent-search -t "tags->>'operator'='opsec'"

Command line flags:
`,
			os.Args[0], os.Args[0])
		flag.PrintDefaults()
	}

	var err error
	homedir := client.FindHomedir()
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var showversion = flag.Bool("V", false, "Show build version and exit")
	var paramSearch = flag.String("p", "", "Search using mig-console search style query")
	var targetSearch = flag.String("t", "", "Search using agent targeting string")
	flag.Parse()

	if *showversion {
		fmt.Println(mig.Version)
		os.Exit(0)
	}

	// Instantiate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "agent-search-"+mig.Version)
	if err != nil {
		panic(err)
	}

	if *paramSearch != "" {
		// Search using mig-console style keywords
		p, err := parseSearchQuery(*paramSearch)
		if err != nil {
			panic(err)
		}
		resources, err := cli.GetAPIResource("search?" + p.String())
		if err != nil {
			panic(err)
		}
		fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; tags; environment")
		for _, item := range resources.Collection.Items {
			for _, data := range item.Data {
				if data.Name != "agent" {
					continue
				}
				agt, err := client.ValueToAgent(data.Value)
				if err != nil {
					panic(err)
				}
				err = printAgent(agt)
				if err != nil {
					panic(err)
				}
			}
		}
	} else if *targetSearch != "" {
		// Search using an agent targeting string
		agents, err := cli.EvaluateAgentTarget(*targetSearch)
		if err != nil {
			panic(err)
		}
		fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; tags; environment")
		for _, agt := range agents {
			err = printAgent(agt)
			if err != nil {
				panic(err)
			}
		}
	} else {
		panic("must specify -p or -t, see help")
	}
	os.Exit(0)
}
Пример #8
0
func main() {
	var err error
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "FATAL: %v\n", e)
		}
	}()
	homedir := client.FindHomedir()
	// command line options
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var quiet = flag.Bool("q", false, "don't display banners and prompts")
	var showversion = flag.Bool("V", false, "show build version and exit")
	var verbose = flag.Bool("v", false, "verbose output, includes debug information and raw queries")
	flag.Parse()

	if *showversion {
		fmt.Println(mig.Version)
		os.Exit(0)
	}

	// silence extra output
	out := os.Stdout
	if *quiet {
		out.Close()
		out, err = os.Open(os.DevNull)
		if err != nil {
			panic(err)
		}
	}
	defer out.Close()

	fmt.Fprintf(out, "\x1b[32;1m"+banner+"\x1b[0m")

	// append a space after completion
	readline.CompletionAppendChar = 0x20
	// load history
	historyfile := homedir + "/.mig_history"
	fi, err := os.Stat(historyfile)
	if err == nil && fi.Size() > 0 {
		err = readline.LoadHistory(historyfile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "failed to load history from %s\n", historyfile)
		}
	}
	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "console-"+mig.Version)
	if err != nil {
		panic(err)
	}
	if *verbose {
		cli.EnableDebug()
	}
	// print platform status
	err = printStatus(cli)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprintf(out, "\nConnected to %s. Exit with \x1b[32;1mctrl+d\x1b[0m. Type \x1b[32;1mhelp\x1b[0m for help.\n", cli.Conf.API.URL)
	for {
		// completion
		var symbols = []string{"action", "agent", "create", "command", "help", "history",
			"exit", "manifest", "showcfg", "status", "investigator", "search", "query",
			"where", "and", "loader"}
		readline.Completer = func(query, ctx string) []string {
			var res []string
			for _, sym := range symbols {
				if strings.HasPrefix(sym, query) {
					res = append(res, sym)
				}
			}
			return res
		}

		input, err := readline.String("\x1b[32;1mmig>\x1b[0m ")
		if err == io.EOF {
			break
		}
		if err != nil {
			fmt.Println("error: ", err)
			break
		}
		orders := strings.Split(strings.TrimSpace(input), " ")
		switch orders[0] {
		case "action":
			if len(orders) == 2 {
				err = actionReader(input, cli)
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing action id in 'action <id>'")
			}
		case "agent":
			err = agentReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "create":
			if len(orders) == 2 {
				switch orders[1] {
				case "action":
					var a mig.Action
					err = actionLauncher(a, cli)
				case "investigator":
					err = investigatorCreator(cli)
				case "loader":
					err = loaderCreator(cli)
				case "manifest":
					err = manifestCreator(cli)
				default:
					fmt.Printf("unknown order 'create %s'\n", orders[1])
				}
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing order, must be 'create <action|investigator>'")
			}
		case "command":
			err = commandReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "exit":
			fmt.Printf("exit\n")
			goto exit
		case "help":
			fmt.Printf(`The following orders are available:
action <id>		enter interactive action reader mode for action <id>
agent <id>		enter interactive agent reader mode for agent <id>
create action		create a new action
create investigator	create a new investigator, will prompt for name and public key
create loader           create a new loader entry
create manifest         create a new manifest
command <id>		enter command reader mode for command <id>
exit			leave
help			show this help
history <count>		print last <count> entries in history. count=10 by default.
investigator <id>	enter interactive investigator management mode for investigator <id>
manifest <id>           enter manifest management mode for manifest <id>
query <uri>		send a raw query string, without the base url, to the api
search <search>		perform a search. see "search help" for more information.
showcfg			display running configuration
status			display platform status: connected agents, latest actions, ...
`)
		case "history":
			var count int64 = 10
			if len(orders) > 1 {
				count, err = strconv.ParseInt(orders[1], 10, 64)
				if err != nil {
					log.Println(err)
					break
				}
			}
			for i := readline.HistorySize(); i > 0 && count > 0; i, count = i-1, count-1 {
				fmt.Println(readline.GetHistory(i - 1))
			}
		case "investigator":
			err = investigatorReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "loader":
			err = loaderReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "manifest":
			err = manifestReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "query":
			fmt.Println("querying", orders[1])
			r, err := http.NewRequest("GET", orders[1], nil)
			if err != nil {
				panic(err)
			}
			resp, err := cli.Do(r)
			if err != nil {
				panic(err)
			}
			if err != nil || resp.Body == nil {
				log.Println("query failed")
			} else {
				body, err := ioutil.ReadAll(resp.Body)
				if err != nil {
					panic(err)
				}
				fmt.Printf("%s\n", body)
			}
		case "search":
			err = search(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "showcfg":
			fmt.Printf("homedir = %s\n[api]\n    url = %s\n[gpg]\n    home = %s\n    keyid = %s\n",
				cli.Conf.API.URL, cli.Conf.Homedir, cli.Conf.GPG.Home, cli.Conf.GPG.KeyID)
		case "status":
			err = printStatus(cli)
			if err != nil {
				log.Println(err)
			}
		case "":
			break
		default:
			fmt.Printf("Unknown order '%s'\n", orders[0])
		}
		readline.AddHistory(input)
	}
exit:
	fmt.Fprintf(out, footer)
	err = readline.SaveHistory(historyfile)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to save history to %s\n", historyfile)
	}
}
Пример #9
0
// Launch an action represented by a scheduler entity, this function takes
// care of submitting the action to the API and making a note of when to
// attempt to retrieve the results of the action.
func (e *entity) launchAction() (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("launchAction() -> %v", e)
		}
	}()
	// Load the action from the entity run directory
	actpath := path.Join(e.baseDir, "action.json")
	act, err := mig.ActionFromFile(actpath)
	if err != nil {
		panic(err)
	}
	act.Name = fmt.Sprintf("mig-runner: %v", e.name)

	cli, err := client.NewClient(ctx.ClientConf, "mig-runner")
	if err != nil {
		panic(err)
	}

	// Borrow some logic from the action generator. Set a validation
	// period starting in the past so our action starts immediately.
	window := time.Duration(-60 * time.Second)
	act.ValidFrom = time.Now().Add(window).UTC()
	exstring := defaultExpiry
	if e.cfg.Configuration.Expiry != "" {
		exstring = e.cfg.Configuration.Expiry
	}
	period, err := time.ParseDuration(exstring)
	if err != nil {
		panic(err)
	}
	// Add the window period to the desired expiry since our start
	// time begins in the past.
	period += -window
	act.ExpireAfter = act.ValidFrom.Add(period)
	act, err = cli.CompressAction(act)
	if err != nil {
		panic(err)
	}
	asig, err := cli.SignAction(act)
	if err != nil {
		panic(err)
	}
	act = asig

	res, err := cli.PostAction(act)
	if err != nil {
		panic(err)
	}
	mlog("%v: launched action %.0f", e.name, res.ID)

	// If we only dispatch this action we are done here.
	if e.cfg.Configuration.SendOnly {
		return nil
	}

	// Notify the results processor an action is in-flight
	re := mig.RunnerResult{}
	re.EntityName = e.name
	re.Action = res
	re.UsePlugin = e.cfg.Configuration.Plugin
	ctx.Channels.Results <- re

	return nil
}