Compare commits
7 Commits
fb624a9147
...
v0.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ebffda2c7 | ||
|
|
cadbbc9080 | ||
|
|
e907fb0b1a | ||
|
|
b0fc0646d2 | ||
|
|
c2eb02afc7 | ||
|
|
262f3daee4 | ||
|
|
fb32886d4a |
13
README.md
13
README.md
@@ -15,14 +15,15 @@ Log-based IPS system written in Go for Linux-based system.
|
||||
|
||||
# Overview
|
||||
BanForge is a simple IPS for replacement fail2ban in Linux system.
|
||||
The project is currently in its early stages of development.
|
||||
All release are available on my self-hosted [Gitea](https://gitea.d3m0k1d.ru/d3m0k1d/BanForge) because Github has limits for Actions.
|
||||
All release are available on my self-hosted [Gitea](https://gitea.d3m0k1d.ru/d3m0k1d/BanForge) after release v1.0.0 are available on Github release page.
|
||||
If you have any questions or suggestions, create issue on [Github](https://github.com/d3m0k1d/BanForge/issues).
|
||||
|
||||
## Roadmap
|
||||
- [x] Real-time Nginx log monitoring
|
||||
- [ ] Add support for other service
|
||||
- [ ] Add support for user service with regular expressions
|
||||
- [x] Rule system
|
||||
- [x] Nginx and Sshd support
|
||||
- [x] Working with ufw/iptables/nftables/firewalld
|
||||
- [ ] Add support for most popular web-service
|
||||
- [ ] User regexp for custom services
|
||||
- [ ] TUI interface
|
||||
|
||||
# Requirements
|
||||
@@ -31,7 +32,7 @@ If you have any questions or suggestions, create issue on [Github](https://githu
|
||||
- ufw/iptables/nftables/firewalld
|
||||
|
||||
# Installation
|
||||
Search for a release on the [Gitea](https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases) releases page and download it. Then create or copy a systemd unit file.
|
||||
Search for a release on the [Gitea](https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases) releases page and download it. Then create or copy(/build dir) a systemd unit(openrc script) file.
|
||||
Or clone the repo and use the Makefile.
|
||||
```
|
||||
git clone https://gitea.d3m0k1d.ru/d3m0k1d/BanForge.git
|
||||
|
||||
@@ -67,7 +67,7 @@ var DaemonCmd = &cobra.Command{
|
||||
j.LoadRules(r)
|
||||
go j.UnbanChecker()
|
||||
go j.Tribunal()
|
||||
go storage.Write(reqDb_w, resultCh)
|
||||
go storage.WriteReq(reqDb_w, resultCh)
|
||||
var scanners []*parser.Scanner
|
||||
|
||||
for _, svc := range cfg.Service {
|
||||
|
||||
@@ -12,12 +12,20 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ip string
|
||||
ttl_fw string
|
||||
)
|
||||
var UnbanCmd = &cobra.Command{
|
||||
Use: "unban",
|
||||
Short: "Unban IP",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
fmt.Println("IP can't be empty")
|
||||
os.Exit(1)
|
||||
}
|
||||
if ttl_fw == "" {
|
||||
ttl_fw = "1y"
|
||||
}
|
||||
ip := args[0]
|
||||
db, err := storage.NewBanWriter()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -60,6 +68,14 @@ var BanCmd = &cobra.Command{
|
||||
Use: "ban",
|
||||
Short: "Ban IP",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
fmt.Println("IP can't be empty")
|
||||
os.Exit(1)
|
||||
}
|
||||
if ttl_fw == "" {
|
||||
ttl_fw = "1y"
|
||||
}
|
||||
ip := args[0]
|
||||
db, err := storage.NewBanWriter()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -89,7 +105,7 @@ var BanCmd = &cobra.Command{
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = db.AddBan(ip, "1y")
|
||||
err = db.AddBan(ip, ttl_fw, "manual ban")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
@@ -99,6 +115,5 @@ var BanCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func FwRegister() {
|
||||
BanCmd.Flags().StringVarP(&ip, "ip", "i", "", "ip to ban")
|
||||
UnbanCmd.Flags().StringVarP(&ip, "ip", "i", "", "ip to unban")
|
||||
BanCmd.Flags().StringVarP(&ttl_fw, "ttl", "t", "", "ban time")
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ banforge unban <ip>
|
||||
**Description**
|
||||
These commands provide an abstraction over your firewall. If you want to simplify the interface to your firewall, you can use these commands.
|
||||
|
||||
Flag -t or -ttl add bantime if not used default ban 1 year
|
||||
### list - Lists the IP addresses that are currently blocked
|
||||
```shell
|
||||
banforge list
|
||||
|
||||
@@ -100,7 +100,7 @@ func (j *Judge) Tribunal() {
|
||||
break
|
||||
}
|
||||
|
||||
err = j.db_w.AddBan(entry.IP, rule.BanTime)
|
||||
err = j.db_w.AddBan(entry.IP, rule.BanTime, rule.Name)
|
||||
if err != nil {
|
||||
j.logger.Error(
|
||||
"Failed to add ban to database",
|
||||
|
||||
@@ -41,7 +41,7 @@ func (d *BanWriter) CreateTable() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *BanWriter) AddBan(ip string, ttl string) error {
|
||||
func (d *BanWriter) AddBan(ip string, ttl string, reason string) error {
|
||||
duration, err := config.ParseDurationWithYears(ttl)
|
||||
if err != nil {
|
||||
d.logger.Error("Invalid duration format", "ttl", ttl, "error", err)
|
||||
@@ -54,7 +54,7 @@ func (d *BanWriter) AddBan(ip string, ttl string) error {
|
||||
_, err = d.db.Exec(
|
||||
"INSERT INTO bans (ip, reason, banned_at, expired_at) VALUES (?, ?, ?, ?)",
|
||||
ip,
|
||||
"1",
|
||||
reason,
|
||||
now.Format(time.RFC3339),
|
||||
expiredAt.Format(time.RFC3339),
|
||||
)
|
||||
@@ -180,8 +180,8 @@ func (d *BanReader) BanList() error {
|
||||
t := table.NewWriter()
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
t.SetStyle(table.StyleBold)
|
||||
t.AppendHeader(table.Row{"№", "IP", "Banned At"})
|
||||
rows, err := d.db.Query("SELECT ip, banned_at FROM bans")
|
||||
t.AppendHeader(table.Row{"№", "IP", "Banned At", "Reason", "Expires At"})
|
||||
rows, err := d.db.Query("SELECT ip, banned_at, reason, expired_at FROM bans")
|
||||
if err != nil {
|
||||
d.logger.Error("Failed to get ban list", "error", err)
|
||||
return err
|
||||
@@ -190,12 +190,14 @@ func (d *BanReader) BanList() error {
|
||||
count++
|
||||
var ip string
|
||||
var bannedAt string
|
||||
err := rows.Scan(&ip, &bannedAt)
|
||||
var reason string
|
||||
var expiredAt string
|
||||
err := rows.Scan(&ip, &bannedAt, &reason, &expiredAt)
|
||||
if err != nil {
|
||||
d.logger.Error("Failed to get ban list", "error", err)
|
||||
return err
|
||||
}
|
||||
t.AppendRow(table.Row{count, ip, bannedAt})
|
||||
t.AppendRow(table.Row{count, ip, bannedAt, reason, expiredAt})
|
||||
|
||||
}
|
||||
t.Render()
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestBanWriter_AddBan(t *testing.T) {
|
||||
ip := "192.168.1.1"
|
||||
ttl := "1h"
|
||||
|
||||
err = writer.AddBan(ip, ttl)
|
||||
err = writer.AddBan(ip, ttl, "test")
|
||||
if err != nil {
|
||||
t.Errorf("AddBan failed: %v", err)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func TestBanWriter_RemoveBan(t *testing.T) {
|
||||
}
|
||||
|
||||
ip := "192.168.1.2"
|
||||
err = writer.AddBan(ip, "1h")
|
||||
err = writer.AddBan(ip, "1h", "test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add ban: %v", err)
|
||||
}
|
||||
@@ -111,13 +111,13 @@ func TestBanWriter_RemoveExpiredBans(t *testing.T) {
|
||||
}
|
||||
|
||||
expiredIP := "192.168.1.3"
|
||||
err = writer.AddBan(expiredIP, "-1h")
|
||||
err = writer.AddBan(expiredIP, "-1h", "tes")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add expired ban: %v", err)
|
||||
}
|
||||
|
||||
activeIP := "192.168.1.4"
|
||||
err = writer.AddBan(activeIP, "1h")
|
||||
err = writer.AddBan(activeIP, "1h", "test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add active ban: %v", err)
|
||||
}
|
||||
@@ -181,7 +181,7 @@ func TestBanReader_IsBanned(t *testing.T) {
|
||||
}
|
||||
|
||||
ip := "192.168.1.5"
|
||||
err = writer.AddBan(ip, "1h")
|
||||
err = writer.AddBan(ip, "1h", "test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add ban: %v", err)
|
||||
}
|
||||
@@ -280,7 +280,7 @@ func TestBanWriter_AddBan_InvalidDuration(t *testing.T) {
|
||||
t.Fatalf("Failed to create table: %v", err)
|
||||
}
|
||||
|
||||
err = writer.AddBan("192.168.1.7", "invalid_duration")
|
||||
err = writer.AddBan("192.168.1.7", "invalid_duration", "test")
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid duration")
|
||||
} else if err.Error() == "" || err.Error() == "<nil>" {
|
||||
@@ -306,7 +306,7 @@ func TestMultipleBans(t *testing.T) {
|
||||
ips := []string{"192.168.1.8", "192.168.1.9", "192.168.1.10"}
|
||||
|
||||
for _, ip := range ips {
|
||||
err := writer.AddBan(ip, "1h")
|
||||
err := writer.AddBan(ip, "1h", "test")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add ban for IP %s: %v", ip, err)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func Write(db *Request_Writer, resultCh <-chan *LogEntry) {
|
||||
func WriteReq(db *Request_Writer, resultCh <-chan *LogEntry) {
|
||||
db.logger.Info("Starting log writer")
|
||||
const batchSize = 100
|
||||
const flushInterval = 1 * time.Second
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestWrite_BatchInsert(t *testing.T) {
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
Write(writer, resultCh)
|
||||
WriteReq(writer, resultCh)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
@@ -115,7 +115,7 @@ func TestWrite_BatchSizeTrigger(t *testing.T) {
|
||||
resultCh := make(chan *LogEntry, 100)
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
Write(writer, resultCh)
|
||||
WriteReq(writer, resultCh)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
@@ -167,7 +167,7 @@ func TestWrite_FlushInterval(t *testing.T) {
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
Write(writer, resultCh)
|
||||
WriteReq(writer, resultCh)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
@@ -216,7 +216,7 @@ func TestWrite_EmptyBatch(t *testing.T) {
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
Write(writer, resultCh)
|
||||
WriteReq(writer, resultCh)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
@@ -250,7 +250,7 @@ func TestWrite_ChannelClosed(t *testing.T) {
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
Write(writer, resultCh)
|
||||
WriteReq(writer, resultCh)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user