diff --git a/.circleci/config.yml b/.circleci/config.yml index dbb8c4f..9d4a529 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/golang:1.10.4-stretch + - image: circleci/golang:1.12.4-stretch working_directory: /go/src/github.com/nshttpd/mikrotik-exporter branches: only: @@ -11,5 +11,5 @@ jobs: steps: - checkout - setup_remote_docker: - version: 18.05.0-ce + version: 18.06.0-ce - run: make dockerhub diff --git a/.gitignore b/.gitignore index 9eefd20..d71d804 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ debug mikrotik-exporter +test-config.yml diff --git a/VERSION b/VERSION index c9a3ecf..f9cbc01 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.7-DEVEL +1.0.7 \ No newline at end of file diff --git a/collector/collector.go b/collector/collector.go index d9b4d07..d5b357b 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -1,7 +1,13 @@ package collector import ( + "crypto/md5" "crypto/tls" + "encoding/hex" + "errors" + "fmt" + "io" + "net" "sync" "time" @@ -206,12 +212,71 @@ func (c *collector) connectAndCollect(d *config.Device, ch chan<- prometheus.Met } func (c *collector) connect(d *config.Device) (*routeros.Client, error) { + var conn net.Conn + var err error + + log.WithField("device", d.Name).Debug("trying to Dial") if !c.enableTLS { - return routeros.DialTimeout(d.Address+apiPort, d.User, d.Password, c.timeout) + conn, err = net.Dial("tcp", d.Address+apiPort) + if err != nil { + return nil, err + } + // return routeros.DialTimeout(d.Address+apiPort, d.User, d.Password, c.timeout) + } else { + tlsCfg := &tls.Config{ + InsecureSkipVerify: c.insecureTLS, + } + conn, err = tls.Dial("tcp", d.Address+apiPortTLS, tlsCfg) + if err != nil { + return nil, err + } + } + log.WithField("device", d.Name).Debug("done dialing") + + client, err := routeros.NewClient(conn) + if err != nil { + return nil, err + } + log.WithField("device", d.Name).Debug("got client") + + log.WithField("device", d.Name).Debug("trying to login") + r, err := client.Run("/login", "=name="+d.User, "=password="+d.Password) + if err != nil { + return nil, err + } + ret, ok := r.Done.Map["ret"] + if !ok { + // Login method post-6.43 one stage, cleartext and no challenge + if r.Done != nil { + return client, nil + } + return nil, errors.New("RouterOS: /login: no ret (challenge) received") } - tls := &tls.Config{ - InsecureSkipVerify: c.insecureTLS, + // Login method pre-6.43 two stages, challenge + b, err := hex.DecodeString(ret) + if err != nil { + return nil, fmt.Errorf("RouterOS: /login: invalid ret (challenge) hex string received: %s", err) } - return routeros.DialTLSTimeout(d.Address+apiPortTLS, d.User, d.Password, tls, c.timeout) + + r, err = client.Run("/login", "=name="+d.User, "=response="+challengeResponse(b, d.Password)) + if err != nil { + return nil, err + } + log.WithField("device", d.Name).Debug("done wth login") + + return client, nil + + //tlsCfg := &tls.Config{ + // InsecureSkipVerify: c.insecureTLS, + //} + // return routeros.DialTLSTimeout(d.Address+apiPortTLS, d.User, d.Password, tlsCfg, c.timeout) +} + +func challengeResponse(cha []byte, password string) string { + h := md5.New() + h.Write([]byte{0}) + _, _ = io.WriteString(h, password) + h.Write(cha) + return fmt.Sprintf("00%x", h.Sum(nil)) } diff --git a/main.go b/main.go index 9e60638..7763a42 100644 --- a/main.go +++ b/main.go @@ -36,7 +36,7 @@ var ( withOptics = flag.Bool("with-optics", false, "retrieves optical diagnostic metrics") withWlanSTA = flag.Bool("with-wlansta", false, "retrieves connected wlan station metrics") withWlanIF = flag.Bool("with-wlanif", false, "retrieves wlan interface metrics") - timeout = flag.Duration("timeout", collector.DefaultTimeout, "timeout when connecting to routers") + timeout = flag.Duration("timeout", collector.DefaultTimeout, "timeout when connecting to devices") tls = flag.Bool("tls", false, "use tls to connect to routers") insecure = flag.Bool("insecure", false, "skips verification of server certificate when using TLS (not recommended)") cfg *config.Config @@ -118,11 +118,11 @@ func startServer() { http.Handle(*metricsPath, h) http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("ok")) + _, _ = w.Write([]byte("ok")) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(` + _, _ = w.Write([]byte(` Mikrotik Exporter

Mikrotik Exporter