329 lines
7.5 KiB
Go
329 lines
7.5 KiB
Go
package rules
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"iwarma.ru/console/correlator/events"
|
|
"net/http"
|
|
"strconv"
|
|
"text/template"
|
|
|
|
"iwarma.ru/console/correlator/config"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
const (
|
|
AssetActionType = "asset"
|
|
)
|
|
|
|
type AssetAction struct {
|
|
Name string
|
|
Description string
|
|
Manufacturer string
|
|
Model string
|
|
Ip string
|
|
Os string
|
|
Ports string
|
|
Vulnerabilities []string
|
|
Group string
|
|
AssetType string
|
|
Status string
|
|
|
|
templates struct {
|
|
name *template.Template
|
|
description *template.Template
|
|
model *template.Template
|
|
ip *template.Template
|
|
ports *template.Template
|
|
}
|
|
|
|
client *http.Client
|
|
token string
|
|
}
|
|
|
|
func (action *AssetAction) GetType() string {
|
|
return AssetActionType
|
|
}
|
|
|
|
func (action AssetAction) ToInterface() (map[string]interface{}, error) {
|
|
result := make(map[string]interface{})
|
|
|
|
result["type"] = AssetActionType
|
|
result["name"] = action.Name
|
|
result["description"] = action.Description
|
|
result["manufacturer"] = action.Manufacturer
|
|
result["model"] = action.Model
|
|
result["ip"] = action.Ip
|
|
result["os"] = action.Os
|
|
result["ports"] = action.Ports
|
|
|
|
if len(action.Vulnerabilities) > 0 {
|
|
vulnerabilities := make([]interface{}, 0)
|
|
for _, cur := range action.Vulnerabilities {
|
|
tmp, err := strconv.Atoi(cur)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vulnerabilities = append(vulnerabilities, tmp)
|
|
}
|
|
result["vulnerabilities"] = vulnerabilities
|
|
}
|
|
|
|
result["group"] = action.Group
|
|
result["asset_type"] = action.AssetType
|
|
result["status"] = action.Status
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (action AssetAction) MarshalJSON() ([]byte, error) {
|
|
data, err := action.ToInterface()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return json.Marshal(data)
|
|
}
|
|
|
|
func (action *AssetAction) UnmarshalJSON(b []byte) error {
|
|
var data interface{}
|
|
err := json.Unmarshal(b, &data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return action.ParseInterface(data)
|
|
}
|
|
|
|
func (action *AssetAction) ParseInterface(v interface{}) error {
|
|
|
|
m, ok := v.(map[string]interface{})
|
|
if !ok {
|
|
return fmt.Errorf("can't parse %v from %T", v, v)
|
|
}
|
|
|
|
t, ok := m["type"].(string)
|
|
if !ok {
|
|
return errors.New("no type")
|
|
}
|
|
|
|
if t != AssetActionType {
|
|
return fmt.Errorf("bad type, expect %v got %v", ExecActionType, t)
|
|
}
|
|
|
|
action.Name, ok = m["name"].(string)
|
|
if !ok {
|
|
return errors.New("no name")
|
|
}
|
|
|
|
action.Ip, ok = m["ip"].(string)
|
|
if !ok {
|
|
return errors.New("no IP")
|
|
}
|
|
|
|
// Not required
|
|
action.Description, _ = m["description"].(string)
|
|
action.Manufacturer, _ = m["manufacturer"].(string)
|
|
action.Model, _ = m["model"].(string)
|
|
action.Os, _ = m["os"].(string)
|
|
action.Ports, _ = m["ports"].(string)
|
|
action.Group, _ = m["group"].(string)
|
|
action.AssetType, _ = m["asset_type"].(string)
|
|
action.Status, _ = m["status"].(string)
|
|
|
|
// For array
|
|
if w, ok := m["vulnerabilities"]; ok {
|
|
switch v := w.(type) {
|
|
case []interface{}:
|
|
{
|
|
for _, cur := range v {
|
|
switch w := cur.(type) {
|
|
case string:
|
|
action.Vulnerabilities = append(action.Vulnerabilities, w)
|
|
case float64:
|
|
action.Vulnerabilities = append(action.Vulnerabilities, fmt.Sprintf("%v", w))
|
|
default:
|
|
return fmt.Errorf("bad vulnerabilities type: %T with value %v in interface %v", cur, cur, m)
|
|
}
|
|
}
|
|
}
|
|
case string:
|
|
action.Vulnerabilities = append(action.Vulnerabilities, v)
|
|
case float64:
|
|
action.Vulnerabilities = append(action.Vulnerabilities, fmt.Sprintf("%v", v))
|
|
default:
|
|
return fmt.Errorf("bad vulnerabilities type: %T with value %v in interface %v", v, v, m)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (action *AssetAction) Perform(events *[]*events.Event) error {
|
|
cl := log.WithFields(log.Fields{"type": IncidentActionType, "event_count": len(*events)})
|
|
cl.Debug("Start action")
|
|
defer cl.Debug("End action")
|
|
|
|
if events == nil || len(*events) == 0 {
|
|
cl.Error("No events")
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
|
|
// Check if we need to prepare templates
|
|
if action.templates.name == nil {
|
|
action.templates.name, err = template.New("Name").Parse(action.Name)
|
|
if err != nil {
|
|
cl.Errorf("Can't create name template: %v", err)
|
|
return err
|
|
}
|
|
|
|
action.templates.description, err = template.New("Description").Parse(action.Description)
|
|
if err != nil {
|
|
cl.Errorf("Can't create description template: %v", err)
|
|
return err
|
|
}
|
|
|
|
action.templates.ip, err = template.New("Ip").Parse(action.Ip)
|
|
if err != nil {
|
|
cl.Errorf("Can't create ip template: %v", err)
|
|
return err
|
|
}
|
|
|
|
action.templates.model, err = template.New("Model").Parse(action.Model)
|
|
if err != nil {
|
|
cl.Errorf("Can't create model template: %v", err)
|
|
return err
|
|
}
|
|
|
|
action.templates.ports, err = template.New("Ports").Parse(action.Ports)
|
|
if err != nil {
|
|
cl.Errorf("Can't create ports template: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Check if we need to prepare http client
|
|
if action.client == nil {
|
|
if viper.GetBool(config.ConsoleIgnoreSSLErrors) {
|
|
transport := &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
}
|
|
action.client = &http.Client{Transport: transport}
|
|
} else {
|
|
action.client = &http.Client{}
|
|
}
|
|
}
|
|
|
|
// Get auth token
|
|
if action.token == "" {
|
|
action.token, err = ObtainAuthToken()
|
|
if err != nil {
|
|
cl.Errorf("Can't get auth token: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Actual action
|
|
for _, event := range *events {
|
|
|
|
// Prepare body
|
|
interfaceBody, err := action.ToInterface()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Render templates
|
|
if _, ok := interfaceBody["name"]; ok {
|
|
interfaceBody["name"], err = renderTemplate(action.templates.name, event)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, ok := interfaceBody["description"]; ok {
|
|
interfaceBody["description"], err = renderTemplate(action.templates.description, event)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, ok := interfaceBody["ip"]; ok {
|
|
interfaceBody["ip"], err = renderTemplate(action.templates.ip, event)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, ok := interfaceBody["model"]; ok {
|
|
interfaceBody["model"], err = renderTemplate(action.templates.model, event)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, ok := interfaceBody["ports"]; ok {
|
|
interfaceBody["ports"], err = renderTemplate(action.templates.ports, event)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Add type to connect asset with sensor
|
|
interfaceBody["sensor"] = event.GetString("type")
|
|
|
|
// Send request
|
|
jsonBody, err := json.Marshal(interfaceBody)
|
|
if err != nil {
|
|
cl.Errorf("Can't serialize body: %v", err)
|
|
return err
|
|
}
|
|
|
|
cl.Debugf("Sending requset: %v", string(jsonBody))
|
|
|
|
request, err := http.NewRequest("POST", viper.GetString(config.ConsoleUrlAsset), bytes.NewBuffer(jsonBody))
|
|
if err != nil {
|
|
cl.Errorf("Can't create request: %v", err)
|
|
return err
|
|
}
|
|
|
|
// Set headers
|
|
request.Header.Set("Authorization", fmt.Sprintf("Token %v", action.token))
|
|
request.Header.Set("Content-type", "application/json")
|
|
|
|
// Do request
|
|
response, err := action.client.Do(request)
|
|
if err != nil {
|
|
cl.Errorf("Can't send request: %v", err)
|
|
return err
|
|
}
|
|
|
|
// Check result
|
|
body, err := ioutil.ReadAll(response.Body)
|
|
if err != nil {
|
|
cl.Errorf("Can't read response body: %v", err)
|
|
return err
|
|
}
|
|
err = response.Body.Close()
|
|
if err != nil {
|
|
cl.Warningf("%v", err)
|
|
}
|
|
|
|
cl.Debugf("Got response: %v", string(body))
|
|
|
|
if response.StatusCode != http.StatusCreated {
|
|
cl.Errorf("Bad status code: %v", response.Status)
|
|
return fmt.Errorf("bad server response code: %v", response.Status)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|