├── config.json ├── init-scripts ├── systemd ├── debian.sh └── ubuntu_14_04 ├── go.mod ├── error.go ├── fail2rest.1 ├── whois.go ├── CONTRIBUTING.md ├── go.sum ├── fail2rest.go ├── global.go ├── README.md └── jail.go /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Addr": "127.0.0.1:5000", 3 | "Fail2banSocket": "/var/run/fail2ban/fail2ban.sock" 4 | } 5 | -------------------------------------------------------------------------------- /init-scripts/systemd: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=fail2ban REST server 3 | Documentation=man:fail2rest(1) 4 | ConditionPathExists=/etc/fail2rest.json 5 | After=fail2ban.service 6 | 7 | [Service] 8 | ExecStart=/usr/bin/fail2rest -config=/etc/fail2rest.json 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sean-der/fail2rest 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/gorilla/mux v1.8.1 7 | github.com/sean-der/fail2go v0.0.1 8 | github.com/sean-der/goWHOIS v0.0.0-20140709054325-70c302b526cf 9 | ) 10 | 11 | require ( 12 | github.com/kisielk/og-rek v1.2.0 // indirect 13 | github.com/mattn/go-sqlite3 v1.14.22 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | type ErrorBody struct { 10 | Error string 11 | } 12 | 13 | func writeHTTPError(res http.ResponseWriter, err error) { 14 | res.WriteHeader(400) 15 | encodedOutput, err := json.Marshal(ErrorBody{Error: err.Error()}) 16 | if err != nil { 17 | fmt.Println("Failed to generate HTTP error: " + err.Error()) 18 | } else { 19 | res.Write(encodedOutput) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /fail2rest.1: -------------------------------------------------------------------------------- 1 | .TH fail2rest 1 "" "" "General Commands Manual" 2 | .SH NAME 3 | fail2rest \- REST server for fail2ban 4 | .SH SYNOPSIS 5 | fail2rest [options] 6 | .SH DESCRIPTION 7 | fail2rest is a small REST server that aims to allow full administration of a fail2ban(1) server via HTTP 8 | .SH OPTIONS 9 | \-config 10 | specify path to json configuration for fail2rest. 11 | .TP 12 | \-h 13 | prints available arguments, then exits. 14 | .SH SEE ALSO 15 | fail2ban(1) 16 | .SH BUGS 17 | No known bugs. 18 | .SH AUTHOR 19 | Sean DuBois (sean@siobud.com) 20 | -------------------------------------------------------------------------------- /whois.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/gorilla/mux" 8 | "github.com/sean-der/fail2go" 9 | "github.com/sean-der/goWHOIS" 10 | ) 11 | 12 | func whoisHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 13 | goWHOISReq := goWHOIS.NewReq(mux.Vars(req)["object"]) 14 | WHOIS, err := goWHOISReq.Raw() 15 | if err != nil { 16 | writeHTTPError(res, err) 17 | return 18 | } 19 | 20 | encodedOutput, _ := json.Marshal(map[string]string{"WHOIS": WHOIS}) 21 | res.Write(encodedOutput) 22 | } 23 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | #Running 2 | To quickly run fail2rest just execute `go run *.go` 3 | 4 | #Guidelines 5 | 6 | * Put as much logic as possible into [fail2go](https://github.com/sean-der/fail2go) 7 | * Make sure code is properly formated [gofmt](http://blog.golang.org/go-fmt-your-code) 8 | * Make sure you code compiles 9 | * If adding new REST endpoints try to follow the current style 10 | 11 | #REST Style 12 | Currently we have three top level endpoints 13 | * /global (Get/Set information relating to fail2ban) 14 | * /jail/{jail} (Get/Set information relating to a single jail) 15 | * /wwhois/{ip} (Run a WHOIS on the given IP) 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= 2 | github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= 3 | github.com/kisielk/og-rek v1.2.0 h1:CTvDIin+YnetsSQAYbe+QNAxXU3B50C5hseEz8xEoJw= 4 | github.com/kisielk/og-rek v1.2.0/go.mod h1:6ihsOSzSAxR/65S3Bn9zNihoEqRquhDQZ2c6I2+MG3c= 5 | github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= 6 | github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 7 | github.com/sean-der/fail2go v0.0.1 h1:AOZ5b/ewYzStnhsPXO/bLWzg97eogEV4gXkiwEJKLxU= 8 | github.com/sean-der/fail2go v0.0.1/go.mod h1:xfDepiRfuCxHX5LZyBl4nAIUvL+8Aed6o8fcxvCMPD4= 9 | github.com/sean-der/goWHOIS v0.0.0-20140709054325-70c302b526cf h1:EmuSitACP3cGjR5/KrEUiyDXPCDI6MNoPzrt4weR0Fw= 10 | github.com/sean-der/goWHOIS v0.0.0-20140709054325-70c302b526cf/go.mod h1:VrJMtetl51Wyuc1JaF5UhewOiLRYBN1PozQtYJp8vCY= 11 | -------------------------------------------------------------------------------- /fail2rest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/gorilla/mux" 11 | "github.com/sean-der/fail2go" 12 | ) 13 | 14 | type Configuration struct { 15 | Addr string 16 | Fail2banSocket string 17 | } 18 | 19 | var fail2goConn *fail2go.Conn 20 | 21 | func main() { 22 | configPath := flag.String("config", "config.json", "path to config.json") 23 | flag.Parse() 24 | 25 | file, fileErr := os.Open(*configPath) 26 | 27 | if fileErr != nil { 28 | fmt.Println("failed to open config:", fileErr) 29 | os.Exit(1) 30 | } 31 | 32 | configuration := new(Configuration) 33 | configErr := json.NewDecoder(file).Decode(configuration) 34 | 35 | if configErr != nil { 36 | fmt.Println("config error:", configErr) 37 | os.Exit(1) 38 | } 39 | 40 | fail2goConn := fail2go.Newfail2goConn(configuration.Fail2banSocket) 41 | r := mux.NewRouter() 42 | 43 | globalHandler(r.PathPrefix("/global").Subrouter(), fail2goConn) 44 | jailHandler(r.PathPrefix("/jail").Subrouter(), fail2goConn) 45 | r.HandleFunc("/whois/{object}", func(res http.ResponseWriter, req *http.Request) { 46 | whoisHandler(res, req, fail2goConn) 47 | }).Methods("GET") 48 | 49 | http.Handle("/", r) 50 | fmt.Println(http.ListenAndServe(configuration.Addr, nil)) 51 | } 52 | -------------------------------------------------------------------------------- /global.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/gorilla/mux" 8 | "github.com/sean-der/fail2go" 9 | ) 10 | 11 | func globalStatusHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 12 | globalStatus, err := fail2goConn.GlobalStatus() 13 | if err != nil { 14 | writeHTTPError(res, err) 15 | return 16 | } 17 | 18 | encodedOutput, _ := json.Marshal(globalStatus) 19 | res.Write(encodedOutput) 20 | } 21 | 22 | func globalPingHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 23 | globalPing, err := fail2goConn.GlobalPing() 24 | if err != nil { 25 | writeHTTPError(res, err) 26 | return 27 | } 28 | 29 | encodedOutput, _ := json.Marshal(globalPing) 30 | res.Write(encodedOutput) 31 | } 32 | 33 | func globalBansHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 34 | globalBans, err := fail2goConn.GlobalBans() 35 | if err != nil { 36 | writeHTTPError(res, err) 37 | return 38 | } 39 | 40 | encodedOutput, _ := json.Marshal(globalBans) 41 | res.Write(encodedOutput) 42 | } 43 | 44 | func globalHandler(globalRouter *mux.Router, fail2goConn *fail2go.Conn) { 45 | globalRouter.HandleFunc("/status", func(res http.ResponseWriter, req *http.Request) { 46 | globalStatusHandler(res, req, fail2goConn) 47 | }).Methods("GET") 48 | globalRouter.HandleFunc("/ping", func(res http.ResponseWriter, req *http.Request) { 49 | globalPingHandler(res, req, fail2goConn) 50 | }).Methods("GET") 51 | globalRouter.HandleFunc("/bans", func(res http.ResponseWriter, req *http.Request) { 52 | globalBansHandler(res, req, fail2goConn) 53 | }).Methods("GET") 54 | 55 | } 56 | -------------------------------------------------------------------------------- /init-scripts/debian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # kFreeBSD do not accept scripts as interpreters, using #!/bin/sh and sourcing. 3 | if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then 4 | set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script 5 | fi 6 | ### BEGIN INIT INFO 7 | # Provides: fail2rest 8 | # Required-Start: $remote_fs $syslog 9 | # Required-Stop: $remote_fs $syslog 10 | # Should-Start: fail2ban 11 | # Should-Stop: fail2ban 12 | # Default-Start: 2 3 4 5 13 | # Default-Stop: 0 1 6 14 | # Short-Description: fail2rest initscript 15 | # Description: fail2rest is a small 16 | # REST server that aims 17 | # to allow full administration 18 | # of a fail2ban server via HTTP 19 | # 20 | ### END INIT INFO 21 | 22 | 23 | USER="root" 24 | #FIXME your GOPATH 25 | GOPATH="GOPATH" 26 | WORKDIR="/var/run/fail2ban" 27 | #FIXME path to your fail2rest binary 28 | DAEMON="FAIL2REST_BINARY" 29 | CONFIG="/etc/fail2rest.json" 30 | 31 | # Author: Sean DuBois 32 | # 33 | DESC="fail2ban REST server" 34 | NAME="fail2rest" 35 | 36 | case "$1" in 37 | start) 38 | echo "Starting $NAME ..." 39 | if [ -f "$WORKDIR/$NAME.pid" ] 40 | then 41 | echo "Already running according to $WORKDIR/$NAME.pid" 42 | exit 1 43 | fi 44 | cd "$WORKDIR" 45 | export GOPATH="$GOPATH" 46 | export PATH="PATH=/usr/sbin:/usr/bin:/sbin:/bin:$GOPATH/bin" 47 | /bin/su -m -l $USER -c "$DAEMON --config $CONFIG" > "$WORKDIR/$NAME.log" 2>&1 & 48 | PID=$! 49 | echo $PID > "$WORKDIR/$NAME.pid" 50 | echo "Started with pid $PID - Logging to $WORKDIR/$NAME.log" && exit 0 51 | ;; 52 | stop) 53 | echo "Stopping $NAME ..." 54 | if [ ! -f "$WORKDIR/$NAME.pid" ] 55 | then 56 | echo "Already stopped!" 57 | exit 1 58 | fi 59 | PID=`cat "$WORKDIR/$NAME.pid"` 60 | kill $PID 61 | rm -f "$WORKDIR/$NAME.pid" 62 | echo "stopped $NAME" && exit 0 63 | ;; 64 | restart) 65 | $0 stop 66 | sleep 1 67 | $0 start 68 | ;; 69 | status) 70 | if [ -f "$WORKDIR/$NAME.pid" ] 71 | then 72 | PID=`cat "$WORKDIR/$NAME.pid"` 73 | if [ "$(/bin/ps --no-headers -p $PID)" ] 74 | then 75 | echo "$NAME is running (pid : $PID)" && exit 0 76 | else 77 | echo "Pid $PID found in $WORKDIR/$NAME.pid, but not running." && exit 1 78 | fi 79 | else 80 | echo "$NAME is NOT running" && exit 1 81 | fi 82 | ;; 83 | *) 84 | echo "Usage: /etc/init.d/$NAME {start|stop|restart|status}" && exit 1 85 | ;; 86 | esac 87 | 88 | exit 0 89 | -------------------------------------------------------------------------------- /init-scripts/ubuntu_14_04: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | ### BEGIN INIT INFO 3 | # 4 | # Provides: fail2rest 5 | # Required-Start: $remote_fs $syslog 6 | # Required-Stop: $remote_fs $syslog 7 | # Should-Start: fail2ban 8 | # Should-Stop: fail2ban 9 | # Default-Start: 2 3 4 5 10 | # Default-Stop: 0 1 6 11 | # Short-Description: fail2rest initscript 12 | # Description: fail2rest is a small REST server that aims allow full administration of a fail2ban server via HTTP 13 | # 14 | ### END INIT INFO 15 | 16 | # PATH should only include /usr/* if it runs after the mountnfs.sh script 17 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 18 | DESC="fail2rest is a small REST server that aims to allow full administration of a fail2ban server via HTTP" 19 | NAME=fail2rest #CHANGE TO THE NAME OF YOUR FAIL2REST BINARY 20 | DAEMON_DIR=/var/www/fail2rest #CHANGE TO YOUR FAIL2REST BINARY DIRECTORY 21 | DAEMON="$DAEMON_DIR/$NAME" 22 | DAEMON_ARGS="-config=$DAEMON_DIR/config.json" #CHANGE TO THE NAME OF YOUR FAIL2REST CONFIG FILE NAME AND DIRECTORY IF NEED BE 23 | PIDFILE=/var/run/$NAME.pid 24 | SCRIPTNAME=/etc/init.d/$NAME 25 | 26 | # Load the VERBOSE setting and other rcS variables 27 | . /lib/init/vars.sh 28 | 29 | # Define LSB log_* functions. 30 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present 31 | . /lib/lsb/init-functions 32 | 33 | # 34 | # Function that starts the daemon/service 35 | # 36 | do_start () 37 | { 38 | # Return 39 | # 0 if daemon has been started 40 | # 1 if daemon was already running 41 | # 2 if daemon could not be started 42 | log_daemon_msg "Starting system $NAME daemon" 43 | start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --chdir $DAEMON_DIR --exec $DAEMON -- $DAEMON_ARGS 44 | log_end_msg $? 45 | 46 | # Add code here, if necessary, that waits for the process to be ready 47 | # to handle requests from services started subsequently which depend 48 | # on this one. As a last resort, sleep for some time. 49 | } 50 | 51 | # 52 | # Function that stops the daemon/service 53 | # 54 | do_stop() 55 | { 56 | # Return 57 | # 0 if daemon has been stopped 58 | # 1 if daemon was already stopped 59 | # 2 if daemon could not be stopped 60 | # other if a failure occurred 61 | log_daemon_msg "Stopping system $NAME daemon" 62 | start-stop-daemon --stop --retry=10 --pidfile $PIDFILE 63 | log_end_msg $? 64 | } 65 | 66 | case "$1" in 67 | start|stop) 68 | do_${1} 69 | ;; 70 | restart|reload|force-reload) 71 | do_stop 72 | do_start 73 | ;; 74 | status) 75 | status_of_proc "$NAME" "$DAEMON" && exit 0 || exit $? 76 | ;; 77 | *) 78 | echo "Usage: $SCRIPTNAME {start|stop|status|restart|reload|force-reload}" >&2 79 | exit 3 80 | ;; 81 | esac 82 | 83 | : 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fail2rest 2 | 3 | ## Overview 4 | fail2rest is a small REST server that aims to allow full administration of a fail2ban server via HTTP 5 | 6 | fail2rest will eventually be used as a backend to a small web app to make fail2ban 7 | administration and reporting easier. 8 | 9 | ## Requirements 10 | fail2rest is written in Golang, so it requires a working Golang installation. If you have never used Golang before install it from your 11 | package manager, and then follow [these](http://golang.org/doc/code.html) instructions to setup your enviroment. 12 | 13 | Some libraries currently are not building on older versions of Go, I recommend running the newest stable of Golang. 14 | 15 | ## Installing 16 | Once you have a working Golang installation all you need to do is run 17 | 18 | go get -v github.com/sean-der/fail2rest 19 | go install -v github.com/sean-der/fail2rest 20 | 21 | fail2rest will now be available as a binary in your GOPATH, you can run 'fail2rest' from the command line, or copy it 22 | somewhere else to make it available to all users. 23 | 24 | Use `whereis fail2rest` to locate your fail2rest binary. 25 | Next you need to configure fail2rest, and then finally make it a system service. 26 | 27 | ## Configuration 28 | fail2rest is configured via config.json, the default is located [here](https://raw.githubusercontent.com/sean-der/fail2rest/master/config.json). 29 | To load your config.json you use the --config flag `fail2rest --config=my_config.json` 30 | 31 | fail2rest has two options that be configured via config.json 32 | * **Fail2banSocket** - The path to the fail2ban socket, can usually be found via `grep socket /etc/fail2ban/fail2ban.conf` you also have to run fail2rest as a user who has permissions to use this socket 33 | * **Addr** - The address that fail2rest is served upon, it is usually best so serve to the loopback, and then allow access via nginx see an example config in the [fail2web](https://github.com/sean-der/fail2web) repository 34 | 35 | The default configuration file used by init-scripts is `/etc/fail2rest.json`. You should download the config to your /tmp/ dir and modify it to your needs. 36 | 37 | cd /tmp/ 38 | wget https://raw.githubusercontent.com/sean-der/fail2rest/master/config.json 39 | 40 | Once you finished editing the configuration file, you should move it from /tmp/config.json to /etc/fail2rest.json executing `mv /tmp/config.json /etc/fail2rest.json` 41 | 42 | ## Running 43 | Once you have a config.json all you need to do is run `fail2rest --config config.json` 44 | 45 | However, fail2rest is designed to run as a service, so init scripts are provided that allow easy management of fail2rest. They can be found [here](https://github.com/sean-der/fail2rest/tree/master/init-scripts) 46 | Download the appropriate init file your Distribution. You may need to customize your init script to load your config.json, but most scripts default to /etc/fail2rest.json 47 | 48 | ## Service 49 | ### systemd 50 | To run as a service you can either copy or create symlinks for systemd and the fail2rest binary. The systemd file shold be added to /etc/systemd/system/fail2rest.service and the the binary to /usr/bin/fail2rest. This scenario will use the symlinks in order to always use the latest files. You should run the commands with sudo if not logged in as root: 51 | 52 | ln -s $GOPATH/bin/fail2rest /usr/bin/ 53 | 54 | Debian 55 | 56 | ln -s $GOPATH/src/github.com/sean-der/fail2rest/init-scripts/systemd /etc/systemd/system/fail2rest.service 57 | 58 | Other Linux 59 | 60 | ln -s $GOPATH/src/github.com/sean-der/fail2rest/init-scripts/systemd /usr/lib/systemd/system/fail2rest.service 61 | 62 | Enable fail2rest service to run at startup 63 | 64 | systemctl enable fail2rest.service 65 | 66 | Run the following systemd command to start the fail2rest service 67 | 68 | systemctl start fail2rest.service 69 | 70 | Verify that the fail2rest service it is active and running 71 | 72 | systemctl status fail2rest.service 73 | 74 | ## License 75 | The MIT License (MIT) 76 | 77 | Copyright (c) 2014 Sean DuBois 78 | 79 | Permission is hereby granted, free of charge, to any person obtaining a copy 80 | of this software and associated documentation files (the "Software"), to deal 81 | in the Software without restriction, including without limitation the rights 82 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 83 | copies of the Software, and to permit persons to whom the Software is 84 | furnished to do so, subject to the following conditions: 85 | 86 | The above copyright notice and this permission notice shall be included in 87 | all copies or substantial portions of the Software. 88 | 89 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 90 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 91 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 92 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 93 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 94 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 95 | THE SOFTWARE. 96 | -------------------------------------------------------------------------------- /jail.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "net/http" 7 | "os" 8 | "regexp" 9 | "strings" 10 | 11 | "github.com/gorilla/mux" 12 | "github.com/sean-der/fail2go" 13 | ) 14 | 15 | func jailGetHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 16 | currentlyFailed, totalFailed, fileList, currentlyBanned, totalBanned, IPList, err := fail2goConn.JailStatus(mux.Vars(req)["jail"]) 17 | if err != nil { 18 | writeHTTPError(res, err) 19 | return 20 | } 21 | 22 | failRegexes, _ := fail2goConn.JailFailRegex(mux.Vars(req)["jail"]) 23 | findTime, _ := fail2goConn.JailFindTime(mux.Vars(req)["jail"]) 24 | useDNS, _ := fail2goConn.JailUseDNS(mux.Vars(req)["jail"]) 25 | maxRetry, _ := fail2goConn.JailMaxRetry(mux.Vars(req)["jail"]) 26 | actions, _ := fail2goConn.JailActions(mux.Vars(req)["jail"]) 27 | 28 | if IPList == nil { 29 | IPList = []string{} 30 | } 31 | if failRegexes == nil { 32 | failRegexes = []string{} 33 | } 34 | 35 | encodedOutput, _ := json.Marshal(map[string]interface{}{ 36 | "currentlyFailed": currentlyFailed, 37 | "totalFailed": totalFailed, 38 | "fileList": fileList, 39 | "currentlyBanned": currentlyBanned, 40 | "totalBanned": totalBanned, 41 | "IPList": IPList, 42 | "failRegexes": failRegexes, 43 | "findTime": findTime, 44 | "useDNS": useDNS, 45 | "maxRetry": maxRetry, 46 | "actions": actions}) 47 | res.Write(encodedOutput) 48 | } 49 | 50 | type jailBanIPBody struct { 51 | IP string 52 | } 53 | 54 | func jailBanIPHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 55 | var input jailBanIPBody 56 | json.NewDecoder(req.Body).Decode(&input) 57 | 58 | output, err := fail2goConn.JailBanIP(mux.Vars(req)["jail"], input.IP) 59 | if err != nil { 60 | writeHTTPError(res, err) 61 | return 62 | } 63 | 64 | encodedOutput, _ := json.Marshal(map[string]interface{}{"bannedIP": output}) 65 | res.Write(encodedOutput) 66 | } 67 | 68 | func jailUnbanIPHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 69 | var input jailBanIPBody 70 | json.NewDecoder(req.Body).Decode(&input) 71 | output, err := fail2goConn.JailUnbanIP(mux.Vars(req)["jail"], input.IP) 72 | if err != nil { 73 | writeHTTPError(res, err) 74 | return 75 | } 76 | 77 | encodedOutput, _ := json.Marshal(map[string]interface{}{"unBannedIP": output}) 78 | res.Write(encodedOutput) 79 | } 80 | 81 | type jailFailRegexBody struct { 82 | FailRegex string 83 | } 84 | 85 | func jailAddFailRegexHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 86 | var input jailFailRegexBody 87 | json.NewDecoder(req.Body).Decode(&input) 88 | 89 | output, err := fail2goConn.JailAddFailRegex(mux.Vars(req)["jail"], input.FailRegex) 90 | if err != nil { 91 | writeHTTPError(res, err) 92 | return 93 | } 94 | 95 | encodedOutput, _ := json.Marshal(map[string]interface{}{"FailRegex": output}) 96 | res.Write(encodedOutput) 97 | } 98 | 99 | func jailDeleteFailRegexHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 100 | var input jailFailRegexBody 101 | json.NewDecoder(req.Body).Decode(&input) 102 | 103 | output, err := fail2goConn.JailDeleteFailRegex(mux.Vars(req)["jail"], input.FailRegex) 104 | if err != nil { 105 | writeHTTPError(res, err) 106 | return 107 | } 108 | 109 | encodedOutput, _ := json.Marshal(map[string]interface{}{"FailRegex": output}) 110 | res.Write(encodedOutput) 111 | } 112 | 113 | type RegexResult struct { 114 | Line string 115 | Match bool 116 | } 117 | 118 | func jailTestFailRegexHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 119 | var input jailFailRegexBody 120 | json.NewDecoder(req.Body).Decode(&input) 121 | 122 | regexp, err := regexp.Compile(strings.Replace(input.FailRegex, "", "(?:::f{4,6}:)?(?P\\S+)", -1)) 123 | 124 | if err != nil { 125 | writeHTTPError(res, err) 126 | return 127 | } 128 | 129 | _, _, fileList, _, _, _, err := fail2goConn.JailStatus(mux.Vars(req)["jail"]) 130 | if err != nil { 131 | writeHTTPError(res, err) 132 | return 133 | } 134 | 135 | output := make(map[string][]RegexResult) 136 | for _, fileName := range fileList { 137 | file, err := os.Open(fileName) 138 | if err != nil { 139 | writeHTTPError(res, err) 140 | return 141 | } 142 | scanner := bufio.NewScanner(file) 143 | for scanner.Scan() { 144 | output[fileName] = append(output[fileName], RegexResult{Match: regexp.MatchString(scanner.Text()), Line: scanner.Text()}) 145 | } 146 | } 147 | 148 | encodedOutput, _ := json.Marshal(output) 149 | res.Write(encodedOutput) 150 | } 151 | 152 | type jailFindTimeBody struct { 153 | FindTime int 154 | } 155 | 156 | func jailSetFindTimeHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 157 | var input jailFindTimeBody 158 | json.NewDecoder(req.Body).Decode(&input) 159 | 160 | output, err := fail2goConn.JailSetFindTime(mux.Vars(req)["jail"], input.FindTime) 161 | if err != nil { 162 | writeHTTPError(res, err) 163 | return 164 | } 165 | 166 | encodedOutput, _ := json.Marshal(map[string]interface{}{"FindTime": output}) 167 | res.Write(encodedOutput) 168 | } 169 | 170 | type jailUseDNSBody struct { 171 | UseDNS string 172 | } 173 | 174 | func jailSetUseDNSHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 175 | var input jailUseDNSBody 176 | json.NewDecoder(req.Body).Decode(&input) 177 | 178 | output, err := fail2goConn.JailSetUseDNS(mux.Vars(req)["jail"], input.UseDNS) 179 | if err != nil { 180 | writeHTTPError(res, err) 181 | return 182 | } 183 | 184 | encodedOutput, _ := json.Marshal(map[string]interface{}{"useDNS": output}) 185 | res.Write(encodedOutput) 186 | } 187 | 188 | type jailMaxRetryBody struct { 189 | MaxRetry int 190 | } 191 | 192 | func jailSetMaxRetryHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 193 | var input jailMaxRetryBody 194 | json.NewDecoder(req.Body).Decode(&input) 195 | 196 | output, err := fail2goConn.JailSetMaxRetry(mux.Vars(req)["jail"], input.MaxRetry) 197 | if err != nil { 198 | writeHTTPError(res, err) 199 | return 200 | } 201 | 202 | encodedOutput, _ := json.Marshal(map[string]interface{}{"maxRetry": output}) 203 | res.Write(encodedOutput) 204 | } 205 | 206 | func jailActionHandler(res http.ResponseWriter, req *http.Request, fail2goConn *fail2go.Conn) { 207 | port, err := fail2goConn.JailActionProperty(mux.Vars(req)["jail"], mux.Vars(req)["action"], "port") 208 | if err != nil { 209 | writeHTTPError(res, err) 210 | return 211 | } 212 | 213 | encodedOutput, _ := json.Marshal(map[string]interface{}{ 214 | "port": port}) 215 | res.Write(encodedOutput) 216 | } 217 | 218 | func jailHandler(jailRouter *mux.Router, fail2goConn *fail2go.Conn) { 219 | 220 | jailRouter.HandleFunc("/{jail}/bannedip", func(res http.ResponseWriter, req *http.Request) { 221 | jailBanIPHandler(res, req, fail2goConn) 222 | }).Methods("POST") 223 | jailRouter.HandleFunc("/{jail}/bannedip", func(res http.ResponseWriter, req *http.Request) { 224 | jailUnbanIPHandler(res, req, fail2goConn) 225 | }).Methods("DELETE") 226 | 227 | jailRouter.HandleFunc("/{jail}/failregex", func(res http.ResponseWriter, req *http.Request) { 228 | jailAddFailRegexHandler(res, req, fail2goConn) 229 | }).Methods("POST") 230 | jailRouter.HandleFunc("/{jail}/failregex", func(res http.ResponseWriter, req *http.Request) { 231 | jailDeleteFailRegexHandler(res, req, fail2goConn) 232 | }).Methods("DELETE") 233 | 234 | jailRouter.HandleFunc("/{jail}/testfailregex", func(res http.ResponseWriter, req *http.Request) { 235 | jailTestFailRegexHandler(res, req, fail2goConn) 236 | }).Methods("POST") 237 | 238 | jailRouter.HandleFunc("/{jail}/findtime", func(res http.ResponseWriter, req *http.Request) { 239 | jailSetFindTimeHandler(res, req, fail2goConn) 240 | }).Methods("POST") 241 | 242 | jailRouter.HandleFunc("/{jail}/usedns", func(res http.ResponseWriter, req *http.Request) { 243 | jailSetUseDNSHandler(res, req, fail2goConn) 244 | }).Methods("POST") 245 | 246 | jailRouter.HandleFunc("/{jail}/maxretry", func(res http.ResponseWriter, req *http.Request) { 247 | jailSetMaxRetryHandler(res, req, fail2goConn) 248 | }).Methods("POST") 249 | 250 | jailRouter.HandleFunc("/{jail}/action/{action}", func(res http.ResponseWriter, req *http.Request) { 251 | jailActionHandler(res, req, fail2goConn) 252 | }).Methods("GET") 253 | 254 | jailRouter.HandleFunc("/{jail}", func(res http.ResponseWriter, req *http.Request) { 255 | jailGetHandler(res, req, fail2goConn) 256 | }).Methods("GET") 257 | } 258 | --------------------------------------------------------------------------------