Skip to content

Commit ee66620

Browse files
committed
Use ipset (fixe #2, #4, #5)
1 parent f504042 commit ee66620

File tree

7 files changed

+267
-62
lines changed

7 files changed

+267
-62
lines changed

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<img width="330" height="330" src="/etc/banisher.png">
55
</p>
66

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

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

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

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

3333
### Config
3434

@@ -122,7 +122,7 @@ For example if you want those two rules, your config file will be:
122122
IPpos: 0
123123
```
124124

125-
## And what can i do if something goes wrong !!!
125+
## And what can i do if something goes wrong ?
126126

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

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

138138
### Prerequisite
139139

140-
- [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).
140+
- [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).
141141
- To compile without the Docker image, the libsystemd0 library is needed (for debian like: `sudo apt install libsystemd-dev`).
142142
- The Banisher is dynamically linked with the glibc.
143143

banisher.go

+90-30
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ import (
44
"log"
55
"net"
66
"strconv"
7-
"strings"
87
"sync"
98
"time"
109

11-
"github.com/dgraph-io/badger"
12-
1310
"github.com/coreos/go-iptables/iptables"
11+
badger "github.com/dgraph-io/badger/v3"
12+
"github.com/nadoo/ipset"
1413
)
1514

15+
const ipsetName = "banisher"
16+
1617
// Banisher is THE banisher
1718
type Banisher struct {
1819
sync.Mutex
@@ -23,11 +24,17 @@ type Banisher struct {
2324
func NewBanisher(databaseFile string) (b *Banisher, err error) {
2425
b = new(Banisher)
2526

26-
// badger
27-
options := badger.DefaultOptions(databaseFile)
28-
options.SyncWrites = true
27+
// badger options
28+
var options badger.Options
29+
if databaseFile == ":memory:" {
30+
options = badger.DefaultOptions("").WithInMemory(true)
31+
} else {
32+
options = badger.DefaultOptions(databaseFile)
33+
options.SyncWrites = true
34+
}
2935
options.Logger = nil
3036

37+
// open badger database
3138
b.db, err = badger.Open(options)
3239
if err != nil {
3340
return nil, err
@@ -38,6 +45,12 @@ func NewBanisher(databaseFile string) (b *Banisher, err error) {
3845
if err != nil {
3946
return nil, err
4047
}
48+
49+
// ipset binding
50+
if err = ipset.Init(); err != nil {
51+
return nil, err
52+
}
53+
4154
return
4255
}
4356

@@ -80,9 +93,9 @@ func (b *Banisher) Add(ip, ruleName string) {
8093
return
8194
}
8295

83-
// iptables
84-
if err = b.IPT.AppendUnique("filter", "INPUT", "-s", ip, "-j", "DROP"); err != nil {
85-
log.Println("failed to ad iptable rule:", err)
96+
// ipset
97+
if err = b.filterIP(ip); err != nil {
98+
log.Println("failed to add an entry to ipset:", err)
8699
return
87100
}
88101

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

94107
if err != nil {
95108
log.Printf("failed to add %s in db: %s", ip, err)
96-
// remove from iptables
97-
if err = b.IPT.Delete("filter", "INPUT", "-s", ip, "-j", "DROP"); err != nil {
98-
log.Printf("failed to remove %s from iptables: %s", ip, err)
109+
// remove from ipset
110+
if err = b.unFilterIP(ip); err != nil {
111+
log.Printf("failed to remove %s from ipset: %s", ip, err)
99112
}
100113
return
101114
}
@@ -105,11 +118,9 @@ func (b *Banisher) Add(ip, ruleName string) {
105118
// Remove unban an IP
106119
func (b *Banisher) Remove(ip string) {
107120
var err error
108-
if err = b.IPT.Delete("filter", "INPUT", "-s", ip, "-j", "DROP"); err != nil {
109-
if !strings.Contains(err.Error(), "matching rule exist") {
110-
log.Printf("failed to delete iptables rules for %s : %s", ip, err)
111-
return
112-
}
121+
if err = b.unFilterIP(ip); err != nil {
122+
log.Printf("failed to remove %s from ipset: %s", ip, err)
123+
return
113124
}
114125
err = b.db.Update(func(txn *badger.Txn) error {
115126
return txn.Delete([]byte(ip))
@@ -122,21 +133,46 @@ func (b *Banisher) Remove(ip string) {
122133
}
123134

124135
// Restore restore iptables rules from DB
125-
func (b Banisher) Restore() error {
126-
err := b.db.View(func(txn *badger.Txn) error {
127-
opts := badger.DefaultIteratorOptions
128-
opts.PrefetchSize = 20
129-
it := txn.NewIterator(opts)
130-
defer it.Close()
131-
for it.Rewind(); it.Valid(); it.Next() {
132-
ip := it.Item().Key()
133-
if err := b.IPT.AppendUnique("filter", "INPUT", "-s", string(ip), "-j", "DROP"); err != nil {
134-
return err
135-
}
136+
func (b *Banisher) Restore() error {
137+
138+
// create an empty ipset for The Banisher
139+
ipset.Destroy(ipsetName)
140+
if err := ipset.Create(ipsetName); err != nil {
141+
return err
142+
}
143+
if err := ipset.Flush(ipsetName); err != nil {
144+
return err
145+
}
146+
147+
// create the iptable rule if it does not exist
148+
exists, err := b.IPT.Exists("filter", "INPUT", "-m", "set", "--match-set", ipsetName, "src", "-j", "DROP")
149+
if err != nil {
150+
return err
151+
}
152+
if !exists {
153+
if err := b.IPT.Insert("filter", "INPUT", 1, "-m", "set", "--match-set", ipsetName, "src", "-j", "DROP"); err != nil {
154+
return err
136155
}
156+
}
157+
158+
if !b.db.Opts().InMemory {
159+
err := b.db.View(func(txn *badger.Txn) error {
160+
opts := badger.DefaultIteratorOptions
161+
opts.PrefetchSize = 20
162+
it := txn.NewIterator(opts)
163+
defer it.Close()
164+
for it.Rewind(); it.Valid(); it.Next() {
165+
ip := it.Item().Key()
166+
if err := b.filterIP(string(ip)); err != nil {
167+
return err
168+
}
169+
}
170+
return nil
171+
})
172+
return err
173+
} else {
137174
return nil
138-
})
139-
return err
175+
}
140176
}
141177

142178
// GC remove expired banishment
@@ -172,3 +208,27 @@ func (b *Banisher) GC() {
172208
time.Sleep(time.Duration(5) * time.Minute)
173209
}
174210
}
211+
212+
func (b *Banisher) Clear() error {
213+
214+
// remove iptable rule
215+
if err := b.IPT.Delete("filter", "INPUT", "-m", "set", "--match-set", ipsetName, "src", "-j", "DROP"); err != nil {
216+
return err
217+
}
218+
219+
// clear and remove ipset
220+
if err := ipset.Flush(ipsetName); err != nil {
221+
return err
222+
}
223+
ipset.Destroy(ipsetName)
224+
225+
return nil
226+
}
227+
228+
func (b *Banisher) filterIP(ip string) error {
229+
return ipset.Add(ipsetName, ip)
230+
}
231+
232+
func (b *Banisher) unFilterIP(ip string) error {
233+
return ipset.Del(ipsetName, ip)
234+
}

debian/banisher.service

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ After=network.target auditd.service
44

55
[Service]
66
ExecStartPre=/usr/bin/install -m 755 -o root -g root -d /var/lib/banisher
7-
ExecStart=/usr/sbin/banisher -conf=/etc/banisher.yml -db=/var/lib/banisher/db.bdg -systemd
7+
ExecStart=/usr/sbin/banisher -conf=/etc/banisher.yml -db=:memory: -systemd
88
KillMode=process
99
Restart=on-failure
1010
StandardOutput=syslog

debian/control

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ Version: #version#
33
Section: net
44
Priority: optional
55
Architecture: amd64
6-
Depends: libc6 (>= 2.28)
6+
Depends: libc6 (>= 2.28), iptables
7+
Suggests: ipset
78
Essential: no
89
Maintainer: Olivier LARRIGAUDIERE
910
Description: Watches your systemd journal and bans, with no delay, abusers.
11+
Homepage: https://github.com/olarriga/banisher

go.mod

+18-7
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,28 @@ go 1.19
55
require (
66
github.com/coreos/go-iptables v0.6.0
77
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
8-
github.com/dgraph-io/badger v1.6.2
8+
github.com/dgraph-io/badger/v3 v3.2103.5
9+
github.com/marcsauter/single v0.0.0-20201009143647-9f8d81240be2
10+
github.com/nadoo/ipset v0.5.0
911
gopkg.in/yaml.v3 v3.0.1
1012
)
1113

1214
require (
13-
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
1415
github.com/cespare/xxhash v1.1.0 // indirect
15-
github.com/dgraph-io/ristretto v0.0.2 // indirect
16+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
17+
github.com/dgraph-io/ristretto v0.1.1 // indirect
1618
github.com/dustin/go-humanize v1.0.0 // indirect
17-
github.com/golang/protobuf v1.3.1 // indirect
18-
github.com/pkg/errors v0.8.1 // indirect
19-
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
20-
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
19+
github.com/gogo/protobuf v1.3.2 // indirect
20+
github.com/golang/glog v1.0.0 // indirect
21+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
22+
github.com/golang/protobuf v1.5.2 // indirect
23+
github.com/golang/snappy v0.0.4 // indirect
24+
github.com/google/flatbuffers v22.11.23+incompatible // indirect
25+
github.com/klauspost/compress v1.15.13 // indirect
26+
github.com/kr/pretty v0.2.0 // indirect
27+
github.com/pkg/errors v0.9.1 // indirect
28+
go.opencensus.io v0.24.0 // indirect
29+
golang.org/x/net v0.4.0 // indirect
30+
golang.org/x/sys v0.3.0 // indirect
31+
google.golang.org/protobuf v1.28.1 // indirect
2132
)

0 commit comments

Comments
 (0)