Compare commits
5 Commits
66d460dbfc
...
v0.6.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0929b92939 | ||
|
|
b75541af61 | ||
|
|
4e56d7bb6c | ||
|
|
efa9abb289 | ||
|
|
2747abfc04 |
@@ -28,7 +28,7 @@ builds:
|
|||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
archives:
|
archives:
|
||||||
- format: tar.gz
|
- formats: [tar.gz]
|
||||||
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
||||||
|
|
||||||
nfpms:
|
nfpms:
|
||||||
@@ -48,6 +48,15 @@ nfpms:
|
|||||||
scripts:
|
scripts:
|
||||||
postinstall: build/postinstall.sh
|
postinstall: build/postinstall.sh
|
||||||
postremove: build/postremove.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:
|
release:
|
||||||
gitea:
|
gitea:
|
||||||
owner: d3m0k1d
|
owner: d3m0k1d
|
||||||
@@ -60,7 +69,6 @@ changelog:
|
|||||||
exclude:
|
exclude:
|
||||||
- "^docs:"
|
- "^docs:"
|
||||||
- "^test:"
|
- "^test:"
|
||||||
|
|
||||||
checksum:
|
checksum:
|
||||||
name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
|
name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
|
||||||
algorithm: sha256
|
algorithm: sha256
|
||||||
|
|||||||
17
Makefile
17
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:
|
help:
|
||||||
@echo "BanForge build targets:"
|
@echo "BanForge build targets:"
|
||||||
@@ -7,6 +7,8 @@ help:
|
|||||||
@echo " make build-tui - Build only TUI"
|
@echo " make build-tui - Build only TUI"
|
||||||
@echo " make clean - Remove binaries"
|
@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
|
build: build-daemon build-tui
|
||||||
@echo "✅ Build complete!"
|
@echo "✅ Build complete!"
|
||||||
@@ -31,3 +33,16 @@ test-cover:
|
|||||||
|
|
||||||
lint:
|
lint:
|
||||||
golangci-lint run --fix
|
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!"
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
@@ -1,7 +1,19 @@
|
|||||||
package actions
|
package actions
|
||||||
|
|
||||||
type Action struct {
|
import "github.com/d3m0k1d/BanForge/internal/config"
|
||||||
Name string
|
|
||||||
Type string
|
type Executor struct {
|
||||||
Args []string
|
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
|
||||||
|
}
|
||||||
@@ -1,24 +1,66 @@
|
|||||||
package actions
|
package actions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/d3m0k1d/BanForge/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SendWebhook(url string, data []byte) (int, error) {
|
var defaultClient = &http.Client{
|
||||||
client := &http.Client{
|
Timeout: 10 * time.Second,
|
||||||
Timeout: 30 * time.Second,
|
Transport: &http.Transport{
|
||||||
}
|
MaxIdleConns: 100,
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewReader(data))
|
MaxIdleConnsPerHost: 10,
|
||||||
if err != nil {
|
IdleConnTimeout: 90 * time.Second,
|
||||||
return 0, err
|
},
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
// #nosec G704 validating by admin
|
func SendWebhook(action config.Action) error {
|
||||||
resp, err := client.Do(req)
|
if !action.Enabled {
|
||||||
if err != nil {
|
return nil
|
||||||
return 0, err
|
}
|
||||||
}
|
if action.URL == "" {
|
||||||
return resp.StatusCode, nil
|
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"`
|
Method string `toml:"method"`
|
||||||
MaxRetry int `toml:"max_retry"`
|
MaxRetry int `toml:"max_retry"`
|
||||||
BanTime string `toml:"ban_time"`
|
BanTime string `toml:"ban_time"`
|
||||||
|
Action []Action
|
||||||
}
|
}
|
||||||
|
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
Enabled bool `toml:"enabled"`
|
Enabled bool `toml:"enabled"`
|
||||||
Port int `toml:"port"`
|
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