support for sending commands over socket

Add support for sending a ping command to the fail2ban server over the
socket file. This includes encoding the command data using og-rek and
decoding the response using `gopickle`.
This commit is contained in:
Hector 2021-08-29 10:44:12 +01:00
parent 073ec89cb3
commit 86f8fd2c07
5 changed files with 107 additions and 0 deletions

View File

@ -3,6 +3,8 @@ module fail2ban-prometheus-exporter
go 1.15
require (
github.com/kisielk/og-rek v1.1.0
github.com/mattn/go-sqlite3 v1.14.6
github.com/nlpodyssey/gopickle v0.1.0
github.com/prometheus/client_golang v1.9.0
)

View File

@ -137,6 +137,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kisielk/og-rek v1.1.0 h1:u10TvQbPtrlY/6H4+BiFsBywwSVTGFsx0YOVtpx3IbI=
github.com/kisielk/og-rek v1.1.0/go.mod h1:6ihsOSzSAxR/65S3Bn9zNihoEqRquhDQZ2c6I2+MG3c=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -175,6 +177,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nlpodyssey/gopickle v0.1.0 h1:9wjwRqXsOSYWZl4c4ko472b6RW+VB1I441ZcfFg1r5g=
github.com/nlpodyssey/gopickle v0.1.0/go.mod h1:YIUwjJ2O7+vnBsxUN+MHAAI3N+adqEGiw+nDpwW95bY=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=

8
src/socket/decoder.go Normal file
View File

@ -0,0 +1,8 @@
package socket
// Py_builtins_str is used by the pickle decoder to parse the server response into a format Go can understand
type Py_builtins_str struct{}
func (c Py_builtins_str) Call(args ...interface{}) (interface{}, error) {
return args[0], nil
}

View File

@ -0,0 +1,38 @@
package socket
import (
"github.com/kisielk/og-rek"
"log"
"net"
)
type Fail2BanSocket struct {
socket net.Conn
encoder *ogórek.Encoder
}
func MustConnectToSocket(path string) *Fail2BanSocket {
c, err := net.Dial("unix", path)
if err != nil {
log.Fatalf("failed to open fail2ban socket: %v", err)
}
return &Fail2BanSocket{
socket: c,
encoder: ogórek.NewEncoderWithConfig(c, &ogórek.EncoderConfig{
Protocol: 5,
}),
}
}
func (s *Fail2BanSocket) Ping() bool {
response, err := s.sendCommand([]string{pingCommand, "100"})
if err != nil {
log.Printf("server ping failed: %v", err)
return false
}
if v, ok := response.([]string); ok && v[1] == "pong" {
return true
}
log.Printf("unexpected response from server: %v", response)
return false
}

55
src/socket/protocol.go Normal file
View File

@ -0,0 +1,55 @@
package socket
import (
"bytes"
"fmt"
"github.com/kisielk/og-rek"
"github.com/nlpodyssey/gopickle/pickle"
"net"
)
const (
commandTerminator = "<F2B_END_COMMAND>"
pingCommand = "ping"
socketReadBufferSize = 10000
)
func (s *Fail2BanSocket) sendCommand(command []string) (interface{}, error) {
err := write(s.encoder, command)
if err != nil {
return nil, err
}
return read(&s.socket)
}
func write(encoder *ogórek.Encoder, command []string) error {
err := encoder.Encode(command)
if err != nil {
return err
}
err = encoder.Encode(commandTerminator)
if err != nil {
return err
}
return nil
}
func read(s *net.Conn) (interface{}, error) {
buf := make([]byte, socketReadBufferSize)
_, err := (*s).Read(buf)
if err != nil {
return nil, err
}
bufReader := bytes.NewReader(buf)
unpickler := pickle.NewUnpickler(bufReader)
unpickler.FindClass = func(module, name string) (interface{}, error) {
if module == "builtins" && name == "str" {
return &Py_builtins_str{}, nil
}
return nil, fmt.Errorf("class not found: " + module + " : " + name)
}
return unpickler.Load()
}