Feat: fail2ban whitelist (#546)
* feat: support whitelist in failtoban plugin * test: add e2e test for failtoban ignore ip * refactor: use netipx for easy contains check
This commit is contained in:
parent
486bd74534
commit
9ff3550786
4 changed files with 294 additions and 1 deletions
|
|
@ -4,9 +4,12 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"go4.org/netipx"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
|
|
@ -40,14 +43,21 @@ func main() {
|
|||
EnvVars: []string{"SSHPIPERD_FAILTOBAN_LOG_ONLY"},
|
||||
Value: false,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "ignore-ip",
|
||||
Usage: "ignore ip, will not ban host matches from these ip addresses",
|
||||
EnvVars: []string{"SSHPIPERD_FAILTOBAN_IGNORE_IP"},
|
||||
Value: cli.NewStringSlice(),
|
||||
},
|
||||
},
|
||||
CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) {
|
||||
|
||||
maxFailures := c.Int("max-failures")
|
||||
banDuration := c.Duration("ban-duration")
|
||||
logOnly := c.Bool("log-only")
|
||||
|
||||
ignoreIP := c.StringSlice("ignore-ip")
|
||||
cache := gocache.New(banDuration, banDuration/2*3)
|
||||
whitelist := buildIPSet(ignoreIP)
|
||||
|
||||
// register signal handler
|
||||
go func() {
|
||||
|
|
@ -74,6 +84,12 @@ func main() {
|
|||
}
|
||||
|
||||
ip, _, _ := net.SplitHostPort(conn.RemoteAddr())
|
||||
ip0, _ := netip.ParseAddr(ip)
|
||||
|
||||
if whitelist.Contains(ip0) {
|
||||
log.Debugf("failtoban: %v in whitelist, ignored.", ip0)
|
||||
return nil
|
||||
}
|
||||
|
||||
failed, found := cache.Get(ip)
|
||||
if !found {
|
||||
|
|
@ -89,11 +105,25 @@ func main() {
|
|||
},
|
||||
UpstreamAuthFailureCallback: func(conn libplugin.ConnMetadata, method string, err error, allowmethods []string) {
|
||||
ip, _, _ := net.SplitHostPort(conn.RemoteAddr())
|
||||
ip0, _ := netip.ParseAddr(ip)
|
||||
|
||||
if whitelist.Contains(ip0) {
|
||||
log.Debugf("failtoban: %v in whitelist, ignored.", ip0)
|
||||
return
|
||||
}
|
||||
|
||||
failed, _ := cache.IncrementInt(ip, 1)
|
||||
log.Warnf("failtoban: %v auth failed. current status: fail %v times, max allowed %v", ip, failed, maxFailures)
|
||||
},
|
||||
PipeCreateErrorCallback: func(remoteAddr string, err error) {
|
||||
ip, _, _ := net.SplitHostPort(remoteAddr)
|
||||
ip0, _ := netip.ParseAddr(ip)
|
||||
|
||||
if whitelist.Contains(ip0) {
|
||||
log.Debugf("failtoban: %v in whitelist, ignored.", ip0)
|
||||
return
|
||||
}
|
||||
|
||||
failed, _ := cache.IncrementInt(ip, 1)
|
||||
log.Warnf("failtoban: %v pipe create failed, reason %v. current status: fail %v times, max allowed %v", ip, err, failed, maxFailures)
|
||||
},
|
||||
|
|
@ -101,3 +131,31 @@ func main() {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func buildIPSet(cidrs []string) *netipx.IPSet {
|
||||
|
||||
var ipsetBuilder netipx.IPSetBuilder
|
||||
|
||||
for _, cidr := range cidrs {
|
||||
if strings.Contains(cidr, "/") {
|
||||
prefix, err := netip.ParsePrefix(cidr)
|
||||
if err != nil {
|
||||
log.Debugf("failtoban: error while parsing ignore IP: \n%v", err)
|
||||
continue
|
||||
}
|
||||
ipsetBuilder.AddPrefix(prefix)
|
||||
} else {
|
||||
ip, err := netip.ParseAddr(cidr)
|
||||
if err != nil {
|
||||
log.Debugf("failtoban: error while parsing ignore IP: \n%v", err)
|
||||
continue
|
||||
}
|
||||
ipsetBuilder.Add(ip)
|
||||
}
|
||||
}
|
||||
ipset, err := ipsetBuilder.IPSet()
|
||||
if err != nil {
|
||||
log.Debugf("failtoban: error while getting IPSet: \n%v", err)
|
||||
}
|
||||
return ipset
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue