diff --git a/core/unifi/examples/ids.json b/core/unifi/examples/ids.json new file mode 100644 index 00000000..5dbb0fe2 --- /dev/null +++ b/core/unifi/examples/ids.json @@ -0,0 +1,66 @@ +{ + "_id": "5d2416c78f0385ccf1c6df44", + "archived": false, + "timestamp": 1562646211, + "flow_id": 1591464006222389, + "in_iface": "eth1", + "event_type": "alert", + "src_ip": "196.196.244.84", + "src_mac": "f0:9f:c2:c4:bb:f1", + "src_port": 51413, + "dest_ip": "192.168.3.2", + "dst_mac": "40:a8:f0:68:c3:58", + "dest_port": 36881, + "proto": "UDP", + "app_proto": "failed", + "host": "f0:22:22:22:22:22", + "usgip": "11.22.33.44", + "unique_alertid": "1341902566-2019-07-08T21:23:31.229941-0700", + "srcipCountry": "SE", + "dstipCountry": false, + "usgipCountry": "US", + "srcipGeo": { + "continent_code": "EU", + "country_code": "SE", + "country_code3": "SWE", + "country_name": "Sweden", + "region": "26", + "city": "Stockholm", + "postal_code": "168 65", + "latitude": 59.349998474121094, + "longitude": 17.91670036315918, + "dma_code": 0, + "area_code": 0 + }, + "dstipGeo": false, + "usgipGeo": { + "continent_code": "NA", + "country_code": "US", + "country_code3": "USA", + "country_name": "United States", + "region": "CA", + "city": "Other", + "postal_code": "99999", + "latitude": 99.139400482177734, + "longitude": -99.39669799804688, + "dma_code": 862, + "area_code": 999 + }, + "srcipASN": "AS42607 Internet Carrier Limited", + "dstipASN": "", + "usgipASN": "AS7922 Comcast Cable Communications, LLC", + "catname": "spamhaus", + "inner_alert_action": "allowed", + "inner_alert_gid": 1, + "inner_alert_signature_id": 2400022, + "inner_alert_rev": 2673, + "inner_alert_signature": "ET DROP Spamhaus DROP Listed Traffic Inbound group 23", + "inner_alert_category": "Misc Attack", + "inner_alert_severity": 2, + "key": "EVT_IPS_IpsAlert", + "subsystem": "www", + "site_id": "574e86994566ffb914a2683c", + "time": 1562646211000, + "datetime": "2019-07-09T04:23:31Z", + "msg": "IPS Alert 2: Misc Attack. Signature ET DROP Spamhaus DROP Listed Traffic Inbound group 23. From: 196.196.244.84:51413, to: 192.168.3.2:36881, protocol: UDP" +}, diff --git a/core/unifi/ids.go b/core/unifi/ids.go new file mode 100644 index 00000000..1a57d8f5 --- /dev/null +++ b/core/unifi/ids.go @@ -0,0 +1,151 @@ +package unifi + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" + + influx "github.com/influxdata/influxdb1-client/v2" + "github.com/pkg/errors" +) + +// IDS holds an Intrusion Prevention System Event. +type IDS struct { + ID string `json:"_id"` + Archived FlexBool `json:"archived"` + Timestamp int64 `json:"timestamp"` + FlowID int64 `json:"flow_id"` + InIface string `json:"in_iface"` + EventType string `json:"event_type"` + SrcIP string `json:"src_ip"` + SrcMac string `json:"src_mac"` + SrcPort int `json:"src_port,omitempty"` + DestIP string `json:"dest_ip"` + DstMac string `json:"dst_mac"` + DestPort int `json:"dest_port,omitempty"` + Proto string `json:"proto"` + AppProto string `json:"app_proto,omitempty"` + Host string `json:"host"` + Usgip string `json:"usgip"` + UniqueAlertid string `json:"unique_alertid"` + SrcipCountry string `json:"srcipCountry"` + DstipCountry FlexBool `json:"dstipCountry"` + UsgipCountry string `json:"usgipCountry"` + SrcipGeo struct { + ContinentCode string `json:"continent_code"` + CountryCode string `json:"country_code"` + CountryCode3 string `json:"country_code3"` + CountryName string `json:"country_name"` + Region string `json:"region"` + City string `json:"city"` + PostalCode string `json:"postal_code"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + DmaCode int64 `json:"dma_code"` + AreaCode int64 `json:"area_code"` + } `json:"srcipGeo"` + DstipGeo bool `json:"dstipGeo"` + UsgipGeo struct { + ContinentCode string `json:"continent_code"` + CountryCode string `json:"country_code"` + CountryCode3 string `json:"country_code3"` + CountryName string `json:"country_name"` + Region string `json:"region"` + City string `json:"city"` + PostalCode string `json:"postal_code"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + DmaCode int64 `json:"dma_code"` + AreaCode int64 `json:"area_code"` + } `json:"usgipGeo"` + SrcipASN string `json:"srcipASN"` + DstipASN string `json:"dstipASN"` + UsgipASN string `json:"usgipASN"` + Catname string `json:"catname"` + InnerAlertAction string `json:"inner_alert_action"` + InnerAlertGid int64 `json:"inner_alert_gid"` + InnerAlertSignatureID int64 `json:"inner_alert_signature_id"` + InnerAlertRev int64 `json:"inner_alert_rev"` + InnerAlertSignature string `json:"inner_alert_signature"` + InnerAlertCategory string `json:"inner_alert_category"` + InnerAlertSeverity int64 `json:"inner_alert_severity"` + Key string `json:"key"` + Subsystem string `json:"subsystem"` + SiteID string `json:"site_id"` + SiteName string `json:"-"` + Time int64 `json:"time"` + Datetime time.Time `json:"datetime"` + Msg string `json:"msg"` + IcmpType int64 `json:"icmp_type,omitempty"` + IcmpCode int64 `json:"icmp_code,omitempty"` +} + +// GetIDS returns Intrusion Detection Systems events. +// Returns all events that happened in site between from and to. +func (u *Unifi) GetIDS(sites []Site, from, to time.Time) ([]IDS, error) { + data := []IDS{} + for _, site := range sites { + var response struct { + Data []IDS `json:"data"` + } + u.DebugLog("Polling Controller, retreiving Unifi IDS/IPS Data, site %s (%s) ", site.Name, site.Desc) + URIpath := fmt.Sprintf(IDSEvents, site.Name) + params := fmt.Sprintf(`{"start":"%v","end":"%v","_limit":50000}`, from.UnixNano(), to.UnixNano()) + req, err := u.UniReq(URIpath, params) + if err != nil { + return nil, err + } + resp, err := u.Do(req) + if err != nil { + return nil, err + } + defer func() { + _ = resp.Body.Close() + }() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, errors.Errorf("invalid status code from server %s", resp.Status) + } + if err := json.Unmarshal(body, &response); err != nil { + return nil, err + } + for i := range response.Data { + response.Data[i].SiteName = site.SiteName + } + data = append(data, response.Data...) + } + return data, nil +} + +// Points generates intrusion detection datapoints for InfluxDB. +// These points can be passed directly to influx. +func (i IDS) Points() ([]*influx.Point, error) { + tags := map[string]string{ + "in_iface": i.InIface, + "event_type": i.EventType, + "proto": i.Proto, + "app_proto": i.AppProto, + "usgip": i.Usgip, + "country_code": i.SrcipGeo.CountryCode, + "country_name": i.SrcipGeo.CountryName, + "region": i.SrcipGeo.Region, + "city": i.SrcipGeo.City, + "postal_code": i.SrcipGeo.PostalCode, + "srcipASN": i.SrcipASN, + "usgipASN": i.UsgipASN, + "alert_category": i.InnerAlertCategory, + "subsystem": i.Subsystem, + "catname": i.Catname, + } + fields := map[string]interface{}{} + pt, err := influx.NewPoint("uap_vaps", tags, fields, i.Datetime) + if err != nil { + return nil, err + } + return []*influx.Point{pt}, nil +} diff --git a/core/unifi/types.go b/core/unifi/types.go index 9d668543..a7c14c0a 100644 --- a/core/unifi/types.go +++ b/core/unifi/types.go @@ -26,6 +26,8 @@ const ( UserGroupPath string = "/api/s/%s/rest/usergroup" // LoginPath is Unifi Controller Login API Path LoginPath string = "/api/login" + // IDSEvents returns Intrusion Detection Systems Events + IDSEvents string = "/api/s/%s/stat/ips/event" ) // Logger is a base type to deal with changing log outputs. Create a logger