You've already forked prometheus-fail2ban-exporter
							
							feat: collect new up metric from fail2ban socket
Add support for connecting the exporter directly to the fail2ban server's socket to send requests and receive data. The path to the socket file is optional and specified on startup. Export a new metric based on the response of the `ping` command sent to the fail2ban server. The metric is set to 1 if the server responds with `pong` and 0 in any other case. This metric is only shown if the path to the socket file was provided on startup.
This commit is contained in:
		
							
								
								
									
										8
									
								
								src/socket/decoder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/socket/decoder.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								src/socket/fail2banSocket.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/socket/fail2banSocket.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
package socket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
 | 
			
		||||
	"github.com/kisielk/og-rek"
 | 
			
		||||
	"github.com/nlpodyssey/gopickle/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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.NewEncoder(c),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 t, ok := response.(*types.Tuple); ok {
 | 
			
		||||
		if (*t)[1] == "pong" {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		log.Printf("unexpected response data: %s", t)
 | 
			
		||||
	}
 | 
			
		||||
	log.Printf("unexpected response format - cannot parse: %v", response)
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								src/socket/protocol.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/socket/protocol.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
package socket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/nlpodyssey/gopickle/pickle"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	commandTerminator    = "<F2B_END_COMMAND>"
 | 
			
		||||
	pingCommand          = "ping"
 | 
			
		||||
	socketReadBufferSize = 10000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s *Fail2BanSocket) sendCommand(command []string) (interface{}, error) {
 | 
			
		||||
	err := s.write(command)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return s.read()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Fail2BanSocket) write(command []string) error {
 | 
			
		||||
	err := s.encoder.Encode(command)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_, err = s.socket.Write([]byte(commandTerminator))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Fail2BanSocket) read() (interface{}, error) {
 | 
			
		||||
	buf := make([]byte, socketReadBufferSize)
 | 
			
		||||
	_, err := s.socket.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()
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user