Skip to content

Commit

Permalink
add go.d/tor
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyam8 committed Aug 18, 2024
1 parent 1005176 commit bc3a380
Show file tree
Hide file tree
Showing 16 changed files with 877 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/collectors/python.d.plugin/python.d.conf
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ go_expvar: no
# smartd_log: yes
# spigotmc: yes
# traefik: yes
# tor: yes
# varnish: yes
# w1sensor: yes
# zscores: no
Expand Down Expand Up @@ -80,5 +79,6 @@ riakkv: no # Removed (replaced with go.d/riak).
sensors: no # Removed (replaced with go.d/sensors).
squid: no # Removed (replaced with go.d/squid).
tomcat: no # Removed (replaced with go.d/tomcat)
tor: no # Removed (replaced with go.d/tor).
puppet: no # Removed (replaced with go.d/puppet).
uwsgi: no # Removed (replaced with go.d/uwsgi).
1 change: 1 addition & 0 deletions src/go/plugin/go.d/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ see the appropriate collector readme.
| [systemdunits](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/systemdunits) | Systemd unit state |
| [tengine](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/tengine) | Tengine |
| [tomcat](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/tomcat) | Tomcat |
| [tor](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/tor) | Tor |
| [traefik](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/traefik) | Traefik |
| [upsd](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/upsd) | UPSd (Nut) |
| [unbound](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/unbound) | Unbound |
Expand Down
1 change: 1 addition & 0 deletions src/go/plugin/go.d/config/go.d.conf
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ modules:
# systemdunits: yes
# tengine: yes
# tomcat: yes
# tor: yes
# traefik: yes
# upsd: yes
# unbound: yes
Expand Down
7 changes: 7 additions & 0 deletions src/go/plugin/go.d/config/go.d/sd/docker.conf
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ classify:
expr: '{{ match "sp" .Image "*/squid */squid:*" }}'
- tags: "tengine"
expr: '{{ match "sp" .Image "*/tengine */tengine:*" }}'
- tags: "tor"
expr: '{{ and (eq .PrivatePort "9051") (match "sp" .Image "*/tor */tor:*") }}'
- tags: "tomcat"
expr: '{{ match "sp" .Image "tomcat tomcat:* */tomcat */tomcat:*" }}'
- tags: "vernemq"
Expand Down Expand Up @@ -243,6 +245,11 @@ compose:
module: tomcat
name: docker_{{.Name}}
url: http://{{.Address}}
- selector: "tor"
template: |
module: tor
name: docker_{{.Name}}
address: {{.Address}}
- selector: "vernemq"
template: |
module: vernemq
Expand Down
7 changes: 7 additions & 0 deletions src/go/plugin/go.d/config/go.d/sd/net_listeners.conf
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ classify:
expr: '{{ and (eq .Port "9001") (eq .Comm "supervisord") }}'
- tags: "tomcat"
expr: '{{ and (eq .Port "8080") (glob .Cmdline "*tomcat*") }}'
- tags: "tor"
expr: '{{ and (eq .Port "9051") (eq .Comm "tor") }}'
- tags: "traefik"
expr: '{{ and (eq .Port "80" "8080") (eq .Comm "traefik") }}'
- tags: "unbound"
Expand Down Expand Up @@ -477,6 +479,11 @@ compose:
module: tomcat
name: local
url: http://{{.Address}}
- selector: "tor"
template: |
module: tor
name: local
address: {{.Address}}
- selector: "unbound"
template: |
module: unbound
Expand Down
6 changes: 6 additions & 0 deletions src/go/plugin/go.d/config/go.d/tor.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## All available configuration options, their descriptions and default values:
## https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/tor#readme

#jobs:
# - name: local
# address: 127.0.0.1:9051
1 change: 1 addition & 0 deletions src/go/plugin/go.d/modules/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import (
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/systemdunits"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/tengine"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/tomcat"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/tor"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/traefik"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/unbound"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/upsd"
Expand Down
43 changes: 43 additions & 0 deletions src/go/plugin/go.d/modules/tor/charts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package tor

import (
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
)

const (
prioTraffic = module.Priority + iota
prioUptime
)

var charts = module.Charts{
trafficChart.Copy(),
uptimeChart.Copy(),
}

var trafficChart = module.Chart{
ID: "traffic",
Title: "Tor Traffic",
Units: "KiB/s",
Fam: "traffic",
Ctx: "tor.traffic",
Type: module.Area,
Priority: prioTraffic,
Dims: module.Dims{
{ID: "traffic/read", Name: "read", Algo: module.Incremental, Div: 1024},
{ID: "traffic/written", Name: "write", Algo: module.Incremental, Mul: -1, Div: 1024},
},
}

var uptimeChart = module.Chart{
ID: "uptime",
Title: "Tor Uptime",
Units: "seconds",
Fam: "uptime",
Ctx: "tor.uptime",
Priority: prioUptime,
Dims: module.Dims{
{ID: "uptime"},
},
}
117 changes: 117 additions & 0 deletions src/go/plugin/go.d/modules/tor/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package tor

import (
"bytes"
"errors"
"fmt"
"strings"

"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/socket"
)

// https://spec.torproject.org/control-spec/index.html
// https://github.com/torproject/stem/blob/master/stem/control.py

const (
cmdAuthenticate = "AUTHENTICATE"
cmdQuit = "QUIT"
cmdGetInfo = "GETINFO"
)

type controlConn interface {
connect() error
disconnect()

getInfo(...string) ([]byte, error)
}

func newControlConn(conf Config) controlConn {
return &torControlClient{
password: conf.Password,
conn: socket.New(socket.Config{
Address: conf.Address,
ConnectTimeout: conf.Timeout.Duration(),
ReadTimeout: conf.Timeout.Duration(),
WriteTimeout: conf.Timeout.Duration(),
})}
}

type torControlClient struct {
password string
conn socket.Client
}

func (c *torControlClient) connect() error {
if err := c.conn.Connect(); err != nil {
return err
}

return c.authenticate()
}

func (c *torControlClient) authenticate() error {
// https://spec.torproject.org/control-spec/commands.html#authenticate

cmd := cmdAuthenticate
if c.password != "" {
cmd = fmt.Sprintf("%s \"%s\"", cmdAuthenticate, c.password)
}

var s string
err := c.conn.Command(cmd+"\n", func(bs []byte) bool {
s = string(bs)
return false
})
if err != nil {
return fmt.Errorf("authentication failed: %v", err)
}
if !strings.HasPrefix(s, "250") {
return fmt.Errorf("authentication failed: %s", s)
}
return nil
}

func (c *torControlClient) disconnect() {
// https://spec.torproject.org/control-spec/commands.html#quit

_ = c.conn.Command(cmdQuit+"\n", func(bs []byte) bool { return false })
_ = c.conn.Disconnect()
}

func (c *torControlClient) getInfo(keywords ...string) ([]byte, error) {
// https://spec.torproject.org/control-spec/commands.html#getinfo

if len(keywords) == 0 {
return nil, errors.New("no keywords specified")
}
cmd := fmt.Sprintf("%s %s", cmdGetInfo, strings.Join(keywords, " "))

var buf bytes.Buffer
var err error

clientErr := c.conn.Command(cmd+"\n", func(bs []byte) bool {
s := string(bs)

switch {
case strings.HasPrefix(s, "250-"):
buf.WriteString(strings.TrimPrefix(s, "250-"))
buf.WriteByte('\n')
return true
case strings.HasPrefix(s, "250 "):
return false
default:
err = errors.New(s)
return false
}
})
if clientErr != nil {
return nil, fmt.Errorf("command '%s' failed: %v", cmd, clientErr)
}
if err != nil {
return nil, fmt.Errorf("command '%s' failed: %v", cmd, err)
}

return buf.Bytes(), nil
}
65 changes: 65 additions & 0 deletions src/go/plugin/go.d/modules/tor/collect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package tor

import (
"bufio"
"bytes"
"fmt"
"strconv"
"strings"
)

func (t *Tor) collect() (map[string]int64, error) {
if t.conn == nil {
conn, err := t.establishConnection()
if err != nil {
return nil, err
}
t.conn = conn
}

mx := make(map[string]int64)
if err := t.collectServerInfo(mx); err != nil {
t.Cleanup()
return nil, err
}

return mx, nil
}

func (t *Tor) collectServerInfo(mx map[string]int64) error {
resp, err := t.conn.getInfo("traffic/read", "traffic/written", "uptime")
if err != nil {
return err
}

sc := bufio.NewScanner(bytes.NewReader(resp))

for sc.Scan() {
line := sc.Text()

key, value, ok := strings.Cut(line, "=")
if !ok {
return fmt.Errorf("failed to parse metric: %s", line)
}

v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return fmt.Errorf("failed to parse metric %s value: %v", line, err)
}
mx[key] = v
}

return nil
}

func (t *Tor) establishConnection() (controlConn, error) {
conn := t.newConn(t.Config)

if err := conn.connect(); err != nil {
return nil, err
}

return conn, nil
}
53 changes: 53 additions & 0 deletions src/go/plugin/go.d/modules/tor/config_schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"jsonSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Tor collector configuration.",
"type": "object",
"properties": {
"update_every": {
"title": "Update every",
"description": "Data collection interval, measured in seconds.",
"type": "integer",
"minimum": 1,
"default": 1
},
"address": {
"title": "Address",
"description": "The IP address and port where the Tor's Control Port listens for connections.",
"type": "string",
"default": "127.0.0.1:9051"
},
"timeout": {
"title": "Timeout",
"description": "Timeout for establishing a connection and communication (reading and writing) in seconds.",
"type": "number",
"minimum": 0.5,
"default": 1
},
"password": {
"title": "Password",
"description": "The password for authentication.",
"type": "string",
"sensitive": true
}
},
"required": [
"address"
],
"additionalProperties": false,
"patternProperties": {
"^name$": {}
}
},
"uiSchema": {
"uiOptions": {
"fullPage": true
},
"timeout": {
"ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
},
"password": {
"ui:widget": "password"
}
}
}
Loading

0 comments on commit bc3a380

Please sign in to comment.