Skip to content

Commit

Permalink
Merge pull request #6 from olarriga/use-ipset
Browse files Browse the repository at this point in the history
Use ipset
  • Loading branch information
olarriga committed Dec 28, 2022
2 parents 5680eec + ee66620 commit 756dce4
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 65 deletions.
13 changes: 5 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@
# IntelliJ IDEA
.idea/*

# Program binary
dist/banisher
# Program binaries and database
dist/*
!dist/config.yml

# Database
dist/db.bdg

# Packages
dist/packages
dist/*.deb
# Vendor folder
vendor/
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<img width="330" height="330" src="/etc/banisher.png">
</p>

The Banisher watches in real time your systemd journal and bans, via iptables, hosts who match on yours rules.
The Banisher watches in real time your systemd journal and bans, via ipset and iptables, hosts who match on yours rules.

Currently hosts (IP) are banished for 1 hour (configurable in config.yml).

Expand All @@ -28,7 +28,7 @@ __WARNING The Banisher works only with logs handled by systemd journal and is cu

1. Download the lastest debian package from the [releases section](https://github.com/olarriga/banisher/releases).
2. Modify the /etc/banisher.yml file to define the configuration according to your needs
3. Restart The Banisher (`systemctl restart banisher.service`).
3. Restart The Banisher (`systemctl restart banisher`).

### Config

Expand Down Expand Up @@ -122,7 +122,7 @@ For example if you want those two rules, your config file will be:
IPpos: 0
```

## And what can i do if something goes wrong !!!
## And what can i do if something goes wrong ?

An iptables rules will be automaticaly removed after defaultBanishmentDuration (defined in your config file).

Expand All @@ -137,7 +137,7 @@ If you made a mistake, just:

### Prerequisite

- [Task](https://taskfile.dev/) is used for compilation with a Docker image to handle glibc version issue to keep The Banisher compatible with debian buster (debian 10).
- [Task](https://taskfile.dev/) is used for compilation with a Docker image to handle glibc version issue to keep The Banisher compatible with debian buster and bullseye (debian 10 and 11).
- To compile without the Docker image, the libsystemd0 library is needed (for debian like: `sudo apt install libsystemd-dev`).
- The Banisher is dynamically linked with the glibc.

Expand Down
120 changes: 90 additions & 30 deletions banisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import (
"log"
"net"
"strconv"
"strings"
"sync"
"time"

"github.com/dgraph-io/badger"

"github.com/coreos/go-iptables/iptables"
badger "github.com/dgraph-io/badger/v3"
"github.com/nadoo/ipset"
)

const ipsetName = "banisher"

// Banisher is THE banisher
type Banisher struct {
sync.Mutex
Expand All @@ -23,11 +24,17 @@ type Banisher struct {
func NewBanisher(databaseFile string) (b *Banisher, err error) {
b = new(Banisher)

// badger
options := badger.DefaultOptions(databaseFile)
options.SyncWrites = true
// badger options
var options badger.Options
if databaseFile == ":memory:" {
options = badger.DefaultOptions("").WithInMemory(true)
} else {
options = badger.DefaultOptions(databaseFile)
options.SyncWrites = true
}
options.Logger = nil

// open badger database
b.db, err = badger.Open(options)
if err != nil {
return nil, err
Expand All @@ -38,6 +45,12 @@ func NewBanisher(databaseFile string) (b *Banisher, err error) {
if err != nil {
return nil, err
}

// ipset binding
if err = ipset.Init(); err != nil {
return nil, err
}

return
}

Expand Down Expand Up @@ -80,9 +93,9 @@ func (b *Banisher) Add(ip, ruleName string) {
return
}

// iptables
if err = b.IPT.AppendUnique("filter", "INPUT", "-s", ip, "-j", "DROP"); err != nil {
log.Println("failed to ad iptable rule:", err)
// ipset
if err = b.filterIP(ip); err != nil {
log.Println("failed to add an entry to ipset:", err)
return
}

Expand All @@ -93,9 +106,9 @@ func (b *Banisher) Add(ip, ruleName string) {

if err != nil {
log.Printf("failed to add %s in db: %s", ip, err)
// remove from iptables
if err = b.IPT.Delete("filter", "INPUT", "-s", ip, "-j", "DROP"); err != nil {
log.Printf("failed to remove %s from iptables: %s", ip, err)
// remove from ipset
if err = b.unFilterIP(ip); err != nil {
log.Printf("failed to remove %s from ipset: %s", ip, err)
}
return
}
Expand All @@ -105,11 +118,9 @@ func (b *Banisher) Add(ip, ruleName string) {
// Remove unban an IP
func (b *Banisher) Remove(ip string) {
var err error
if err = b.IPT.Delete("filter", "INPUT", "-s", ip, "-j", "DROP"); err != nil {
if !strings.Contains(err.Error(), "matching rule exist") {
log.Printf("failed to delete iptables rules for %s : %s", ip, err)
return
}
if err = b.unFilterIP(ip); err != nil {
log.Printf("failed to remove %s from ipset: %s", ip, err)
return
}
err = b.db.Update(func(txn *badger.Txn) error {
return txn.Delete([]byte(ip))
Expand All @@ -122,21 +133,46 @@ func (b *Banisher) Remove(ip string) {
}

// Restore restore iptables rules from DB
func (b Banisher) Restore() error {
err := b.db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.PrefetchSize = 20
it := txn.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
ip := it.Item().Key()
if err := b.IPT.AppendUnique("filter", "INPUT", "-s", string(ip), "-j", "DROP"); err != nil {
return err
}
func (b *Banisher) Restore() error {

// create an empty ipset for The Banisher
ipset.Destroy(ipsetName)
if err := ipset.Create(ipsetName); err != nil {
return err
}
if err := ipset.Flush(ipsetName); err != nil {
return err
}

// create the iptable rule if it does not exist
exists, err := b.IPT.Exists("filter", "INPUT", "-m", "set", "--match-set", ipsetName, "src", "-j", "DROP")
if err != nil {
return err
}
if !exists {
if err := b.IPT.Insert("filter", "INPUT", 1, "-m", "set", "--match-set", ipsetName, "src", "-j", "DROP"); err != nil {
return err
}
}

if !b.db.Opts().InMemory {
err := b.db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.PrefetchSize = 20
it := txn.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
ip := it.Item().Key()
if err := b.filterIP(string(ip)); err != nil {
return err
}
}
return nil
})
return err
} else {
return nil
})
return err
}
}

// GC remove expired banishment
Expand Down Expand Up @@ -172,3 +208,27 @@ func (b *Banisher) GC() {
time.Sleep(time.Duration(5) * time.Minute)
}
}

func (b *Banisher) Clear() error {

// remove iptable rule
if err := b.IPT.Delete("filter", "INPUT", "-m", "set", "--match-set", ipsetName, "src", "-j", "DROP"); err != nil {
return err
}

// clear and remove ipset
if err := ipset.Flush(ipsetName); err != nil {
return err
}
ipset.Destroy(ipsetName)

return nil
}

func (b *Banisher) filterIP(ip string) error {
return ipset.Add(ipsetName, ip)
}

func (b *Banisher) unFilterIP(ip string) error {
return ipset.Del(ipsetName, ip)
}
2 changes: 1 addition & 1 deletion debian/banisher.service
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ After=network.target auditd.service

[Service]
ExecStartPre=/usr/bin/install -m 755 -o root -g root -d /var/lib/banisher
ExecStart=/usr/sbin/banisher -conf=/etc/banisher.yml -db=/var/lib/banisher/db.bdg -systemd
ExecStart=/usr/sbin/banisher -conf=/etc/banisher.yml -db=:memory: -systemd
KillMode=process
Restart=on-failure
StandardOutput=syslog
Expand Down
4 changes: 3 additions & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ Version: #version#
Section: net
Priority: optional
Architecture: amd64
Depends: libc6 (>= 2.28)
Depends: libc6 (>= 2.28), iptables
Suggests: ipset
Essential: no
Maintainer: Olivier LARRIGAUDIERE
Description: Watches your systemd journal and bans, with no delay, abusers.
Homepage: https://github.com/olarriga/banisher
25 changes: 18 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@ go 1.19
require (
github.com/coreos/go-iptables v0.6.0
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/dgraph-io/badger v1.6.2
github.com/dgraph-io/badger/v3 v3.2103.5
github.com/marcsauter/single v0.0.0-20201009143647-9f8d81240be2
github.com/nadoo/ipset v0.5.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/dgraph-io/ristretto v0.0.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/golang/protobuf v1.3.1 // indirect
github.com/pkg/errors v0.8.1 // indirect
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/flatbuffers v22.11.23+incompatible // indirect
github.com/klauspost/compress v1.15.13 // indirect
github.com/kr/pretty v0.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)
Loading

0 comments on commit 756dce4

Please sign in to comment.