1
0
Fork 0
telegraf/plugins/inputs/fail2ban/fail2ban.go

147 lines
3 KiB
Go
Raw Normal View History

//go:generate ../../../tools/readme_config_includer/generator
package fail2ban
import (
_ "embed"
"errors"
"fmt"
"os/exec"
"strconv"
"strings"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
var (
execCommand = exec.Command // execCommand is used to mock commands in tests.
metricsTargets = []struct {
target string
field string
}{
{
target: "Currently failed:",
field: "failed",
},
{
target: "Currently banned:",
field: "banned",
},
}
)
const cmd = "fail2ban-client"
type Fail2ban struct {
UseSudo bool `toml:"use_sudo"`
Socket string `toml:"socket"`
path string
}
func (*Fail2ban) SampleConfig() string {
return sampleConfig
}
func (f *Fail2ban) Init() error {
// Set defaults
if f.path == "" {
path, err := exec.LookPath(cmd)
if err != nil {
return fmt.Errorf("looking up %q failed: %w", cmd, err)
}
f.path = path
}
// Check parameters
if f.path == "" {
return fmt.Errorf("%q not found", cmd)
}
return nil
}
func (f *Fail2ban) Gather(acc telegraf.Accumulator) error {
if len(f.path) == 0 {
return errors.New("fail2ban-client not found: verify that fail2ban is installed and that fail2ban-client is in your PATH")
}
name := f.path
var args []string
if f.UseSudo {
name = "sudo"
args = append(args, f.path)
}
if f.Socket != "" {
args = append(args, "--socket", f.Socket)
}
args = append(args, "status")
cmd := execCommand(name, args...)
out, err := cmd.Output()
if err != nil {
return fmt.Errorf("failed to run command %q: %w - %s", strings.Join(cmd.Args, " "), err, string(out))
}
lines := strings.Split(string(out), "\n")
const targetString = "Jail list:"
var jails []string
for _, line := range lines {
idx := strings.LastIndex(line, targetString)
if idx < 0 {
// not target line, skip.
continue
}
jails = strings.Split(strings.TrimSpace(line[idx+len(targetString):]), ", ")
break
}
for _, jail := range jails {
// Skip over empty jails
if jail == "" {
continue
}
fields := make(map[string]interface{})
cmd := execCommand(name, append(args, jail)...)
out, err := cmd.Output()
if err != nil {
return fmt.Errorf("failed to run command %q: %w - %s", strings.Join(cmd.Args, " "), err, string(out))
}
lines := strings.Split(string(out), "\n")
for _, line := range lines {
key, value := extractCount(line)
if key != "" {
fields[key] = value
}
}
acc.AddFields("fail2ban", fields, map[string]string{"jail": jail})
}
return nil
}
func extractCount(line string) (string, int) {
for _, metricsTarget := range metricsTargets {
idx := strings.LastIndex(line, metricsTarget.target)
if idx < 0 {
continue
}
ban := strings.TrimSpace(line[idx+len(metricsTarget.target):])
banCount, err := strconv.Atoi(ban)
if err != nil {
return "", -1
}
return metricsTarget.field, banCount
}
return "", -1
}
func init() {
inputs.Add("fail2ban", func() telegraf.Input {
return &Fail2ban{}
})
}