Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8b9b227a9 | ||
|
|
08d3214f22 | ||
|
|
6ebda76738 | ||
|
|
b9754f605b | ||
|
|
be6b19426b |
78
README.md
78
README.md
@@ -32,15 +32,79 @@ 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(/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
|
||||
cd BanForge
|
||||
sudo make build-daemon
|
||||
cd bin
|
||||
Search for a release on the [Gitea](https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases) releases page and download it.
|
||||
In release page you can find rpm, deb, apk packages, for amd or arm architecture.
|
||||
|
||||
## Installation guide for packages
|
||||
|
||||
### Debian/Ubuntu(.deb)
|
||||
```bash
|
||||
# Download the latest DEB package
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.4.0/banforge_0.4.0_linux_amd64.deb
|
||||
|
||||
# Install
|
||||
sudo dpkg -i banforge_0.4.0_linux_amd64.deb
|
||||
|
||||
# Verify installation
|
||||
sudo systemctl status banforge
|
||||
```
|
||||
|
||||
### RHEL-based(.rpm)
|
||||
```bash
|
||||
|
||||
# Download
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.4.0/banforge_0.4.0_linux_amd64.rpm
|
||||
|
||||
# Install
|
||||
sudo rpm -i banforge_0.4.0_linux_amd64.rpm
|
||||
|
||||
# Or with dnf (CentOS 8+, AlmaLinux)
|
||||
sudo dnf install banforge_0.4.0_linux_amd64.rpm
|
||||
|
||||
# Verify
|
||||
sudo systemctl status banforge
|
||||
```
|
||||
|
||||
### Alpine(.apk)
|
||||
```bash
|
||||
|
||||
# Download
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.4.0/banforge_0.4.0_linux_amd64.apk
|
||||
|
||||
# Install
|
||||
sudo apk add --allow-untrusted banforge_0.4.0_linux_amd64.apk
|
||||
|
||||
# Verify
|
||||
sudo rc-service banforge status
|
||||
```
|
||||
|
||||
### Arch Linux(.pkg.tar.zst)
|
||||
```bash
|
||||
|
||||
# Download
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.4.0/banforge_0.4.0_linux_amd64.pkg.tar.zst
|
||||
|
||||
# Install
|
||||
sudo pacman -U banforge_0.4.0_linux_amd64.pkg.tar.zst
|
||||
|
||||
# Verify
|
||||
sudo systemctl status banforge
|
||||
```
|
||||
This is examples for other versions with different architecture or new versions check release page on [Gitea](https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases).
|
||||
|
||||
## Installation guide for source code
|
||||
```bash
|
||||
# Download
|
||||
git clone https://github.com/d3m0k1d/BanForge.git
|
||||
cd BanForge
|
||||
make build-daemon
|
||||
cd bin
|
||||
mv banforge /usr/bin/banforge
|
||||
cd ..
|
||||
# Add init script and uses banforge init
|
||||
cd build
|
||||
./postinstall.sh
|
||||
```
|
||||
# Usage
|
||||
For first steps use this commands
|
||||
```bash
|
||||
|
||||
@@ -112,6 +112,11 @@ var DaemonCmd = &cobra.Command{
|
||||
ssh := parser.NewSshdParser()
|
||||
ssh.Parse(p.Events(), entryCh)
|
||||
}
|
||||
if svc.Name == "apache" {
|
||||
log.Info("Starting apache parser", "service", serviceName)
|
||||
ap := parser.NewApacheParser()
|
||||
ap.Parse(p.Events(), entryCh)
|
||||
}
|
||||
}(pars, svc.Name)
|
||||
continue
|
||||
}
|
||||
@@ -131,14 +136,18 @@ var DaemonCmd = &cobra.Command{
|
||||
if svc.Name == "nginx" {
|
||||
log.Info("Starting nginx parser", "service", serviceName)
|
||||
ng := parser.NewNginxParser()
|
||||
ng.Parse(p.Events(), resultCh)
|
||||
ng.Parse(p.Events(), entryCh)
|
||||
|
||||
}
|
||||
if svc.Name == "ssh" {
|
||||
log.Info("Starting ssh parser", "service", serviceName)
|
||||
ssh := parser.NewSshdParser()
|
||||
ssh.Parse(p.Events(), resultCh)
|
||||
|
||||
ssh.Parse(p.Events(), entryCh)
|
||||
}
|
||||
if svc.Name == "apache" {
|
||||
log.Info("Starting apache parser", "service", serviceName)
|
||||
ap := parser.NewApacheParser()
|
||||
ap.Parse(p.Events(), entryCh)
|
||||
}
|
||||
|
||||
}(pars, svc.Name)
|
||||
|
||||
@@ -21,14 +21,14 @@ func (f *Firewalld) Ban(ip string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command("sudo", "firewall-cmd", "--zone=drop", "--add-source", ip, "--permanent")
|
||||
cmd := exec.Command("firewall-cmd", "--zone=drop", "--add-source", ip, "--permanent")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
f.logger.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
f.logger.Info("Add source " + ip + " " + string(output))
|
||||
output, err = exec.Command("sudo", "firewall-cmd", "--reload").CombinedOutput()
|
||||
output, err = exec.Command("firewall-cmd", "--reload").CombinedOutput()
|
||||
if err != nil {
|
||||
f.logger.Error(err.Error())
|
||||
return err
|
||||
@@ -42,14 +42,14 @@ func (f *Firewalld) Unban(ip string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command("sudo", "firewall-cmd", "--zone=drop", "--remove-source", ip, "--permanent")
|
||||
cmd := exec.Command("firewall-cmd", "--zone=drop", "--remove-source", ip, "--permanent")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
f.logger.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
f.logger.Info("Remove source " + ip + " " + string(output))
|
||||
output, err = exec.Command("sudo", "firewall-cmd", "--reload").CombinedOutput()
|
||||
output, err = exec.Command("firewall-cmd", "--reload").CombinedOutput()
|
||||
if err != nil {
|
||||
f.logger.Error(err.Error())
|
||||
return err
|
||||
|
||||
@@ -27,7 +27,7 @@ func (f *Iptables) Ban(ip string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command("sudo", "iptables", "-A", "INPUT", "-s", ip, "-j", "DROP")
|
||||
cmd := exec.Command("iptables", "-A", "INPUT", "-s", ip, "-j", "DROP")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
f.logger.Error("failed to ban IP",
|
||||
@@ -45,7 +45,7 @@ func (f *Iptables) Ban(ip string) error {
|
||||
return err
|
||||
}
|
||||
// #nosec G204 - f.config is validated above via validateConfigPath()
|
||||
cmd = exec.Command("sudo", "iptables-save", "-f", f.config)
|
||||
cmd = exec.Command("iptables-save", "-f", f.config)
|
||||
output, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
f.logger.Error("failed to save config",
|
||||
@@ -69,7 +69,7 @@ func (f *Iptables) Unban(ip string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command("sudo", "iptables", "-D", "INPUT", "-s", ip, "-j", "DROP")
|
||||
cmd := exec.Command("iptables", "-D", "INPUT", "-s", ip, "-j", "DROP")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
f.logger.Error("failed to unban IP",
|
||||
@@ -87,7 +87,7 @@ func (f *Iptables) Unban(ip string) error {
|
||||
return err
|
||||
}
|
||||
// #nosec G204 - f.config is validated above via validateConfigPath()
|
||||
cmd = exec.Command("sudo", "iptables-save", "-f", f.config)
|
||||
cmd = exec.Command("iptables-save", "-f", f.config)
|
||||
output, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
f.logger.Error("failed to save config",
|
||||
|
||||
@@ -26,7 +26,7 @@ func (n *Nftables) Ban(ip string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("sudo", "nft", "add", "rule", "inet", "banforge", "banned",
|
||||
cmd := exec.Command("nft", "add", "rule", "inet", "banforge", "banned",
|
||||
"ip", "saddr", ip, "drop")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -70,7 +70,7 @@ func (n *Nftables) Unban(ip string) error {
|
||||
return fmt.Errorf("no rule found for IP %s", ip)
|
||||
}
|
||||
// #nosec G204 - handle is extracted from nftables output and validated
|
||||
cmd := exec.Command("sudo", "nft", "delete", "rule", "inet", "banforge", "banned",
|
||||
cmd := exec.Command("nft", "delete", "rule", "inet", "banforge", "banned",
|
||||
"handle", handle)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -112,7 +112,7 @@ func (n *Nftables) Setup(config string) error {
|
||||
}
|
||||
}
|
||||
`
|
||||
cmd := exec.Command("sudo", "tee", config)
|
||||
cmd := exec.Command("tee", config)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create stdin pipe: %w", err)
|
||||
@@ -135,7 +135,7 @@ func (n *Nftables) Setup(config string) error {
|
||||
return fmt.Errorf("failed to save config: %w", err)
|
||||
}
|
||||
|
||||
cmd = exec.Command("sudo", "nft", "-f", config)
|
||||
cmd = exec.Command("nft", "-f", config)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load nftables config: %s", string(output))
|
||||
@@ -145,7 +145,7 @@ func (n *Nftables) Setup(config string) error {
|
||||
}
|
||||
|
||||
func (n *Nftables) findRuleHandle(ip string) (string, error) {
|
||||
cmd := exec.Command("sudo", "nft", "-a", "list", "chain", "inet", "banforge", "banned")
|
||||
cmd := exec.Command("nft", "-a", "list", "chain", "inet", "banforge", "banned")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to list chain rules: %w", err)
|
||||
@@ -172,13 +172,13 @@ func saveNftablesConfig(configPath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("sudo", "nft", "list", "ruleset")
|
||||
cmd := exec.Command("nft", "list", "ruleset")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nftables ruleset: %w", err)
|
||||
}
|
||||
|
||||
cmd = exec.Command("sudo", "tee", configPath)
|
||||
cmd = exec.Command("tee", configPath)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create stdin pipe: %w", err)
|
||||
|
||||
@@ -23,7 +23,7 @@ func (u *Ufw) Ban(ip string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("sudo", "ufw", "--force", "deny", "from", ip)
|
||||
cmd := exec.Command("ufw", "--force", "deny", "from", ip)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
u.logger.Error("failed to ban IP",
|
||||
@@ -42,7 +42,7 @@ func (u *Ufw) Unban(ip string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("sudo", "ufw", "--force", "delete", "deny", "from", ip)
|
||||
cmd := exec.Command("ufw", "--force", "delete", "deny", "from", ip)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
u.logger.Error("failed to unban IP",
|
||||
@@ -59,7 +59,7 @@ func (u *Ufw) Unban(ip string) error {
|
||||
func (u *Ufw) Setup(config string) error {
|
||||
if config != "" {
|
||||
fmt.Printf("Ufw dont support config file\n")
|
||||
cmd := exec.Command("sudo", "ufw", "enable")
|
||||
cmd := exec.Command("ufw", "enable")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
u.logger.Error("failed to enable ufw",
|
||||
@@ -69,7 +69,7 @@ func (u *Ufw) Setup(config string) error {
|
||||
}
|
||||
}
|
||||
if config == "" {
|
||||
cmd := exec.Command("sudo", "ufw", "enable")
|
||||
cmd := exec.Command("ufw", "enable")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
u.logger.Error("failed to enable ufw",
|
||||
|
||||
61
internal/parser/ApacheParser.go
Normal file
61
internal/parser/ApacheParser.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/d3m0k1d/BanForge/internal/logger"
|
||||
"github.com/d3m0k1d/BanForge/internal/storage"
|
||||
)
|
||||
|
||||
type ApacheParser struct {
|
||||
pattern *regexp.Regexp
|
||||
logger *logger.Logger
|
||||
}
|
||||
|
||||
func NewApacheParser() *ApacheParser {
|
||||
pattern := regexp.MustCompile(
|
||||
`^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+-\s+-\s+\[(.*?)\]\s+"(\w+)\s+(.*?)\s+HTTP/[\d.]+"\s+(\d+)\s+(\d+|-)\s+"(.*?)"\s+"(.*?)"`,
|
||||
)
|
||||
// Groups:
|
||||
// 1: IP
|
||||
// 2: Timestamp
|
||||
// 3: Method (GET, POST, etc.)
|
||||
// 4: Path
|
||||
// 5: Status Code (200, 404, 403...)
|
||||
// 6: Response Size
|
||||
// 7: Referer
|
||||
// 8: User-Agent
|
||||
|
||||
return &ApacheParser{
|
||||
pattern: pattern,
|
||||
logger: logger.New(false),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ApacheParser) Parse(eventCh <-chan Event, resultCh chan<- *storage.LogEntry) {
|
||||
// Group 1: IP, Group 2: Timestamp, Group 3: Method, Group 4: Path, Group 5: Status
|
||||
for event := range eventCh {
|
||||
matches := p.pattern.FindStringSubmatch(event.Data)
|
||||
if matches == nil {
|
||||
continue
|
||||
}
|
||||
path := matches[4]
|
||||
status := matches[5]
|
||||
method := matches[3]
|
||||
|
||||
resultCh <- &storage.LogEntry{
|
||||
Service: "apache",
|
||||
IP: matches[1],
|
||||
Path: path,
|
||||
Status: status,
|
||||
Method: method,
|
||||
}
|
||||
p.logger.Info(
|
||||
"Parsed apache log entry",
|
||||
"ip", matches[1],
|
||||
"path", path,
|
||||
"status", status,
|
||||
"method", method,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user