mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-11 13:47:05 +02:00
134 lines
3.6 KiB
Go
134 lines
3.6 KiB
Go
package integrations
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
"github.com/swissmakers/fail2ban-ui/internal/config"
|
|
)
|
|
|
|
type mikrotikIntegration struct{}
|
|
|
|
func init() {
|
|
Register(&mikrotikIntegration{})
|
|
}
|
|
|
|
func (m *mikrotikIntegration) ID() string {
|
|
return "mikrotik"
|
|
}
|
|
|
|
func (m *mikrotikIntegration) DisplayName() string {
|
|
return "Mikrotik RouterOS"
|
|
}
|
|
|
|
func (m *mikrotikIntegration) Validate(cfg config.AdvancedActionsConfig) error {
|
|
if cfg.Mikrotik.Host == "" {
|
|
return fmt.Errorf("mikrotik host is required")
|
|
}
|
|
if cfg.Mikrotik.Username == "" {
|
|
return fmt.Errorf("mikrotik username is required")
|
|
}
|
|
if cfg.Mikrotik.Password == "" && cfg.Mikrotik.SSHKeyPath == "" {
|
|
return fmt.Errorf("mikrotik password or SSH key path is required")
|
|
}
|
|
if cfg.Mikrotik.AddressList == "" {
|
|
return fmt.Errorf("mikrotik address list is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *mikrotikIntegration) BlockIP(req Request) error {
|
|
if err := m.Validate(req.Config); err != nil {
|
|
return err
|
|
}
|
|
cmd := fmt.Sprintf(`/ip firewall address-list add list=%s address=%s comment="Fail2ban-UI permanent block"`,
|
|
req.Config.Mikrotik.AddressList, req.IP)
|
|
return m.runCommand(req, cmd)
|
|
}
|
|
|
|
func (m *mikrotikIntegration) UnblockIP(req Request) error {
|
|
if err := m.Validate(req.Config); err != nil {
|
|
return err
|
|
}
|
|
cmd := fmt.Sprintf(`/ip firewall address-list remove [/ip firewall address-list find address=%s list=%s]`,
|
|
req.IP, req.Config.Mikrotik.AddressList)
|
|
return m.runCommand(req, cmd)
|
|
}
|
|
|
|
func (m *mikrotikIntegration) runCommand(req Request, command string) error {
|
|
cfg := req.Config.Mikrotik
|
|
|
|
authMethods := []ssh.AuthMethod{}
|
|
if cfg.Password != "" {
|
|
authMethods = append(authMethods, ssh.Password(cfg.Password))
|
|
}
|
|
if cfg.SSHKeyPath != "" {
|
|
key, err := os.ReadFile(cfg.SSHKeyPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read mikrotik ssh key: %w", err)
|
|
}
|
|
signer, err := ssh.ParsePrivateKey(key)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse mikrotik ssh key: %w", err)
|
|
}
|
|
authMethods = append(authMethods, ssh.PublicKeys(signer))
|
|
}
|
|
|
|
if len(authMethods) == 0 {
|
|
return fmt.Errorf("no authentication method available for mikrotik")
|
|
}
|
|
|
|
port := cfg.Port
|
|
if port == 0 {
|
|
port = 22
|
|
}
|
|
|
|
clientCfg := &ssh.ClientConfig{
|
|
User: cfg.Username,
|
|
Auth: authMethods,
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
Timeout: 10 * time.Second,
|
|
}
|
|
|
|
address := net.JoinHostPort(cfg.Host, fmt.Sprintf("%d", port))
|
|
client, err := ssh.Dial("tcp", address, clientCfg)
|
|
if err != nil {
|
|
// Provide more specific error messages for common connection issues
|
|
if netErr, ok := err.(net.Error); ok {
|
|
if netErr.Timeout() {
|
|
return fmt.Errorf("connection to mikrotik at %s timed out: %w", address, err)
|
|
}
|
|
}
|
|
if opErr, ok := err.(*net.OpError); ok {
|
|
if opErr.Err != nil {
|
|
return fmt.Errorf("failed to connect to mikrotik at %s: %v (check host, port %d, and network connectivity)", address, opErr.Err, port)
|
|
}
|
|
}
|
|
return fmt.Errorf("failed to connect to mikrotik at %s: %w (check host, port %d, username, and credentials)", address, err, port)
|
|
}
|
|
defer client.Close()
|
|
|
|
session, err := client.NewSession()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create mikrotik ssh session: %w", err)
|
|
}
|
|
defer session.Close()
|
|
|
|
if req.Logger != nil {
|
|
req.Logger("Running Mikrotik command: %s", command)
|
|
}
|
|
|
|
output, err := session.CombinedOutput(command)
|
|
if err != nil {
|
|
return fmt.Errorf("mikrotik command failed: %w (output: %s)", err, string(output))
|
|
}
|
|
if req.Logger != nil {
|
|
req.Logger("Mikrotik command output: %s", string(output))
|
|
}
|
|
return nil
|
|
}
|