[medium] add API endpoint to query agents

This commit is contained in:
Julien Vehent 2014-07-14 19:11:56 -04:00
Родитель 8d42d11915
Коммит 80d39565a8
4 изменённых файлов: 98 добавлений и 16 удалений

Просмотреть файл

@ -101,7 +101,7 @@ func main() {
s.HandleFunc("/command", getCommand).Methods("GET")
s.HandleFunc("/command/cancel/", describeCancelCommand).Methods("GET")
s.HandleFunc("/command/cancel/", cancelCommand).Methods("POST")
s.HandleFunc("/agent/dashboard", getAgentsDashboard).Methods("GET")
s.HandleFunc("/agent", getAgent).Methods("GET")
s.HandleFunc("/dashboard", getDashboard).Methods("GET")
// all set, start the http handler
@ -157,14 +157,6 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
panic(err)
}
resource.AddQuery(cljs.Query{
Rel: "Get agent dashboard",
Href: fmt.Sprintf("%s/agent/dashboard", ctx.Server.BaseURL),
})
if err != nil {
panic(err)
}
err = resource.AddLink(cljs.Link{
Rel: "create action",
Href: fmt.Sprintf("%s/action/create/", ctx.Server.BaseURL),
@ -214,6 +206,18 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
panic(err)
}
resource.AddQuery(cljs.Query{
Rel: "Query agent by ID",
Href: fmt.Sprintf("%s/agent", ctx.Server.BaseURL),
Prompt: "GET endpoint to query an agent by ID, using url parameter ?agentid=<numerical id>",
Data: []cljs.Data{
{Name: "agentid", Value: "[0-9]{1,20}", Prompt: "Agent ID"},
},
})
if err != nil {
panic(err)
}
resource.AddQuery(cljs.Query{
Rel: "Search stuff",
Href: fmt.Sprintf("%s/search", ctx.Server.BaseURL),
@ -566,7 +570,7 @@ func cancelCommand(respWriter http.ResponseWriter, request *http.Request) {
respond(501, resource, respWriter, request, opid)
}
func getAgentsDashboard(respWriter http.ResponseWriter, request *http.Request) {
func getAgent(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
@ -578,7 +582,43 @@ func getAgentsDashboard(respWriter http.ResponseWriter, request *http.Request) {
}
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getAgentsDashboard()"}.Debug()
}()
respond(501, resource, respWriter, request, opid)
agentID, err := strconv.ParseFloat(request.URL.Query()["agentid"][0], 64)
if err != nil {
err = fmt.Errorf("Wrong parameters 'agentid': '%v'", err)
panic(err)
}
// retrieve the command
var agt mig.Agent
if agentID > 0 {
agt, err = ctx.DB.AgentByID(agentID)
if err != nil {
if fmt.Sprintf("%v", err) == "Error while retrieving agent: 'sql: no rows in result set'" {
// not found, return 404
resource.SetError(cljs.Error{
Code: fmt.Sprintf("%.0f", opid),
Message: fmt.Sprintf("Agent ID '%.0f' not found", agentID)})
respond(404, resource, respWriter, request, opid)
return
} else {
panic(err)
}
}
} else {
// bad request, return 400
resource.SetError(cljs.Error{
Code: fmt.Sprintf("%.0f", opid),
Message: fmt.Sprintf("Invalid Agent ID '%.0f'", agentID)})
respond(400, resource, respWriter, request, opid)
return
}
// store the results in the resource
agentItem, err := agentToItem(agt)
if err != nil {
panic(err)
}
resource.AddItem(agentItem)
respond(200, resource, respWriter, request, opid)
}
func getDashboard(respWriter http.ResponseWriter, request *http.Request) {

Просмотреть файл

@ -85,6 +85,15 @@ func commandToItem(cmd mig.Command) (item cljs.Item, err error) {
return
}
// agentToItem receives an agent and returns an Item in Collection+JSON
func agentToItem(agt mig.Agent) (item cljs.Item, err error) {
item.Href = fmt.Sprintf("%s/agent?agentid=%.0f", ctx.Server.BaseURL, agt.ID)
item.Data = []cljs.Data{
{Name: "agent", Value: agt},
}
return
}
// agentsSumToItem receives an AgentsSum and returns an Item
// in the Collection+JSON format
func agentsSummaryToItem(sum []migdb.AgentsSum, count float64, ctx Context) (item cljs.Item, err error) {

Просмотреть файл

@ -83,6 +83,8 @@ func search(respWriter http.ResponseWriter, request *http.Request) {
if err != nil {
panic("after date not in RFC3339 format")
}
case "agentid":
p.AgentID = request.URL.Query()["agentid"][0]
case "agentname":
p.AgentName = request.URL.Query()["agentname"][0]
case "before":

Просмотреть файл

@ -68,6 +68,7 @@ type SearchParameters struct {
After time.Time `json:"after"`
Type string `json:"type"`
Report string `json:"report"`
AgentID string `json:"agentid"`
AgentName string `json:"agentname"`
ActionName string `json:"actionname"`
ActionID string `json:"actionid"`
@ -83,6 +84,7 @@ func NewSearchParameters() (p SearchParameters) {
p.Before = time.Date(9998, time.January, 11, 11, 11, 11, 11, time.UTC)
p.After = time.Date(11, time.January, 11, 11, 11, 11, 11, time.UTC)
p.AgentName = "%"
p.AgentID = "∞"
p.ActionName = "%"
p.ActionID = "∞"
p.CommandID = "∞"
@ -118,6 +120,18 @@ func (db *DB) SearchCommands(p SearchParameters) (commands []mig.Command, err er
return
}
}
var minAgentID float64 = 0
var maxAgentID float64 = 18446744073709551616 //2^64
if p.AgentID != "∞" {
minAgentID, err = strconv.ParseFloat(p.AgentID, 64)
if err != nil {
return
}
maxAgentID, err = strconv.ParseFloat(p.AgentID, 64)
if err != nil {
return
}
}
rows, err := db.c.Query(`SELECT commands.id, commands.status, commands.results, commands.starttime, commands.finishtime,
actions.id, actions.name, actions.target, actions.description, actions.threat,
actions.operations, actions.validfrom, actions.expireafter,
@ -130,11 +144,12 @@ func (db *DB) SearchCommands(p SearchParameters) (commands []mig.Command, err er
AND actions.name LIKE $5
AND actions.id >= $6 AND actions.id <= $7
AND agents.name LIKE $8
AND actions.threat->>'family' LIKE $9
AND commands.status LIKE $10
ORDER BY commands.id DESC LIMIT $11`,
AND agents.id >= $9 AND agents.id <= $10
AND actions.threat->>'family' LIKE $11
AND commands.status LIKE $12
ORDER BY commands.id DESC LIMIT $13`,
p.Before, p.After, minCommandID, maxCommandID, p.ActionName, minActionID, maxActionID,
p.AgentName, p.ThreatFamily, p.Status, uint64(p.Limit))
p.AgentName, minAgentID, maxAgentID, p.ThreatFamily, p.Status, uint64(p.Limit))
if err != nil {
err = fmt.Errorf("Error while finding commands: '%v'", err)
return
@ -257,7 +272,7 @@ func (db *DB) SearchAgents(p SearchParameters) (agents []mig.Agent, err error) {
WHERE agents.heartbeattime <= $1 AND agents.heartbeattime >= $2
AND agents.name LIKE $3
AND agents.status LIKE $4
ORDER BY agents.heartbeattime LIMIT $5`,
ORDER BY agents.heartbeattime DESC LIMIT $5`,
p.Before, p.After, p.AgentName, p.Status, uint64(p.Limit))
if err != nil {
err = fmt.Errorf("Error while finding agents: '%v'", err)
@ -691,6 +706,22 @@ func (db *DB) AgentByQueueAndPID(queueloc string, pid int) (agent mig.Agent, err
return
}
// AgentByID returns a single agent identified by its ID
func (db *DB) AgentByID(id float64) (agent mig.Agent, err error) {
err = db.c.QueryRow(`SELECT id, name, queueloc, os, version, pid, starttime, heartbeattime,
status FROM agents WHERE id=$1`, id).Scan(
&agent.ID, &agent.Name, &agent.QueueLoc, &agent.OS, &agent.Version, &agent.PID,
&agent.StartTime, &agent.HeartBeatTS, &agent.Status)
if err != nil {
err = fmt.Errorf("Error while retrieving agent: '%v'", err)
return
}
if err == sql.ErrNoRows {
return
}
return
}
// AgentsActiveSince returns an array of Agents that have sent a heartbeat between
// a point in time and now
func (db *DB) AgentsActiveSince(pointInTime time.Time) (agents []mig.Agent, err error) {