Add web server update pieces

This commit is contained in:
davidnewhall2 2020-06-28 04:46:17 -07:00
parent 57470c3073
commit cbb4893970
7 changed files with 336 additions and 78 deletions

View File

@ -0,0 +1,131 @@
package inputunifi
import (
"time"
"github.com/pkg/errors"
"github.com/unifi-poller/unifi"
"github.com/unifi-poller/webserver"
)
/* Event collection. Events are also sent to the webserver for display. */
func (u *InputUnifi) collectControllerEvents(c *Controller) ([]interface{}, error) {
var (
logs = []interface{}{}
newLogs []interface{}
)
// Get the sites we care about.
sites, err := u.getFilteredSites(c)
if err != nil {
return nil, errors.Wrap(err, "unifi.GetSites()")
}
type caller func([]interface{}, []*unifi.Site, *Controller) ([]interface{}, error)
for _, call := range []caller{u.collectIDS, u.collectAnomalies, u.collectAlarms, u.collectEvents} {
if newLogs, err = call(logs, sites, c); err != nil {
return logs, err
}
logs = append(logs, newLogs...)
}
return logs, nil
}
func (u *InputUnifi) collectAlarms(logs []interface{}, sites []*unifi.Site, c *Controller) ([]interface{}, error) {
if *c.SaveAlarms {
events, err := c.Unifi.GetAlarms(sites)
if err != nil {
return logs, errors.Wrap(err, "unifi.GetAlarms()")
}
for _, e := range events {
logs = append(logs, e)
webserver.NewInputEvent(PluginName, c.URL+" alarms", &webserver.Event{Ts: e.Datetime, Msg: e.Msg,
Tags: map[string]string{"key": e.Key, "site_id": e.SiteID, "site_name": e.SiteName, "source": e.SourceName},
})
}
}
return logs, nil
}
func (u *InputUnifi) collectAnomalies(logs []interface{}, sites []*unifi.Site, c *Controller) ([]interface{}, error) {
if *c.SaveAnomal {
events, err := c.Unifi.GetAnomalies(sites, time.Now().Add(-time.Hour))
if err != nil {
return logs, errors.Wrap(err, "unifi.GetAnomalies()")
}
for _, e := range events {
logs = append(logs, e)
webserver.NewInputEvent(PluginName, c.URL+" anomalies", &webserver.Event{Ts: e.Datetime, Msg: e.Anomaly,
Tags: map[string]string{"site_name": e.SiteName, "source": e.SourceName},
})
}
}
return logs, nil
}
func (u *InputUnifi) collectEvents(logs []interface{}, sites []*unifi.Site, c *Controller) ([]interface{}, error) {
if *c.SaveEvents {
events, err := c.Unifi.GetEvents(sites, time.Hour)
if err != nil {
return logs, errors.Wrap(err, "unifi.GetEvents()")
}
for _, e := range events {
e := redactEvent(e, c.HashPII)
logs = append(logs, e)
webserver.NewInputEvent(PluginName, c.URL+" events", &webserver.Event{Msg: e.Msg, Ts: e.Datetime,
Tags: map[string]string{"key": e.Key, "site_id": e.SiteID, "site_name": e.SiteName, "source": e.SourceName},
})
}
}
return logs, nil
}
func (u *InputUnifi) collectIDS(logs []interface{}, sites []*unifi.Site, c *Controller) ([]interface{}, error) {
if *c.SaveIDS {
events, err := c.Unifi.GetIDS(sites, time.Now().Add(-time.Hour))
if err != nil {
return logs, errors.Wrap(err, "unifi.GetIDS()")
}
for _, e := range events {
logs = append(logs, e)
webserver.NewInputEvent(PluginName, c.URL+" ids", &webserver.Event{Ts: e.Datetime, Msg: e.Msg,
Tags: map[string]string{"key": e.Key, "site_id": e.SiteID, "site_name": e.SiteName, "source": e.SourceName},
})
}
}
return logs, nil
}
// redactEvent attempts to mask personally identying information from log messages.
// This currently misses the "msg" value entirely and leaks PII information.
func redactEvent(e *unifi.Event, hash *bool) *unifi.Event {
if !*hash {
return e
}
// metrics.Events[i].Msg <-- not sure what to do here.
e.DestIPGeo = unifi.IPGeo{}
e.SourceIPGeo = unifi.IPGeo{}
e.Host = RedactNamePII(e.Host, hash)
e.Hostname = RedactNamePII(e.Hostname, hash)
e.DstMAC = RedactMacPII(e.DstMAC, hash)
e.SrcMAC = RedactMacPII(e.SrcMAC, hash)
return e
}

View File

@ -82,62 +82,6 @@ func (u *InputUnifi) collectController(c *Controller) (*poller.Metrics, error) {
return metrics, err return metrics, err
} }
func (u *InputUnifi) collectControllerEvents(c *Controller) ([]interface{}, error) {
logs := []interface{}{}
// Get the sites we care about.
sites, err := u.getFilteredSites(c)
if err != nil {
return nil, errors.Wrap(err, "unifi.GetSites()")
}
if *c.SaveAnomal {
anom, err := c.Unifi.GetAnomalies(sites, time.Now().Add(-time.Hour))
if err != nil {
return nil, errors.Wrap(err, "unifi.GetAnomalies()")
}
for _, a := range anom {
logs = append(logs, a)
}
}
if *c.SaveAlarms {
alarms, err := c.Unifi.GetAlarms(sites)
if err != nil {
return nil, errors.Wrap(err, "unifi.GetAlarms()")
}
for _, a := range alarms {
logs = append(logs, a)
}
}
if *c.SaveEvents {
events, err := c.Unifi.GetEvents(sites, time.Hour)
if err != nil {
return nil, errors.Wrap(err, "unifi.GetEvents()")
}
for _, e := range events {
logs = append(logs, redactEvent(e, c.HashPII))
}
}
if *c.SaveIDS {
events, err := c.Unifi.GetIDS(sites, time.Now().Add(-time.Hour))
if err != nil {
return nil, errors.Wrap(err, "unifi.GetIDS()")
}
for _, e := range events {
logs = append(logs, e)
}
}
return logs, nil
}
func (u *InputUnifi) pollController(c *Controller) (*poller.Metrics, error) { func (u *InputUnifi) pollController(c *Controller) (*poller.Metrics, error) {
u.RLock() u.RLock()
defer u.RUnlock() defer u.RUnlock()
@ -169,6 +113,8 @@ func (u *InputUnifi) pollController(c *Controller) (*poller.Metrics, error) {
return nil, errors.Wrapf(err, "unifi.GetDevices(%s)", c.URL) return nil, errors.Wrapf(err, "unifi.GetDevices(%s)", c.URL)
} }
defer updateWeb(m)
return u.augmentMetrics(c, m), nil return u.augmentMetrics(c, m), nil
} }
@ -257,24 +203,6 @@ func extractDevices(metrics *Metrics) (*poller.Metrics, map[string]string, map[s
return m, devices, bssdIDs return m, devices, bssdIDs
} }
// redactEvent attempts to mask personally identying information from log messages.
// This currently misses the "msg" value entirely and leaks PII information.
func redactEvent(e *unifi.Event, hash *bool) *unifi.Event {
if !*hash {
return e
}
// metrics.Events[i].Msg <-- not sure what to do here.
e.DestIPGeo = unifi.IPGeo{}
e.SourceIPGeo = unifi.IPGeo{}
e.Host = RedactNamePII(e.Host, hash)
e.Hostname = RedactNamePII(e.Hostname, hash)
e.DstMAC = RedactMacPII(e.DstMAC, hash)
e.SrcMAC = RedactMacPII(e.SrcMAC, hash)
return e
}
// RedactNamePII converts a name string to an md5 hash (first 24 chars only). // RedactNamePII converts a name string to an md5 hash (first 24 chars only).
// Useful for maskiing out personally identifying information. // Useful for maskiing out personally identifying information.
func RedactNamePII(pii string, hash *bool) string { func RedactNamePII(pii string, hash *bool) string {

View File

@ -2,8 +2,13 @@ module github.com/unifi-poller/inputunifi
go 1.14 go 1.14
replace github.com/unifi-poller/webserver => ../webserver
replace github.com/unifi-poller/poller => ../poller
require ( require (
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/unifi-poller/poller v0.0.8-0.20200621214016-5d1ed3324a46 github.com/unifi-poller/poller v0.0.8-0.20200626082958-a9a7092a5684
github.com/unifi-poller/unifi v0.0.6-0.20200625090439-421046871a37 github.com/unifi-poller/unifi v0.0.6-0.20200625090439-421046871a37
github.com/unifi-poller/webserver v0.0.0-20200628114213-2b89a50ff1c0
) )

View File

@ -41,6 +41,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@ -108,6 +110,7 @@ github.com/unifi-poller/unifi v0.0.5-0.20200622073824-4a29471d80f6/go.mod h1:L1k
github.com/unifi-poller/unifi v0.0.6-0.20200625090439-421046871a37 h1:T2y8JWkjZd1vz2ZKu4vmmAk9s6PUwupuTldwhfww5xY= github.com/unifi-poller/unifi v0.0.6-0.20200625090439-421046871a37 h1:T2y8JWkjZd1vz2ZKu4vmmAk9s6PUwupuTldwhfww5xY=
github.com/unifi-poller/unifi v0.0.6-0.20200625090439-421046871a37/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8= github.com/unifi-poller/unifi v0.0.6-0.20200625090439-421046871a37/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=

View File

@ -14,8 +14,10 @@ import (
"github.com/unifi-poller/unifi" "github.com/unifi-poller/unifi"
) )
// PluginName is the name of this input plugin.
const PluginName = "unifi"
const ( const (
PluginName = "unifi" // PluginName is the name of this input plugin.
defaultURL = "https://127.0.0.1:8443" defaultURL = "https://127.0.0.1:8443"
defaultUser = "unifipoller" defaultUser = "unifipoller"
defaultPass = "unifipoller" defaultPass = "unifipoller"
@ -27,7 +29,7 @@ type InputUnifi struct {
*Config `json:"unifi" toml:"unifi" xml:"unifi" yaml:"unifi"` *Config `json:"unifi" toml:"unifi" xml:"unifi" yaml:"unifi"`
dynamic map[string]*Controller dynamic map[string]*Controller
sync.Mutex // to lock the map above. sync.Mutex // to lock the map above.
poller.Logger Logger poller.Logger
} }
// Controller represents the configuration for a UniFi Controller. // Controller represents the configuration for a UniFi Controller.
@ -44,7 +46,7 @@ type Controller struct {
User string `json:"user" toml:"user" xml:"user" yaml:"user"` User string `json:"user" toml:"user" xml:"user" yaml:"user"`
Pass string `json:"pass" toml:"pass" xml:"pass" yaml:"pass"` Pass string `json:"pass" toml:"pass" xml:"pass" yaml:"pass"`
URL string `json:"url" toml:"url" xml:"url" yaml:"url"` URL string `json:"url" toml:"url" xml:"url" yaml:"url"`
Sites []string `json:"sites,omitempty" toml:"sites,omitempty" xml:"site" yaml:"sites"` Sites []string `json:"sites" toml:"sites" xml:"site" yaml:"sites"`
Unifi *unifi.Unifi `json:"-" toml:"-" xml:"-" yaml:"-"` Unifi *unifi.Unifi `json:"-" toml:"-" xml:"-" yaml:"-"`
} }
@ -57,6 +59,7 @@ type Config struct {
Controllers []*Controller `json:"controllers" toml:"controller" xml:"controller" yaml:"controllers"` Controllers []*Controller `json:"controllers" toml:"controller" xml:"controller" yaml:"controllers"`
} }
// Metrics is simply a useful container for everything.
type Metrics struct { type Metrics struct {
TS time.Time TS time.Time
Sites []*unifi.Site Sites []*unifi.Site

View File

@ -9,6 +9,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/unifi-poller/poller" "github.com/unifi-poller/poller"
"github.com/unifi-poller/unifi" "github.com/unifi-poller/unifi"
"github.com/unifi-poller/webserver"
) )
var ( var (
@ -53,6 +54,8 @@ func (u *InputUnifi) Initialize(l poller.Logger) error {
u.logController(c) u.logController(c)
} }
webserver.UpdateInput(&webserver.Input{Name: PluginName, Config: formatConfig(u.Config)})
return nil return nil
} }

View File

@ -0,0 +1,185 @@
package inputunifi
import (
"fmt"
"strconv"
"time"
"github.com/unifi-poller/unifi"
"github.com/unifi-poller/webserver"
)
/* This code reformats our data to be displayed on the built-in web interface. */
func updateWeb(metrics *Metrics) {
webserver.UpdateInput(&webserver.Input{
Sites: formatSites(metrics.Sites),
Clients: formatClients(metrics.Clients),
Devices: formatDevices(metrics.Devices),
})
}
func formatConfig(config *Config) *Config {
return &Config{
Default: *formatControllers([]*Controller{&config.Default})[0],
Disable: config.Disable,
Dynamic: config.Dynamic,
Controllers: formatControllers(config.Controllers),
}
}
func formatControllers(controllers []*Controller) []*Controller {
fixed := []*Controller{}
for _, c := range controllers {
fixed = append(fixed, &Controller{
VerifySSL: c.VerifySSL,
SaveAnomal: c.SaveAnomal,
SaveAlarms: c.SaveAlarms,
SaveEvents: c.SaveEvents,
SaveIDS: c.SaveIDS,
SaveDPI: c.SaveDPI,
HashPII: c.HashPII,
SaveSites: c.SaveSites,
User: c.User,
Pass: strconv.FormatBool(c.Pass != ""),
URL: c.URL,
Sites: c.Sites,
})
}
return fixed
}
func formatSites(sites []*unifi.Site) (s webserver.Sites) {
for _, site := range sites {
s = append(s, &webserver.Site{
ID: site.ID,
Name: site.Name,
Desc: site.Desc,
Source: site.SourceName,
})
}
return s
}
func formatClients(clients []*unifi.Client) (c webserver.Clients) {
for _, client := range clients {
clientType, deviceMAC := "unknown", "unknown"
if client.ApMac != "" {
clientType = "wireless"
deviceMAC = client.ApMac
} else if client.SwMac != "" {
clientType = "wired"
deviceMAC = client.SwMac
}
if deviceMAC == "" {
deviceMAC = client.GwMac
}
c = append(c, &webserver.Client{
Name: client.Name,
SiteID: client.SiteID,
Source: client.SourceName,
MAC: client.Mac,
IP: client.IP,
Type: clientType,
DeviceMAC: deviceMAC,
Since: time.Unix(client.FirstSeen, 0),
Last: time.Unix(client.LastSeen, 0),
})
}
return c
}
func formatDevices(devices *unifi.Devices) (d webserver.Devices) {
for _, device := range devices.UAPs {
d = append(d, &webserver.Device{
Name: device.Name,
SiteID: device.SiteID,
Source: device.SourceName,
MAC: device.Mac,
IP: device.IP,
Type: device.Type,
Model: device.Model,
Version: device.Version,
Config: nil,
})
}
for _, device := range devices.UDMs {
d = append(d, &webserver.Device{
Name: device.Name,
SiteID: device.SiteID,
Source: device.SourceName,
MAC: device.Mac,
IP: device.IP,
Type: device.Type,
Model: device.Model,
Version: device.Version,
Config: nil,
})
}
for _, device := range devices.USWs {
d = append(d, &webserver.Device{
Name: device.Name,
SiteID: device.SiteID,
Source: device.SourceName,
MAC: device.Mac,
IP: device.IP,
Type: device.Type,
Model: device.Model,
Version: device.Version,
Config: nil,
})
}
for _, device := range devices.USGs {
d = append(d, &webserver.Device{
Name: device.Name,
SiteID: device.SiteID,
Source: device.SourceName,
MAC: device.Mac,
IP: device.IP,
Type: device.Type,
Model: device.Model,
Version: device.Version,
Config: nil,
})
}
return d
}
// Logf logs a message.
func (u *InputUnifi) Logf(msg string, v ...interface{}) {
webserver.NewInputEvent(PluginName, PluginName, &webserver.Event{
Ts: time.Now(),
Msg: fmt.Sprintf(msg, v...),
Tags: map[string]string{"type": "info"},
})
u.Logger.Logf(msg, v...)
}
// LogErrorf logs an error message.
func (u *InputUnifi) LogErrorf(msg string, v ...interface{}) {
webserver.NewInputEvent(PluginName, PluginName, &webserver.Event{
Ts: time.Now(),
Msg: fmt.Sprintf(msg, v...),
Tags: map[string]string{"type": "error"},
})
u.Logger.LogErrorf(msg, v...)
}
// LogDebugf logs a debug message.
func (u *InputUnifi) LogDebugf(msg string, v ...interface{}) {
webserver.NewInputEvent(PluginName, PluginName, &webserver.Event{
Ts: time.Now(),
Msg: fmt.Sprintf(msg, v...),
Tags: map[string]string{"type": "debug"},
})
u.Logger.LogDebugf(msg, v...)
}