Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0929b92939 | ||
|
|
b75541af61 | ||
|
|
4e56d7bb6c | ||
|
|
efa9abb289 | ||
|
|
2747abfc04 | ||
|
|
66d460dbfc | ||
|
|
783645c30b |
@@ -28,7 +28,7 @@ builds:
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
archives:
|
||||
- format: tar.gz
|
||||
- formats: [tar.gz]
|
||||
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
||||
|
||||
nfpms:
|
||||
@@ -48,6 +48,15 @@ nfpms:
|
||||
scripts:
|
||||
postinstall: build/postinstall.sh
|
||||
postremove: build/postremove.sh
|
||||
contents:
|
||||
- src: docs/man/banforge.1
|
||||
dst: /usr/share/man/man1/banforge.1
|
||||
file_info:
|
||||
mode: 0644
|
||||
- src: docs/man/banforge.5
|
||||
dst: /usr/share/man/man5/banforge.5
|
||||
file_info:
|
||||
mode: 0644
|
||||
release:
|
||||
gitea:
|
||||
owner: d3m0k1d
|
||||
@@ -60,7 +69,6 @@ changelog:
|
||||
exclude:
|
||||
- "^docs:"
|
||||
- "^test:"
|
||||
|
||||
checksum:
|
||||
name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
|
||||
algorithm: sha256
|
||||
|
||||
19
Makefile
19
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: build build-daemon build-tui clean help
|
||||
.PHONY: build build-daemon build-tui clean help install-man check-man
|
||||
|
||||
help:
|
||||
@echo "BanForge build targets:"
|
||||
@@ -6,7 +6,9 @@ help:
|
||||
@echo " make build-daemon - Build only daemon"
|
||||
@echo " make build-tui - Build only TUI"
|
||||
@echo " make clean - Remove binaries"
|
||||
@echo " make test - Run tests"
|
||||
@echo " make test - Run tests"
|
||||
@echo " make install-man - Install manpages to system"
|
||||
@echo " make check-man - Validate manpage syntax"
|
||||
|
||||
build: build-daemon build-tui
|
||||
@echo "✅ Build complete!"
|
||||
@@ -31,3 +33,16 @@ test-cover:
|
||||
|
||||
lint:
|
||||
golangci-lint run --fix
|
||||
|
||||
check-man:
|
||||
@echo "Checking manpage syntax..."
|
||||
@man -l docs/man/banforge.1 > /dev/null && echo "✅ banforge.1 OK"
|
||||
@man -l docs/man/banforge.5 > /dev/null && echo "✅ banforge.5 OK"
|
||||
|
||||
install-man:
|
||||
@echo "Installing manpages..."
|
||||
install -d $(DESTDIR)/usr/share/man/man1
|
||||
install -d $(DESTDIR)/usr/share/man/man5
|
||||
install -m 644 docs/man/banforge.1 $(DESTDIR)/usr/share/man/man1/banforge.1
|
||||
install -m 644 docs/man/banforge.5 $(DESTDIR)/usr/share/man/man5/banforge.5
|
||||
@echo "✅ Manpages installed!"
|
||||
|
||||
19
README.md
19
README.md
@@ -22,6 +22,7 @@ If you have any questions or suggestions, create issue on [Github](https://githu
|
||||
- [x] Rule system
|
||||
- [x] Nginx and Sshd support
|
||||
- [x] Working with ufw/iptables/nftables/firewalld
|
||||
- [x] Prometheus metrics
|
||||
- [ ] Add support for most popular web-service
|
||||
- [ ] User regexp for custom services
|
||||
- [ ] TUI interface
|
||||
@@ -40,10 +41,10 @@ In release page you can find rpm, deb, apk packages, for amd or arm architecture
|
||||
### 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
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.6.0/banforge_0.6.0_linux_amd64.deb
|
||||
|
||||
# Install
|
||||
sudo dpkg -i banforge_0.4.0_linux_amd64.deb
|
||||
sudo dpkg -i banforge_0.6.0_linux_amd64.deb
|
||||
|
||||
# Verify installation
|
||||
sudo systemctl status banforge
|
||||
@@ -53,13 +54,13 @@ sudo systemctl status banforge
|
||||
```bash
|
||||
|
||||
# Download
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.4.0/banforge_0.4.0_linux_amd64.rpm
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.6.0/banforge_0.6.0_linux_amd64.rpm
|
||||
|
||||
# Install
|
||||
sudo rpm -i banforge_0.4.0_linux_amd64.rpm
|
||||
sudo rpm -i banforge_0.6.0_linux_amd64.rpm
|
||||
|
||||
# Or with dnf (CentOS 8+, AlmaLinux)
|
||||
sudo dnf install banforge_0.4.0_linux_amd64.rpm
|
||||
sudo dnf install banforge_0.6.0_linux_amd64.rpm
|
||||
|
||||
# Verify
|
||||
sudo systemctl status banforge
|
||||
@@ -69,10 +70,10 @@ sudo systemctl status banforge
|
||||
```bash
|
||||
|
||||
# Download
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.4.0/banforge_0.4.0_linux_amd64.apk
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.6.0/banforge_0.6.0_linux_amd64.apk
|
||||
|
||||
# Install
|
||||
sudo apk add --allow-untrusted banforge_0.4.0_linux_amd64.apk
|
||||
sudo apk add --allow-untrusted banforge_0.6.0_linux_amd64.apk
|
||||
|
||||
# Verify
|
||||
sudo rc-service banforge status
|
||||
@@ -82,10 +83,10 @@ sudo rc-service banforge status
|
||||
```bash
|
||||
|
||||
# Download
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.4.0/banforge_0.4.0_linux_amd64.pkg.tar.zst
|
||||
wget https://gitea.d3m0k1d.ru/d3m0k1d/BanForge/releases/download/v0.6.0/banforge_0.6.0_linux_amd64.pkg.tar.zst
|
||||
|
||||
# Install
|
||||
sudo pacman -U banforge_0.4.0_linux_amd64.pkg.tar.zst
|
||||
sudo pacman -U banforge_0.6.0_linux_amd64.pkg.tar.zst
|
||||
|
||||
# Verify
|
||||
sudo systemctl status banforge
|
||||
|
||||
249
docs/man/banforge.1
Normal file
249
docs/man/banforge.1
Normal file
@@ -0,0 +1,249 @@
|
||||
.TH BANFORGE 1 "24 February 2026" "BanForge 1.0"
|
||||
.
|
||||
.SH NAME
|
||||
banforge \- BanForge IPS utility for Linux
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B banforge
|
||||
[\fIOPTIONS\fR] \fICOMMAND\fR [\fIARGUMENTS\fR]
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
BanForge is an Intrusion Prevention System (IPS) utility for Linux.
|
||||
It monitors service logs, detects anomalies and malicious activity,
|
||||
and automatically applies firewall rules to block suspicious IP addresses.
|
||||
.
|
||||
.PP
|
||||
The program consists of two components:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBbanforge\fR \- CLI utility for management
|
||||
.IP \(bu 2
|
||||
\fBbanforge daemon\fR \- background service for real-time monitoring
|
||||
.RE
|
||||
.
|
||||
.SH COMMANDS
|
||||
.
|
||||
.SS init \- Create configuration files
|
||||
.PP
|
||||
\fBbanforge init\fR
|
||||
.PP
|
||||
Creates the necessary directories and base configuration files:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fI/etc/banforge/config.toml\fR \- main configuration
|
||||
.IP \(bu 2
|
||||
\fI/etc/banforge/rules.toml\fR \- default rules file
|
||||
.IP \(bu 2
|
||||
\fI/etc/banforge/rules.d/\fR \- directory for individual rule files
|
||||
.RE
|
||||
.
|
||||
.SS version \- Display BanForge version
|
||||
.PP
|
||||
\fBbanforge version\fR
|
||||
.PP
|
||||
Displays the current version of the BanForge software.
|
||||
.
|
||||
.SS daemon \- Start the BanForge daemon
|
||||
.PP
|
||||
\fBbanforge daemon\fR
|
||||
.PP
|
||||
Starts the BanForge daemon process in the background.
|
||||
The daemon continuously monitors incoming requests, detects anomalies,
|
||||
and applies firewall rules in real-time.
|
||||
.
|
||||
.SS firewall \- Manage firewall rules
|
||||
.PP
|
||||
\fBbanforge ban\fR \fI<ip>\fR [\fIOPTIONS\fR]
|
||||
.br
|
||||
\fBbanforge unban\fR \fI<ip>\fR
|
||||
.PP
|
||||
These commands provide an abstraction over your firewall.
|
||||
.PP
|
||||
\fBoptions:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fB-t\fR, \fB--ttl\fR \- Ban duration (default: 1 year)
|
||||
.RE
|
||||
.PP
|
||||
\fBExamples:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBbanforge ban 192.168.1.100 -t 1h\fR \- Ban IP for 1 hour
|
||||
.IP \(bu 2
|
||||
\fBbanforge unban 192.168.1.100\fR \- Unban IP
|
||||
.RE
|
||||
.
|
||||
.SS ports \- Manage firewall ports
|
||||
.PP
|
||||
\fBbanforge open\fR \fB-port\fR \fI<port>\fR \fB-protocol\fR \fI<protocol>\fR
|
||||
.br
|
||||
\fBbanforge close\fR \fB-port\fR \fI<port>\fR \fB-protocol\fR \fI<protocol>\fR
|
||||
.PP
|
||||
Open or close ports on the firewall.
|
||||
.PP
|
||||
\fBflags:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fB-port\fR \- Port number (e.g., 80) \fI(required)\fR
|
||||
.IP \(bu 2
|
||||
\fB-protocol\fR \- Protocol (tcp/udp) \fI(required)\fR
|
||||
.RE
|
||||
.PP
|
||||
\fBExamples:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBbanforge open -port 80 -protocol tcp\fR
|
||||
.IP \(bu 2
|
||||
\fBbanforge close -port 443 -protocol tcp\fR
|
||||
.RE
|
||||
.
|
||||
.SS list \- List blocked IP addresses
|
||||
.PP
|
||||
\fBbanforge list\fR
|
||||
.PP
|
||||
Outputs a table of IP addresses that are currently blocked.
|
||||
.
|
||||
.SS rule \- Manage detection rules
|
||||
.PP
|
||||
Rules are stored in \fI/etc/banforge/rules.d/\fR as individual \fI.toml\fR files.
|
||||
.
|
||||
.SS "rule add \- Add a new rule"
|
||||
.PP
|
||||
\fBbanforge rule add\fR \fB-n\fR \fI<name>\fR \fB-s\fR \fI<service>\fR [\fIOPTIONS\fR]
|
||||
.PP
|
||||
\fBflags:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fB-n\fR, \fB--name\fR \- Rule name (used as filename) \fI(required)\fR
|
||||
.IP \(bu 2
|
||||
\fB-s\fR, \fB--service\fR \- Service name (nginx, apache, ssh, etc.) \fI(required)\fR
|
||||
.IP \(bu 2
|
||||
\fB-p\fR, \fB--path\fR \- Request path to match
|
||||
.IP \(bu 2
|
||||
\fB-m\fR, \fB--method\fR \- HTTP method (GET, POST, etc.)
|
||||
.IP \(bu 2
|
||||
\fB-c\fR, \fB--status\fR \- HTTP status code (403, 404, etc.)
|
||||
.IP \(bu 2
|
||||
\fB-t\fR, \fB--ttl\fR \- Ban duration (default: 1y)
|
||||
.IP \(bu 2
|
||||
\fB-r\fR, \fB--max_retry\fR \- Max retries before ban (default: 0)
|
||||
.RE
|
||||
.PP
|
||||
\fBNote:\fR At least one of \fB-p\fR, \fB-m\fR, or \fB-c\fR must be specified.
|
||||
.PP
|
||||
\fBExamples:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBbanforge rule add -n "Forbidden" -s nginx -c 403 -t 30m\fR
|
||||
.IP \(bu 2
|
||||
\fBbanforge rule add -n "Admin Access" -s nginx -p "/admin/*" -t 2h -r 3\fR
|
||||
.IP \(bu 2
|
||||
\fBbanforge rule add -n "SSH Bruteforce" -s ssh -c "Failed" -t 1h -r 5\fR
|
||||
.RE
|
||||
.
|
||||
.SS "rule list \- List all rules"
|
||||
.PP
|
||||
\fBbanforge rule list\fR
|
||||
.PP
|
||||
Displays all configured rules in a table format.
|
||||
.
|
||||
.SS "rule edit \- Edit an existing rule"
|
||||
.PP
|
||||
\fBbanforge rule edit\fR \fB-n\fR \fI<name>\fR [\fIOPTIONS\fR]
|
||||
.PP
|
||||
Edit fields of an existing rule. Only specified fields will be updated.
|
||||
.PP
|
||||
\fBflags:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fB-n\fR, \fB--name\fR \- Rule name to edit \fI(required)\fR
|
||||
.IP \(bu 2
|
||||
\fB-s\fR, \fB--service\fR \- New service name
|
||||
.IP \(bu 2
|
||||
\fB-p\fR, \fB--path\fR \- New path
|
||||
.IP \(bu 2
|
||||
\fB-m\fR, \fB--method\fR \- New method
|
||||
.IP \(bu 2
|
||||
\fB-c\fR, \fB--status\fR \- New status code
|
||||
.RE
|
||||
.PP
|
||||
\fBExamples:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBbanforge rule edit -n "SSH Bruteforce" -t 2h\fR
|
||||
.IP \(bu 2
|
||||
\fBbanforge rule edit -n "Forbidden" -c 403\fR
|
||||
.RE
|
||||
.
|
||||
.SS "rule remove \- Remove a rule"
|
||||
.PP
|
||||
\fBbanforge rule remove\fR \fI<name>\fR
|
||||
.PP
|
||||
Permanently delete a rule by name.
|
||||
.PP
|
||||
\fBExample:\fR \fBbanforge rule remove "Old Rule"\fR
|
||||
.
|
||||
.SH "BAN TIME FORMAT"
|
||||
.PP
|
||||
Use the following suffixes for ban duration:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBs\fR \- Seconds
|
||||
.IP \(bu 2
|
||||
\fBm\fR \- Minutes
|
||||
.IP \(bu 2
|
||||
\fBh\fR \- Hours
|
||||
.IP \(bu 2
|
||||
\fBd\fR \- Days
|
||||
.IP \(bu 2
|
||||
\fBM\fR \- Months (30 days)
|
||||
.IP \(bu 2
|
||||
\fBy\fR \- Years (365 days)
|
||||
.RE
|
||||
.PP
|
||||
\fBExamples:\fR 30s, 5m, 2h, 1d, 1M, 1y
|
||||
.
|
||||
.SH "CONFIGURATION FILES"
|
||||
.PP
|
||||
Configuration files are stored in \fI/etc/banforge/\fR:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fIconfig.toml\fR \- main daemon configuration
|
||||
.IP \(bu 2
|
||||
\fIrules.toml\fR \- default rules
|
||||
.IP \(bu 2
|
||||
\fIrules.d/*.toml\fR \- individual rule files
|
||||
.RE
|
||||
.
|
||||
.SH "EXIT STATUS"
|
||||
.PP
|
||||
\fB0\fR \- Success
|
||||
.br
|
||||
\fB1\fR \- General error
|
||||
.br
|
||||
\fB2\fR \- Configuration error
|
||||
.
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
Initialize configuration: \fBbanforge init\fR
|
||||
.IP \(bu 2
|
||||
Start daemon: \fBbanforge daemon\fR
|
||||
.IP \(bu 2
|
||||
Ban an IP: \fBbanforge ban 192.168.1.100 -t 1h\fR
|
||||
.IP \(bu 2
|
||||
Add a rule: \fBbanforge rule add -n "404" -s nginx -c 404 -t 30m\fR
|
||||
.IP \(bu 2
|
||||
List blocked IPs: \fBbanforge list\fR
|
||||
.RE
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
.BR iptables (8),
|
||||
.BR nftables (8),
|
||||
.BR fail2ban (1),
|
||||
.BR nginx (8)
|
||||
.
|
||||
.SH AUTHOR
|
||||
.PP
|
||||
Ilya "d3m0k1d" Chernishev contact@d3m0k1d.ru
|
||||
339
docs/man/banforge.5
Normal file
339
docs/man/banforge.5
Normal file
@@ -0,0 +1,339 @@
|
||||
.TH BANFORGE 5 "24 February 2026" "BanForge 1.0"
|
||||
.
|
||||
.SH NAME
|
||||
banforge \- BanForge configuration file format
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
BanForge uses TOML configuration files stored in \fI/etc/banforge/\fR:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fIconfig.toml\fR \- main daemon configuration
|
||||
.IP \(bu 2
|
||||
\fIrules.toml\fR \- default rules (legacy)
|
||||
.IP \(bu 2
|
||||
\fIrules.d/*.toml\fR \- individual rule files (recommended)
|
||||
.RE
|
||||
.
|
||||
.SH "CONFIG.TOML FORMAT"
|
||||
.PP
|
||||
Main configuration file for BanForge daemon.
|
||||
.PP
|
||||
\fBStructure:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fB[firewall]\fR \- firewall parameters
|
||||
.IP \(bu 2
|
||||
\fB[[service]]\fR \- service monitoring configuration (multiple allowed)
|
||||
.IP \(bu 2
|
||||
\fB[metrics]\fR \- Prometheus metrics configuration (optional)
|
||||
.RE
|
||||
.
|
||||
.SS "Firewall Section"
|
||||
.PP
|
||||
\fB[firewall]\fR
|
||||
.PP
|
||||
Defines firewall parameters. The \fBbanforge init\fR command automatically
|
||||
detects your installed firewall (nftables, iptables, ufw, firewalld).
|
||||
.PP
|
||||
\fBFields:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBname\fR \- Firewall type (nftables, iptables, ufw, firewalld)
|
||||
.IP \(bu 2
|
||||
\fBconfig\fR \- Path to firewall configuration file
|
||||
.RE
|
||||
.PP
|
||||
\fBExample:\fR
|
||||
.RS
|
||||
.nf
|
||||
[firewall]
|
||||
name = "nftables"
|
||||
config = "/etc/nftables.conf"
|
||||
.fi
|
||||
.RE
|
||||
.
|
||||
.SS "Service Section"
|
||||
.PP
|
||||
\fB[[service]]\fR
|
||||
.PP
|
||||
Configures service monitoring. Multiple service blocks are allowed.
|
||||
.PP
|
||||
\fBFields:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBname\fR \- Service name (nginx, apache, ssh, etc.) \fI(required)\fR
|
||||
.IP \(bu 2
|
||||
\fBlogging\fR \- Log source type: "file" or "journald" \fI(required)\fR
|
||||
.IP \(bu 2
|
||||
\fBlog_path\fR \- Path to log file or journal unit name \fI(required)\fR
|
||||
.IP \(bu 2
|
||||
\fBenabled\fR \- Enable/disable service monitoring (true/false)
|
||||
.RE
|
||||
.PP
|
||||
\fBExamples:\fR
|
||||
.RS
|
||||
.nf
|
||||
# File-based logging
|
||||
[[service]]
|
||||
name = "nginx"
|
||||
logging = "file"
|
||||
log_path = "/var/log/nginx/access.log"
|
||||
enabled = true
|
||||
|
||||
# Journald logging
|
||||
[[service]]
|
||||
name = "nginx"
|
||||
logging = "journald"
|
||||
log_path = "nginx"
|
||||
enabled = false
|
||||
.fi
|
||||
.RE
|
||||
.PP
|
||||
\fBNote:\fR When using journald logging, specify the service name in \fBlog_path\fR.
|
||||
.
|
||||
.SS "Metrics Section"
|
||||
.PP
|
||||
\fB[metrics]\fR
|
||||
.PP
|
||||
Configures Prometheus metrics endpoint (optional).
|
||||
.PP
|
||||
\fBFields:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBenabled\fR \- Enable/disable metrics (true/false)
|
||||
.IP \(bu 2
|
||||
\fBport\fR \- Port for metrics endpoint
|
||||
.RE
|
||||
.PP
|
||||
\fBExample:\fR
|
||||
.RS
|
||||
.nf
|
||||
[metrics]
|
||||
enabled = true
|
||||
port = 9090
|
||||
.fi
|
||||
.RE
|
||||
.
|
||||
.SH "RULES.TOML FORMAT"
|
||||
.PP
|
||||
Detection rules define conditions for blocking IP addresses.
|
||||
Rules are stored in \fI/etc/banforge/rules.d/\fR as individual \fI.toml\fR files.
|
||||
.
|
||||
.SS "Rule Section"
|
||||
.PP
|
||||
\fB[[rule]]\fR
|
||||
.PP
|
||||
Defines a single detection rule.
|
||||
.PP
|
||||
\fBFields:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBname\fR \- Rule name (used as filename) \fI(required)\fR
|
||||
.IP \(bu 2
|
||||
\fBservice\fR \- Service name (nginx, apache, ssh, etc.) \fI(required)\fR
|
||||
.IP \(bu 2
|
||||
\fBpath\fR \- Request path to match (e.g., "/admin/*", "*.php")
|
||||
.IP \(bu 2
|
||||
\fBstatus\fR \- HTTP status code (403, 404, 304, etc.)
|
||||
.IP \(bu 2
|
||||
\fBmethod\fR \- HTTP method (GET, POST, etc.)
|
||||
.IP \(bu 2
|
||||
\fBmax_retry\fR \- Max retries before ban (0 = ban on first request)
|
||||
.IP \(bu 2
|
||||
\fBban_time\fR \- Ban duration (e.g., "1m", "1h", "1d", "1M", "1y")
|
||||
.RE
|
||||
.PP
|
||||
\fBNote:\fR At least one of \fBpath\fR, \fBstatus\fR, or \fBmethod\fR must be specified.
|
||||
.PP
|
||||
\fBExamples:\fR
|
||||
.RS
|
||||
.nf
|
||||
# Ban on HTTP 304 status
|
||||
[[rule]]
|
||||
name = "304 http"
|
||||
service = "nginx"
|
||||
path = ""
|
||||
status = "304"
|
||||
max_retry = 3
|
||||
method = ""
|
||||
ban_time = "1m"
|
||||
|
||||
# Ban on path pattern (admin panel)
|
||||
[[rule]]
|
||||
name = "Admin Access"
|
||||
service = "nginx"
|
||||
path = "/admin/*"
|
||||
status = ""
|
||||
method = ""
|
||||
max_retry = 2
|
||||
ban_time = "2h"
|
||||
|
||||
# SSH brute force protection
|
||||
[[rule]]
|
||||
name = "SSH Bruteforce"
|
||||
service = "ssh"
|
||||
path = ""
|
||||
status = "Failed"
|
||||
method = ""
|
||||
max_retry = 5
|
||||
ban_time = "1h"
|
||||
.fi
|
||||
.RE
|
||||
.
|
||||
.SH "BAN TIME FORMAT"
|
||||
.PP
|
||||
Use the following suffixes for ban duration:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBs\fR \- Seconds
|
||||
.IP \(bu 2
|
||||
\fBm\fR \- Minutes
|
||||
.IP \(bu 2
|
||||
\fBh\fR \- Hours
|
||||
.IP \(bu 2
|
||||
\fBd\fR \- Days
|
||||
.IP \(bu 2
|
||||
\fBM\fR \- Months (30 days)
|
||||
.IP \(bu 2
|
||||
\fBy\fR \- Years (365 days)
|
||||
.RE
|
||||
.
|
||||
.SH "ACTIONS(NOT WORKING ON THIS VERSION)"
|
||||
.PP
|
||||
Rules can trigger custom actions when an IP is banned.
|
||||
.
|
||||
.SS "Script Action"
|
||||
.PP
|
||||
Execute a custom script when an IP is banned.
|
||||
.PP
|
||||
\fBFields:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBtype\fR \- "script"
|
||||
.IP \(bu 2
|
||||
\fBenabled\fR \- Enable/disable action (true/false)
|
||||
.IP \(bu 2
|
||||
\fBinterpretator\fR \- Script interpretator (e.g., "/bin/bash")
|
||||
.IP \(bu 2
|
||||
\fBscript\fR \- Path to script file
|
||||
.RE
|
||||
.PP
|
||||
\fBExample:\fR
|
||||
.RS
|
||||
.nf
|
||||
[[rule]]
|
||||
name = "Notify on Ban"
|
||||
service = "nginx"
|
||||
status = "403"
|
||||
ban_time = "1h"
|
||||
|
||||
[[rule.action]]
|
||||
type = "script"
|
||||
enabled = true
|
||||
interpretator = "/bin/bash"
|
||||
script = "/opt/banforge/scripts/notify.sh"
|
||||
.fi
|
||||
.RE
|
||||
.
|
||||
.SS "Webhook Action"
|
||||
.PP
|
||||
Send HTTP webhook when an IP is banned.
|
||||
.PP
|
||||
\fBFields:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBtype\fR \- "webhook"
|
||||
.IP \(bu 2
|
||||
\fBenabled\fR \- Enable/disable action (true/false)
|
||||
.IP \(bu 2
|
||||
\fBurl\fR \- Webhook URL
|
||||
.IP \(bu 2
|
||||
\fBmethod\fR \- HTTP method (POST, GET)
|
||||
.IP \(bu 2
|
||||
\fBheaders\fR \- Custom headers (key-value pairs)
|
||||
.IP \(bu 2
|
||||
\fBbody\fR \- Request body (supports templates)
|
||||
.RE
|
||||
.PP
|
||||
\fBExample:\fR
|
||||
.RS
|
||||
.nf
|
||||
[[rule.action]]
|
||||
type = "webhook"
|
||||
enabled = true
|
||||
url = "https://hooks.example.com/ban"
|
||||
method = "POST"
|
||||
|
||||
[rule.action.headers]
|
||||
Content-Type = "application/json"
|
||||
Authorization = "Bearer TOKEN"
|
||||
|
||||
[rule.action.body]
|
||||
ip = "{{.IP}}"
|
||||
reason = "{{.Rule}}"
|
||||
.fi
|
||||
.RE
|
||||
.
|
||||
.SS "Email Action"
|
||||
.PP
|
||||
Send email notification when an IP is banned.
|
||||
.PP
|
||||
\fBFields:\fR
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBtype\fR \- "email"
|
||||
.IP \(bu 2
|
||||
\fBenabled\fR \- Enable/disable action (true/false)
|
||||
.IP \(bu 2
|
||||
\fBemail\fR \- Recipient email address
|
||||
.IP \(bu 2
|
||||
\fBemail_sender\fR \- Sender email address
|
||||
.IP \(bu 2
|
||||
\fBemail_subject\fR \- Email subject
|
||||
.IP \(bu 2
|
||||
\fBsmtp_host\fR \- SMTP server host
|
||||
.IP \(bu 2
|
||||
\fBsmtp_port\fR \- SMTP server port
|
||||
.IP \(bu 2
|
||||
\fBsmtp_user\fR \- SMTP username
|
||||
.IP \(bu 2
|
||||
\fBsmtp_password\fR \- SMTP password
|
||||
.IP \(bu 2
|
||||
\fBsmtp_tls\fR \- Enable TLS (true/false)
|
||||
.RE
|
||||
.PP
|
||||
\fBExample:\fR
|
||||
.RS
|
||||
.nf
|
||||
[[rule.action]]
|
||||
type = "email"
|
||||
enabled = true
|
||||
email = "admin@example.com"
|
||||
email_sender = "banforge@example.com"
|
||||
email_subject = "IP Banned"
|
||||
smtp_host = "smtp.example.com"
|
||||
smtp_port = 587
|
||||
smtp_user = "banforge"
|
||||
smtp_password = "secret"
|
||||
smtp_tls = true
|
||||
.fi
|
||||
.RE
|
||||
.
|
||||
.SH FILES
|
||||
.PP
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fI/etc/banforge/config.toml\fR \- main configuration
|
||||
.IP \(bu 2
|
||||
\fI/etc/banforge/rules.d/\fR \- rule files directory
|
||||
.RE
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
.BR banforge (1),
|
||||
.BR iptables (8),
|
||||
.BR nftables (8),
|
||||
.BR systemd (1)
|
||||
.
|
||||
.SH AUTHOR
|
||||
.PP
|
||||
Ilya "d3m0k1d" Chernishev contact@d3m0k1d.ru
|
||||
9
internal/actions/email.go
Normal file
9
internal/actions/email.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"github.com/d3m0k1d/BanForge/internal/config"
|
||||
)
|
||||
|
||||
func SendEmail(action config.Action) error {
|
||||
return nil
|
||||
}
|
||||
19
internal/actions/interface.go
Normal file
19
internal/actions/interface.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package actions
|
||||
|
||||
import "github.com/d3m0k1d/BanForge/internal/config"
|
||||
|
||||
type Executor struct {
|
||||
Action config.Action
|
||||
}
|
||||
|
||||
func (e *Executor) Execute() error {
|
||||
switch e.Action.Type {
|
||||
case "email":
|
||||
return SendEmail(e.Action)
|
||||
case "webhook":
|
||||
return SendWebhook(e.Action)
|
||||
case "script":
|
||||
return RunScript(e.Action)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
32
internal/actions/scripts.go
Normal file
32
internal/actions/scripts.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"github.com/d3m0k1d/BanForge/internal/config"
|
||||
)
|
||||
|
||||
func RunScript(action config.Action) error {
|
||||
if !action.Enabled {
|
||||
return nil
|
||||
}
|
||||
if action.Script == "" {
|
||||
return fmt.Errorf("script on config is empty")
|
||||
}
|
||||
if action.Interpretator == "" {
|
||||
// #nosec G204 - managed by system adminstartor
|
||||
cmd := exec.Command(action.Script)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("run script: %w", err)
|
||||
}
|
||||
}
|
||||
// #nosec G204 - managed by system adminstartor
|
||||
cmd := exec.Command(action.Interpretator, action.Script)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("run script: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
66
internal/actions/webhooks.go
Normal file
66
internal/actions/webhooks.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/d3m0k1d/BanForge/internal/config"
|
||||
)
|
||||
|
||||
var defaultClient = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 10,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
func SendWebhook(action config.Action) error {
|
||||
if !action.Enabled {
|
||||
return nil
|
||||
}
|
||||
if action.URL == "" {
|
||||
return fmt.Errorf("URL on config is empty")
|
||||
}
|
||||
|
||||
method := action.Method
|
||||
if method == "" {
|
||||
method = "POST"
|
||||
}
|
||||
|
||||
var bodyReader io.Reader
|
||||
if action.Body != "" {
|
||||
bodyReader = strings.NewReader(action.Body)
|
||||
if action.Headers["Content-Type"] == "" && action.Headers["content-type"] == "" {
|
||||
action.Headers["Content-Type"] = "application/json"
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, action.URL, bodyReader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create request: %w", err)
|
||||
}
|
||||
|
||||
for key, value := range action.Headers {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
|
||||
// #nosec G704 - HTTP request validation by system administrators
|
||||
resp, err := defaultClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("execute request: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("webhook returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -31,9 +31,30 @@ type Rule struct {
|
||||
Method string `toml:"method"`
|
||||
MaxRetry int `toml:"max_retry"`
|
||||
BanTime string `toml:"ban_time"`
|
||||
Action []Action
|
||||
}
|
||||
|
||||
type Metrics struct {
|
||||
Enabled bool `toml:"enabled"`
|
||||
Port int `toml:"port"`
|
||||
}
|
||||
|
||||
// Actions
|
||||
type Action struct {
|
||||
Type string `toml:"type"`
|
||||
Enabled bool `toml:"enabled"`
|
||||
URL string `toml:"url"`
|
||||
Method string `toml:"method"`
|
||||
Headers map[string]string `toml:"headers"`
|
||||
Body string `toml:"body"`
|
||||
Email string `toml:"email"`
|
||||
EmailSender string `toml:"email_sender"`
|
||||
EmailSubject string `toml:"email_subject"`
|
||||
SMTPHost string `toml:"smtp_host"`
|
||||
SMTPPort int `toml:"smtp_port"`
|
||||
SMTPUser string `toml:"smtp_user"`
|
||||
SMTPPassword string `toml:"smtp_password"`
|
||||
SMTPTLS bool `toml:"smtp_tls"`
|
||||
Interpretator string `toml:"interpretator"`
|
||||
Script string `toml:"script"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user