From 10d17b4a319e91c8d4f46e61dc289af83cdb53d7 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 25 Aug 2019 01:54:19 -0700 Subject: [PATCH] Add reauth feature, remove errors library. --- Gopkg.lock | 9 --------- examples/MANUAL.md | 36 +++++++++++++++++++++--------------- examples/up.conf.example | 13 ++++++++++--- examples/up.json.example | 1 + examples/up.xml.example | 8 ++++++++ examples/up.yaml.example | 7 +++++++ unifipoller/config.go | 1 + unifipoller/dumper.go | 3 +-- unifipoller/start.go | 16 +++++++--------- unifipoller/unifi.go | 17 +++++++++++++---- 10 files changed, 69 insertions(+), 42 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index c0edbdc2..b005b0a3 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -21,14 +21,6 @@ pruneopts = "UT" revision = "fc22c7df067eefd070157f157893fbce961d6359" -[[projects]] - digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "UT" - revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" - version = "v0.8.1" - [[projects]] digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" name = "github.com/spf13/pflag" @@ -59,7 +51,6 @@ input-imports = [ "github.com/BurntSushi/toml", "github.com/influxdata/influxdb1-client/v2", - "github.com/pkg/errors", "github.com/spf13/pflag", "golift.io/unifi", "gopkg.in/yaml.v2", diff --git a/examples/MANUAL.md b/examples/MANUAL.md index d65445c5..e9201ac8 100644 --- a/examples/MANUAL.md +++ b/examples/MANUAL.md @@ -65,7 +65,7 @@ is provided so the application can be easily adapted to any environment. `Config File Parameters` - sites default: ["all"] + sites default: ["all"] This list of strings should represent the names of sites on the UniFi controller that will be polled for data. Pass `all` in the list to poll all sites. On startup, the application prints out all site names @@ -73,20 +73,20 @@ is provided so the application can be easily adapted to any environment. next to them. The cryptic names go into the config file `sites` list. The controller's first site is not cryptic and is named `default`. - interval default: 30s + interval default: 30s How often to poll the controller for updated client and device data. The UniFi Controller only updates traffic stats about every 30 seconds. - debug default: false + debug default: false This turns on time stamps and line numbers in logs, outputs a few extra lines of information while processing. - quiet default: false + quiet default: false Setting this to true will turn off per-device and per-interval logs. Only errors will be logged. Using this with debug=true adds line numbers to any error logs. - mode default: "influx" + mode default: "influx" * Value: influx This default mode runs this application as a daemon. It will poll the controller at the configured interval. Providing an invalid value @@ -101,7 +101,7 @@ is provided so the application can be easily adapted to any environment. This mode can also be combined with a "test database" in InfluxDB to give yourself a "test config file" you may run ad-hoc to test changes. - max_errors default: 0 + max_errors default: 0 If you restart the UniFI controller, the poller will lose access until it is restarted. Specifying a number greater than -1 for max_errors will cause the poller to exit when it reaches the error count specified. @@ -113,16 +113,16 @@ is provided so the application can be easily adapted to any environment. docker or launchd. The default setting of 0 will cause an exit after just 1 error. Recommended values are 0-5. - influx_url default: http://127.0.0.1:8086 + influx_url default: http://127.0.0.1:8086 This is the URL where the Influx web server is available. - influx_user default: unifi + influx_user default: unifi Username used to authenticate with InfluxDB. - influx_pass default: unifi + influx_pass default: unifi Password used to authenticate with InfluxDB. - influx_db default: unifi + influx_db default: unifi Custom database created in InfluxDB to use with this application. On first setup, log into InfluxDB and create access: $ influx -host localhost -port 8086 @@ -130,24 +130,30 @@ is provided so the application can be easily adapted to any environment. CREATE USER unifi WITH PASSWORD 'unifi' WITH ALL PRIVILEGES GRANT ALL ON unifi TO unifi - unifi_url default: https://127.0.0.1:8443 + unifi_url default: https://127.0.0.1:8443 This is the URL where the UniFi Controller is available. - unifi_user default: influxdb + unifi_user default: influxdb Username used to authenticate with UniFi controller. This should be a special service account created on the control with read-only access. - unifi_user no default ENV: UNIFI_PASSWORD + unifi_user no default ENV: UNIFI_PASSWORD Password used to authenticate with UniFi controller. This can also be set in an environment variable instead of a configuration file. - collect_ids default: false + collect_ids default: false Setting this parameter to true will enable collection of Intrusion Detection System data. IDS and IPS are the same data set. This is off by default because most controllers do not have this enabled. It also creates a lot of new metrics from controllers with a lot of IDS entries. - verify_ssl default: false + reauthenticate default: false + Setting this parameter to true will make UniFi Poller send a new login + request on every interval. This generates a new cookie. Some controller + or reverse proxy configurations require this. Do not enable it unless + your configuration causes the poller to be logged out after some time. + + verify_ssl default: false If your UniFi controller has a valid SSL certificate, you can enable this option to validate it. Otherwise, any SSL certificate is valid. diff --git a/examples/up.conf.example b/examples/up.conf.example index db884ce5..c7860e7a 100644 --- a/examples/up.conf.example +++ b/examples/up.conf.example @@ -54,7 +54,14 @@ unifi_pass = "4BB9345C-2341-48D7-99F5-E01B583FF77F" # Only useful if IDS or IPS are enabled on one of the sites. #collect_ids = false -# If your UniFi controller has a valid SSL certificate, you can enable -# this option to validate it. Otherwise, any SSL certificate is valid. -# If you don't know if you have a valid SSL cert, then you don't have one. +# Some controllers or reverse proxy configurations do not allow cookies to be +# re-user on every request (every interval). This setting provides a workaround +# That causes the poller to re-auth (login) to the controller on every interval. +# Only enable this if you get login errors after 30 seconds. This will generate +# a few more requests to your controller every interval. +#reauthenticate = false + +# If your UniFi controller has a valid SSL certificate (like lets encrypt), +# you can enable this option to validate it. Otherwise, any SSL certificate is +# valid. If you don't know if you have a valid SSL cert, then you don't have one. #verify_ssl = false diff --git a/examples/up.json.example b/examples/up.json.example index 39708b2f..db259063 100644 --- a/examples/up.json.example +++ b/examples/up.json.example @@ -13,5 +13,6 @@ "unifi_pass": "", "unifi_url": "https://127.0.0.1:8443", "collect_ids": false, + "reauthenticate": false, "verify_ssl": false } diff --git a/examples/up.xml.example b/examples/up.xml.example index e45965c6..cf17add2 100644 --- a/examples/up.xml.example +++ b/examples/up.xml.example @@ -77,6 +77,14 @@ --> false + false + diff --git a/examples/up.yaml.example b/examples/up.yaml.example index 3ddca849..748d0076 100644 --- a/examples/up.yaml.example +++ b/examples/up.yaml.example @@ -54,6 +54,13 @@ unifi_url: "https://127.0.0.1:8443" # Only useful if IDS or IPS are enabled on one of the sites. collect_ids: false +# Some controllers or reverse proxy configurations do not allow cookies to be +# re-user on every request (every interval). This setting provides a workaround +# That causes the poller to re-auth (login) to the controller on every interval. +# Only enable this if you get login errors after 30 seconds. This will generate +# a few more requests to your controller every interval. +reauthenticate: false + # If your UniFi controller has a valid SSL certificate, you can enable # this option to validate it. Otherwise, any SSL certificate is valid. verify_ssl: false diff --git a/unifipoller/config.go b/unifipoller/config.go index 4ee5a42a..dcd385f5 100644 --- a/unifipoller/config.go +++ b/unifipoller/config.go @@ -60,6 +60,7 @@ type Config struct { Quiet bool `json:"quiet,_omitempty" toml:"quiet,_omitempty" xml:"quiet" yaml:"quiet"` VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"` CollectIDS bool `json:"collect_ids" toml:"collect_ids" xml:"collect_ids" yaml:"collect_ids"` + ReAuth bool `json:"reauthenticate" toml:"reauthenticate" xml:"reauthenticate" yaml:"reauthenticate"` Mode string `json:"mode" toml:"mode" xml:"mode" yaml:"mode"` InfluxURL string `json:"influx_url,_omitempty" toml:"influx_url,_omitempty" xml:"influx_url" yaml:"influx_url"` InfluxUser string `json:"influx_user,_omitempty" toml:"influx_user,_omitempty" xml:"influx_user" yaml:"influx_user"` diff --git a/unifipoller/dumper.go b/unifipoller/dumper.go index f1f2a1d6..d2cc35a7 100644 --- a/unifipoller/dumper.go +++ b/unifipoller/dumper.go @@ -5,7 +5,6 @@ import ( "os" "strings" - "github.com/pkg/errors" "golift.io/unifi" ) @@ -35,7 +34,7 @@ func (u *UnifiPoller) DumpJSONPayload() (err error) { _, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping Path '%s':\n", apiPath) return u.PrintRawAPIJSON(apiPath) default: - return errors.New("must provide filter: devices, clients, other") + return fmt.Errorf("must provide filter: devices, clients, other") } } diff --git a/unifipoller/start.go b/unifipoller/start.go index e722da49..d8332ca2 100644 --- a/unifipoller/start.go +++ b/unifipoller/start.go @@ -12,10 +12,9 @@ import ( "github.com/BurntSushi/toml" influx "github.com/influxdata/influxdb1-client/v2" - "github.com/pkg/errors" - flag "github.com/spf13/pflag" + "github.com/spf13/pflag" "golift.io/unifi" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" ) // Start begins the application from a CLI. @@ -36,7 +35,7 @@ func Start() error { // ParseFlags runs the parser. func (u *UnifiPoller) ParseFlags(args []string) { - u.Flag = flag.NewFlagSet("unifi-poller", flag.ExitOnError) + u.Flag = pflag.NewFlagSet("unifi-poller", pflag.ExitOnError) u.Flag.Usage = func() { fmt.Println("Usage: unifi-poller [--config=filepath] [--version]") u.Flag.PrintDefaults() @@ -91,9 +90,11 @@ func (u *UnifiPoller) Run() (err error) { if err = u.GetUnifi(); err != nil { return err } + u.Logf("Polling UniFi Controller at %s v%s as user %s. Sites: %v", u.UnifiBase, u.ServerVersion, u.UnifiUser, u.Sites) if err = u.GetInfluxDB(); err != nil { return err } + u.Logf("Logging Measurements to InfluxDB at %s as user %s", u.InfluxURL, u.InfluxUser) switch strings.ToLower(u.Mode) { case "influxlambda", "lambdainflux", "lambda_influx", "influx_lambda": u.LogDebugf("Lambda Mode Enabled") @@ -112,9 +113,8 @@ func (u *UnifiPoller) GetInfluxDB() (err error) { Password: u.InfluxPass, }) if err != nil { - return errors.Wrap(err, "influxdb") + return fmt.Errorf("influxdb: %v", err) } - u.Logf("Logging Measurements to InfluxDB at %s as user %s", u.InfluxURL, u.InfluxUser) return nil } @@ -123,14 +123,12 @@ func (u *UnifiPoller) GetUnifi() (err error) { // Create an authenticated session to the Unifi Controller. u.Unifi, err = unifi.NewUnifi(u.UnifiUser, u.UnifiPass, u.UnifiBase, u.VerifySSL) if err != nil { - return errors.Wrap(err, "unifi controller") + return fmt.Errorf("unifi controller: %v", err) } u.Unifi.ErrorLog = u.LogErrorf // Log all errors. u.Unifi.DebugLog = u.LogDebugf // Log debug messages. - u.Logf("Authenticated to UniFi Controller at %s version %s as user %s", u.UnifiBase, u.ServerVersion, u.UnifiUser) if err := u.CheckSites(); err != nil { return err } - u.Logf("Polling UniFi Controller Sites: %v", u.Sites) return nil } diff --git a/unifipoller/unifi.go b/unifipoller/unifi.go index 37330686..7c80ebaf 100644 --- a/unifipoller/unifi.go +++ b/unifipoller/unifi.go @@ -7,7 +7,6 @@ import ( "time" influx "github.com/influxdata/influxdb1-client/v2" - "github.com/pkg/errors" "golift.io/unifi" ) @@ -49,9 +48,19 @@ func (u *UnifiPoller) PollController() error { log.Println("[INFO] Everything checks out! Poller started, interval:", u.Interval.Round(time.Second)) ticker := time.NewTicker(u.Interval.Round(time.Second)) for u.LastCheck = range ticker.C { - _ = u.CollectAndReport() + var err error + if u.ReAuth { + // Some users need to re-auth every interval because the cookie times out. + if err = u.GetUnifi(); err != nil { + u.LogError(err, "re-authenticating") + } + } + if err == nil { + // Only run this if the authentication procedure didn't return error. + _ = u.CollectAndReport() + } if u.MaxErrors >= 0 && u.errorCount > u.MaxErrors { - return errors.Errorf("reached maximum error count, stopping poller (%d > %d)", u.errorCount, u.MaxErrors) + return fmt.Errorf("reached maximum error count, stopping poller (%d > %d)", u.errorCount, u.MaxErrors) } } return nil @@ -131,7 +140,7 @@ func (u *UnifiPoller) ReportMetrics(metrics *Metrics) error { } err := u.Write(metrics.BatchPoints) if err != nil { - return errors.Wrap(err, "influxdb.Write(points)") + return fmt.Errorf("influxdb.Write(points): %v", err) } var fields, points int for _, p := range metrics.Points() {