From 62d83807543bed5eddb06a76c083892068d2b635 Mon Sep 17 00:00:00 2001 From: Nils Decker Date: Sun, 11 Dec 2022 15:26:15 +0100 Subject: [PATCH] Add TLS support and config from environment variables Needs a patched go-http-digest-auth-client, to allow a custom client that skips TLS verify --- README.md | 29 ++++++++-------- collector.go | 49 ++++++++++++++++++++------- fritzbox_upnp/action.go | 27 ++++++++++----- fritzbox_upnp/client.go | 34 +++++++++++++++++++ fritzbox_upnp/service.go | 54 ++++++++++++++---------------- go.mod | 6 ++-- go.sum | 20 +++-------- main.go | 71 +++++++++++++++++++++++++++++++++------- metrics.go | 5 ++- 9 files changed, 199 insertions(+), 96 deletions(-) create mode 100644 fritzbox_upnp/client.go diff --git a/README.md b/README.md index 6996fda..6a1dbf8 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ This exporter is known to work with the following models: go install ### Docker + git clone https://github.com/ndecker/fritzbox_exporter/ docker build -t fritzbox_exporter fritzbox_exporter @@ -39,21 +40,19 @@ This exporter is known to work with the following models: In the configuration of the Fritzbox the option "Statusinformationen über UPnP übertragen" in the dialog "Heimnetz > Heimnetzübersicht > Netzwerkeinstellungen" has to be enabled. -Usage: - - ./fritzbox_exporter -h - -gateway-address string - The hostname or IP of the FRITZ!Box (default "fritz.box") - -gateway-port int - The port of the FRITZ!Box UPnP service (default 49000) - -listen-address string - The address to listen on for HTTP requests. (default ":9133") - -password string - The password for the FRITZ!Box UPnP service - -test - print all available metrics to stdout - -username string - The user for the FRITZ!Box UPnP service +## Configuration + +| command line parameter | environment variable | default | | +|------------------------|---------------------------|-----------|------------------------------------------------| +| -listen-address | FRITZBOX_EXPORTER_LISTEN | :9133 | The address to listen on for HTTP requests | +| -gateway-address | FRITZBOX_DEVICE | fritz.box | The hostname or IP of the FRITZ!Box | +| -gateway-port | FRITZBOX_PORT | 49000 | The port of the FRITZ!Box UPnP service | +| -gateway-port | FRITZBOX_PORT_TLS | 49443 | The port of the FRITZ!Box TLS UPnP service | +| -username | FRITZBOX_USERNAME | | The user for the FRITZ!Box UPnP service | +| -password | FRITZBOX_PASSWORD | | The password for the FRITZ!Box UPnP service | +| -use-tls | FRITZBOX_USE_TLS | true | Use TLS/HTTPS connection to FRITZ!Box | +| -allow-selfsigned | FTITZBOX_ALLOW_SELFSIGNED | true | Allow selfsigned certificate from FRITZ!Box | +| -test | | | Print all available metrics to stdout and exit | ## Prerequisites diff --git a/collector.go b/collector.go index 997610b..c70d5fe 100644 --- a/collector.go +++ b/collector.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "sync" "time" @@ -18,41 +19,65 @@ var collectErrors = prometheus.NewCounter(prometheus.CounterOpts{ type FritzboxCollector struct { Parameters upnp.ConnectionParameters - sync.Mutex // protects Root - Root *upnp.Root + sync.Mutex // protects Roots + IGDRoot *upnp.Root + TR64Root *upnp.Root } // LoadServices tries to load the service information. Retries until success. func (fc *FritzboxCollector) LoadServices() { + igdRoot := fc.loadService(upnp.IGDServiceDescriptor) + log.Printf("%d IGD services loaded\n", len(igdRoot.Services)) + fc.Lock() + fc.IGDRoot = igdRoot + fc.Unlock() + + if fc.Parameters.Username == "" { + log.Printf("no username set: not loading TR64 services") + return + } + tr64Root := fc.loadService(upnp.TR64ServiceDescriptor) + log.Printf("%d TR64 services loaded\n", len(tr64Root.Services)) + fc.Lock() + fc.TR64Root = tr64Root + fc.Unlock() +} + +func (fc *FritzboxCollector) loadService(desc string) *upnp.Root { for { - root, err := upnp.LoadServices(fc.Parameters) + root, err := upnp.LoadServiceRoot(fc.Parameters, desc) if err != nil { fmt.Printf("cannot load services: %s\n", err) time.Sleep(serviceLoadRetryTime) continue } - - fmt.Printf("services loaded\n") - - fc.Lock() - fc.Root = root - fc.Unlock() - return + return root } } func (fc *FritzboxCollector) Describe(ch chan<- *prometheus.Desc) { - for _, m := range metrics { + for _, m := range IGDMetrics { + ch <- m.Desc + } + for _, m := range TR64Metrics { ch <- m.Desc } } func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) { fc.Lock() - root := fc.Root + root := fc.IGDRoot fc.Unlock() + fc.collectRoot(ch, root, IGDMetrics) + + fc.Lock() + root = fc.TR64Root + fc.Unlock() + fc.collectRoot(ch, root, TR64Metrics) +} +func (fc *FritzboxCollector) collectRoot(ch chan<- prometheus.Metric, root *upnp.Root, metrics []*Metric) { if root == nil { return // Services not loaded yet } diff --git a/fritzbox_upnp/action.go b/fritzbox_upnp/action.go index 1a14941..7ebad69 100644 --- a/fritzbox_upnp/action.go +++ b/fritzbox_upnp/action.go @@ -5,15 +5,13 @@ import ( "encoding/xml" "fmt" "io" - "log" "net/http" "strconv" "strings" - - dac "github.com/123Haynes/go-http-digest-auth-client" + "time" ) -// An UPNP Acton on a service +// Action is an UPNP Action on a service type Action struct { service *Service @@ -78,11 +76,11 @@ func (a *Action) Call() (Result, error) { req.Header.Set("Content-Type", textXml) req.Header.Set("SoapAction", action) - t := dac.NewTransport(a.service.Device.root.params.Username, a.service.Device.root.params.Password) + client := a.service.Device.root.client - resp, err := t.RoundTrip(req) + resp, err := client.Transport.RoundTrip(req) if err != nil { - log.Fatalln(err) + return nil, fmt.Errorf("cannod call %s: %w", a.Name, err) } defer closeIgnoringError(resp.Body) @@ -157,8 +155,21 @@ func convertResult(val string, arg *Argument) (interface{}, error) { return nil, err } return res, nil + case "i1", "i2", "i4": + res, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return nil, err + } + return res, nil + case "dateTime": + const timeLayout = "2006-01-02T15:04:05" + res, err := time.Parse(timeLayout, val) + if err != nil { + return nil, err + } + return res, nil default: - return nil, fmt.Errorf("unknown datatype: %s", arg.StateVariable.DataType) + return nil, fmt.Errorf("unknown datatype: %s: %s", arg.StateVariable.DataType, val) } } diff --git a/fritzbox_upnp/client.go b/fritzbox_upnp/client.go new file mode 100644 index 0000000..a2597d2 --- /dev/null +++ b/fritzbox_upnp/client.go @@ -0,0 +1,34 @@ +package fritzbox_upnp + +import ( + "crypto/tls" + "net/http" + + dac "github.com/ndecker/go-http-digest-auth-client" +) + +func setupClient(username string, password string, allowSelfsigned bool) *http.Client { + var t http.RoundTripper + t = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: allowSelfsigned, + }, + } + + if username != "" { + client := &http.Client{ + Transport: t, + } + + t = &dac.DigestTransport{ + Client: client, + Username: username, + Password: password, + } + } + + client := &http.Client{ + Transport: t, + } + return client +} diff --git a/fritzbox_upnp/service.go b/fritzbox_upnp/service.go index 137d188..34535c6 100644 --- a/fritzbox_upnp/service.go +++ b/fritzbox_upnp/service.go @@ -37,21 +37,25 @@ import ( const textXml = `text/xml; charset="utf-8"` const ( - igdServiceDescriptor = "igddesc.xml" - tr64ServiceDescriptor = "tr64desc.xml" + IGDServiceDescriptor = "igddesc.xml" + TR64ServiceDescriptor = "tr64desc.xml" ) var ErrInvalidSOAPResponse = errors.New("invalid SOAP response") type ConnectionParameters struct { - Device string // Hostname or IP - Port int - Username string - Password string + Device string // Hostname or IP + Port int + PortTLS int + UseTLS bool + Username string + Password string + AllowSelfSigned bool } // Root of the UPNP tree type Root struct { + client *http.Client baseUrl string params ConnectionParameters Device Device `xml:"device"` @@ -104,9 +108,9 @@ func (d *Device) fillServices(r *Root) error { for _, s := range d.Services { s.Device = d - response, err := http.Get(r.baseUrl + s.SCPDUrl) + response, err := r.client.Get(r.baseUrl + s.SCPDUrl) if err != nil { - return err + return fmt.Errorf("cannot load services for %s: %w", s.SCPDUrl, err) } var scpd scpdRoot @@ -149,11 +153,20 @@ func (d *Device) fillServices(r *Root) error { return nil } -// LoadServiceRoot loads a service descriptor and poplates a Service Root +// LoadServiceRoot loads a service descriptor and populates a Service Root func LoadServiceRoot(params ConnectionParameters, descriptor string) (*Root, error) { + var baseUrl string + if params.UseTLS { + baseUrl = fmt.Sprintf("https://%s:%d", params.Device, params.PortTLS) + } else { + baseUrl = fmt.Sprintf("http://%s:%d", params.Device, params.Port) + + } + var root = &Root{ params: params, - baseUrl: fmt.Sprintf("http://%s:%d", params.Device, params.Port), + client: setupClient(params.Username, params.Password, params.AllowSelfSigned), + baseUrl: baseUrl, Services: make(map[string]*Service), } @@ -162,7 +175,7 @@ func LoadServiceRoot(params ConnectionParameters, descriptor string) (*Root, err return nil, err } - igddesc, err := http.Get(descUrl) + igddesc, err := root.client.Get(descUrl) if err != nil { return nil, err } @@ -192,25 +205,6 @@ func LoadServiceRoot(params ConnectionParameters, descriptor string) (*Root, err return root, nil } -// LoadServices loads the services tree from a device. -func LoadServices(params ConnectionParameters) (*Root, error) { - root, err := LoadServiceRoot(params, igdServiceDescriptor) - if err != nil { - return nil, err // already annotated - } - - rootTR64, err := LoadServiceRoot(params, tr64ServiceDescriptor) - if err != nil { - return nil, err // already annotated - } - - for k, v := range rootTR64.Services { - root.Services[k] = v - } - - return root, nil -} - // closeIgnoringError closes c an ignores errors func closeIgnoringError(c io.Closer) { _ = c.Close() diff --git a/go.mod b/go.mod index 220a74d..fc98a71 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,12 @@ module github.com/ndecker/fritzbox_exporter go 1.18 -require github.com/prometheus/client_golang v1.14.0 +require ( + github.com/ndecker/go-http-digest-auth-client v0.4.0 + github.com/prometheus/client_golang v1.14.0 +) require ( - github.com/123Haynes/go-http-digest-auth-client v0.3.1-0.20171226204513-4c2ff1556cab // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/golang/protobuf v1.5.2 // indirect diff --git a/go.sum b/go.sum index 2aa107a..e08a057 100644 --- a/go.sum +++ b/go.sum @@ -31,10 +31,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/123Haynes/go-http-digest-auth-client v0.3.0 h1:KXsi65zWO1G6G9RLiObP8E+4JAeupGJi3A/lgt1wvBE= -github.com/123Haynes/go-http-digest-auth-client v0.3.0/go.mod h1:QBTQTKXpTpwXFy0QjvHU3Dkbtnxe/A2ER6EIvnI6mCg= -github.com/123Haynes/go-http-digest-auth-client v0.3.1-0.20171226204513-4c2ff1556cab h1:FQO+Wk1cfsj8IRyLQXorAfgyWwRnH7hs7/GXPu4xTtE= -github.com/123Haynes/go-http-digest-auth-client v0.3.1-0.20171226204513-4c2ff1556cab/go.mod h1:QBTQTKXpTpwXFy0QjvHU3Dkbtnxe/A2ER6EIvnI6mCg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -42,8 +38,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a h1:BtpsbiV638WQZwhA98cEZw2BsbnQJrbd0BI7tsy0W1c= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -58,6 +52,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -87,8 +82,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v0.0.0-20160817174113-f592bd283e9e h1:NsBEuFdvVJjikQS2+pq88q9LeMNgcCo99Ub1RFB/U1g= -github.com/golang/protobuf v0.0.0-20160817174113-f592bd283e9e/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -117,6 +110,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -158,12 +152,12 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/ndecker/go-http-digest-auth-client v0.4.0 h1:CFF9O7WkOuYq7bz1Ue21i8jR92C6Y5r68aU+zDlyTy0= +github.com/ndecker/go-http-digest-auth-client v0.4.0/go.mod h1:hYm/ylvg/9ZK/ENyShXzNk2tqj8Eq1WjUAceOXNhXuM= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.8.1-0.20161017123536-334af0119a8f h1:OEEy9S1AoiQa4dhkMO/A+Y1r/aIJpHYZuYOAMQkpwOI= -github.com/prometheus/client_golang v0.8.1-0.20161017123536-334af0119a8f/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -171,24 +165,18 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.0.0-20150212101744-fa8ad6fec335 h1:0E/5GnGmzoDCtmzTycjGDWW33H0UBmAhR0h+FC8hWLs= -github.com/prometheus/client_model v0.0.0-20150212101744-fa8ad6fec335/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20160801171955-ebdfc6da4652 h1:A/LzG6yfGZUKPD+rhVa5CAqQawH8KHuLlZgukD8dxLs= -github.com/prometheus/common v0.0.0-20160801171955-ebdfc6da4652/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20160411190841-abf152e5f3e9 h1:ex32PG6WhE5zviWS08vcXTwX2IkaH9zpeYZZvrmj3/U= -github.com/prometheus/procfs v0.0.0-20160411190841-abf152e5f3e9/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= diff --git a/main.go b/main.go index 4eaff6b..9df354a 100644 --- a/main.go +++ b/main.go @@ -22,22 +22,46 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "log" "net/http" + "os" + "strconv" ) func main() { - flagTest := flag.Bool("test", false, "print all available metrics to stdout") - flagAddr := flag.String("listen-address", ":9133", "The address to listen on for HTTP requests.") + listenAddress := GetEnv("FRITZBOX_EXPORTER_LISTEN", ":9133") + flag.StringVar(&listenAddress, "listen-address", listenAddress, "The address to listen on for HTTP requests.") - var parameters upnp.ConnectionParameters - flag.StringVar(¶meters.Device, "gateway-address", "fritz.box", "The hostname or IP of the FRITZ!Box") - flag.IntVar(¶meters.Port, "gateway-port", 49000, "The port of the FRITZ!Box UPnP service") - flag.StringVar(¶meters.Username, "username", "", "The user for the FRITZ!Box UPnP service") - flag.StringVar(¶meters.Password, "password", "", "The password for the FRITZ!Box UPnP service") + flagTestIGD := flag.Bool("test-igd", false, "print all available IGD metrics to stdout") + flagTestTR64 := flag.Bool("test-tr64", false, "print all available TR64 metrics to stdout") + + parameters := upnp.ConnectionParameters{ + Device: GetEnv("FRITZBOX_DEVICE", "fritz.box"), + Port: GetEnvInt("FRITZBOX_PORT", 49000), + PortTLS: GetEnvInt("FRITZBOX_PORT_TLS", 49443), + Username: GetEnv("FRITZBOX_USERNAME", ""), + Password: GetEnv("FRITZBOX_PASSWORD", ""), + UseTLS: GetEnv("FRITZBOX_USE_TLS", "true") == "true", + AllowSelfSigned: GetEnv("FRITZBOX_ALLOW_SELFSIGNED", "true") == "true", + } + + flag.StringVar(¶meters.Device, "gateway-address", parameters.Device, "The hostname or IP of the FRITZ!Box") + flag.IntVar(¶meters.Port, "gateway-port", parameters.Port, "The port of the FRITZ!Box UPnP service") + flag.IntVar(¶meters.PortTLS, "gateway-port-tls", parameters.PortTLS, "The TLS port of the FRITZ!Box UPnP service") + flag.StringVar(¶meters.Username, "username", parameters.Username, "The user for the FRITZ!Box UPnP service") + flag.StringVar(¶meters.Password, "password", parameters.Password, "The password for the FRITZ!Box UPnP service") + flag.BoolVar(¶meters.UseTLS, "use-tls", parameters.UseTLS, "Use TLS to connect to FRITZ!Box") + flag.BoolVar(¶meters.AllowSelfSigned, "allow-selfsigned", parameters.AllowSelfSigned, "Allow selfsigned certificate") flag.Parse() - if *flagTest { - test(parameters) + if *flagTestIGD { + test(parameters, upnp.IGDServiceDescriptor) + return + } + if *flagTestTR64 { + if parameters.Username == "" { + log.Fatal("no username/password set for TR64") + } + test(parameters, upnp.TR64ServiceDescriptor) return } @@ -51,16 +75,17 @@ func main() { prometheus.MustRegister(collectErrors) http.Handle("/metrics", promhttp.Handler()) - log.Fatal(http.ListenAndServe(*flagAddr, nil)) + log.Fatal(http.ListenAndServe(listenAddress, nil)) } -func test(p upnp.ConnectionParameters) { - root, err := upnp.LoadServices(p) +func test(p upnp.ConnectionParameters, desc string) { + root, err := upnp.LoadServiceRoot(p, desc) if err != nil { panic(err) } for _, s := range root.Services { + fmt.Println(s.SCPDUrl) for _, a := range s.Actions { if !a.IsGetOnly() { continue @@ -79,3 +104,25 @@ func test(p upnp.ConnectionParameters) { } } } + +func GetEnv(name string, def string) string { + env := os.Getenv(name) + if env != "" { + return env + } else { + return def + } +} + +func GetEnvInt(name string, def int) int { + env := os.Getenv(name) + if env != "" { + val, err := strconv.Atoi(env) + if err != nil { + log.Fatalf("cannot convert %s to int", env) + } + return val + } else { + return def + } +} diff --git a/metrics.go b/metrics.go index 176f629..23c6c52 100644 --- a/metrics.go +++ b/metrics.go @@ -12,7 +12,7 @@ type Metric struct { MetricType prometheus.ValueType } -var metrics = []*Metric{ +var IGDMetrics = []*Metric{ { Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", Action: "GetTotalPacketsReceived", @@ -147,6 +147,9 @@ var metrics = []*Metric{ ), MetricType: prometheus.GaugeValue, }, +} + +var TR64Metrics = []*Metric{ { Service: "urn:dslforum-org:service:WLANConfiguration:1", Action: "GetTotalAssociations",