From 689f3fadd893afacad1dd217ea5cf802c556f2cd Mon Sep 17 00:00:00 2001 From: Michael Reber Date: Wed, 29 Jan 2025 19:49:51 +0100 Subject: [PATCH] Update file-headers and add a testing geoip_notify script --- README.md | 3 +- internal/config/settings.go | 32 +++++---- internal/fail2ban/client.go | 18 ++++- internal/fail2ban/logparse.go | 16 +++++ internal/geoip_notify.py | 132 ++++++++++++++++++++++++++++++++++ pkg/web/handlers.go | 16 +++++ pkg/web/routes.go | 16 +++++ pkg/web/templates/index.html | 17 +++++ 8 files changed, 233 insertions(+), 17 deletions(-) create mode 100644 internal/geoip_notify.py diff --git a/README.md b/README.md index 5890f6a..328cd55 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Fail2ban UI -A **Go**-powered, **single-page** web interface for [Fail2ban](https://www.fail2ban.org/). +A Swissmade, management interface for [Fail2ban](https://www.fail2ban.org/). It provides a modern dashboard to currently: - View all Fail2ban jails and banned IPs @@ -8,6 +8,7 @@ It provides a modern dashboard to currently: - Edit and save jail/filter configs - Reload Fail2ban when needed - See recent ban events +- More to come... Built by [Swissmakers GmbH](https://swissmakers.ch). diff --git a/internal/config/settings.go b/internal/config/settings.go index e4350ff..30bf18c 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -1,3 +1,19 @@ +// Fail2ban UI - A Swiss made, management interface for Fail2ban. +// +// Copyright (C) 2025 Swissmakers GmbH (https://swissmakers.ch) +// +// Licensed under the GNU General Public License, Version 3 (GPL-3.0) +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package config import ( @@ -252,20 +268,7 @@ norestored = 1 # Option: actionban # This executes the Python script with and the list of allowed countries. # If the country matches the allowed list, it sends the email. -actionban = /etc/fail2ban/scripts/check_geoip.py "%s" && ( - printf %%%%b "Subject: [Fail2Ban] : banned from - Date: `+"`LC_ALL=C date +\"%%%%a, %%%%d %%%%h %%%%Y %%%%T %%%%z\"`"+` - From: <> - To: \n - Hi,\n - The IP has just been banned by Fail2Ban after attempts against .\n\n - Here is more information about :\n" - %%(_whois_command)s; - printf %%%%b "\nLines containing failures of (max )\n"; - %%(_grep_logs)s; - printf %%%%b "\n - Regards,\n - Fail2Ban" ) | +actionban = /etc/fail2ban/action.d/geoip_notify.py "%s" 'name=;ip=;fq-hostname=;sendername=;sender=;dest=;failures=;_whois_command=%%(_whois_command)s;_grep_logs=%%(_grep_logs)s;grepmax=;mailcmd=' [Init] @@ -281,7 +284,6 @@ logpath = /dev/null `, countriesFormatted) // Write the action file - //actionFilePath := "/etc/fail2ban/action.d/ui-custom-action.conf" err := os.WriteFile(actionFile, []byte(actionConfig), 0644) if err != nil { return fmt.Errorf("failed to write action file: %w", err) diff --git a/internal/fail2ban/client.go b/internal/fail2ban/client.go index 81fea76..654aa8b 100644 --- a/internal/fail2ban/client.go +++ b/internal/fail2ban/client.go @@ -1,3 +1,19 @@ +// Fail2ban UI - A Swiss made, management interface for Fail2ban. +// +// Copyright (C) 2025 Swissmakers GmbH (https://swissmakers.ch) +// +// Licensed under the GNU General Public License, Version 3 (GPL-3.0) +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package fail2ban import ( @@ -21,7 +37,7 @@ func GetJails() ([]string, error) { cmd := exec.Command("fail2ban-client", "status") out, err := cmd.CombinedOutput() if err != nil { - return nil, fmt.Errorf("could not run 'fail2ban-client status': %v", err) + return nil, fmt.Errorf("could not get jail information. is fail2ban running? error: %v", err) } var jails []string diff --git a/internal/fail2ban/logparse.go b/internal/fail2ban/logparse.go index 66bc211..e77913b 100644 --- a/internal/fail2ban/logparse.go +++ b/internal/fail2ban/logparse.go @@ -1,3 +1,19 @@ +// Fail2ban UI - A Swiss made, management interface for Fail2ban. +// +// Copyright (C) 2025 Swissmakers GmbH (https://swissmakers.ch) +// +// Licensed under the GNU General Public License, Version 3 (GPL-3.0) +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package fail2ban import ( diff --git a/internal/geoip_notify.py b/internal/geoip_notify.py new file mode 100644 index 0000000..d9cf4d5 --- /dev/null +++ b/internal/geoip_notify.py @@ -0,0 +1,132 @@ +#!/usr/bin/python3.9 +""" +Fail2ban UI - A Swiss made, management interface for Fail2ban. + +Copyright (C) 2025 Swissmakers GmbH + +Licensed under the GNU General Public License, Version 3 (GPL-3.0) +You may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.gnu.org/licenses/gpl-3.0.en.html + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# This file is for testing purposes only. +# (Must be copied to "/etc/fail2ban/action.d/geoip_notify.py") +#python3.9 -c "import maxminddb; print('maxminddb is installed successfully')" + +import sys +import subprocess + +# Manually set Python path where maxminddb is installed +sys.path.append("/usr/local/lib64/python3.9/site-packages/") + +try: + import maxminddb +except ImportError: + print("Error: maxminddb module not found, even after modifying PYTHONPATH.") + sys.exit(1) + + +# Path to MaxMind GeoIP2 database +GEOIP_DB_PATH = "/usr/share/GeoIP/GeoLite2-Country.mmdb" + +def get_country(ip): + """ + Perform a GeoIP lookup to get the country code from an IP address. + Returns the country code (e.g., "CH") or None if lookup fails. + """ + try: + with maxminddb.open_database(GEOIP_DB_PATH) as reader: + geo_data = reader.get(ip) + if geo_data and "country" in geo_data and "iso_code" in geo_data["country"]: + return geo_data["country"]["iso_code"] + except Exception as e: + print(f"GeoIP lookup failed: {e}", file=sys.stderr) + return None + +def parse_placeholders(placeholder_str): + """ + Parses Fail2Ban placeholders passed as a string in "key=value" format. + Returns a dictionary. + """ + placeholders = {} + for item in placeholder_str.split(";"): + key_value = item.split("=", 1) + if len(key_value) == 2: + key, value = key_value + placeholders[key.strip()] = value.strip() + return placeholders + +def send_email(placeholders): + """ + Generates and sends the email alert using sendmail. + """ + email_content = f"""Subject: [Fail2Ban] {placeholders['name']}: banned {placeholders['ip']} from {placeholders['fq-hostname']} +Date: $(LC_ALL=C date +"%a, %d %h %Y %T %z") +From: {placeholders['sendername']} <{placeholders['sender']}> +To: {placeholders['dest']} + +Hi, + +The IP {placeholders['ip']} has just been banned by Fail2Ban after {placeholders['failures']} attempts against {placeholders['name']}. + +Here is more information about {placeholders['ip']}: +{subprocess.getoutput(placeholders['_whois_command'])} + +Lines containing failures of {placeholders['ip']} (max {placeholders['grepmax']}): +{subprocess.getoutput(placeholders['_grep_logs'])} + +Regards, +Fail2Ban""" + + try: + subprocess.run( + ["/usr/sbin/sendmail", "-f", placeholders["sender"], placeholders["dest"]], + input=email_content, + text=True, + check=True + ) + print("Email sent successfully.") + except subprocess.CalledProcessError as e: + print(f"Failed to send email: {e}", file=sys.stderr) + +def main(ip, allowed_countries, placeholder_str): + """ + Main function to check the IP's country and send an email if it matches the allowed list. + """ + allowed_countries = allowed_countries.split(",") + placeholders = parse_placeholders(placeholder_str) + + # Perform GeoIP lookup + country = get_country(ip) + if not country: + print(f"Could not determine country for IP {ip}", file=sys.stderr) + sys.exit(1) + + print(f"IP {ip} belongs to country: {country}") + + # If the country is in the allowed list or "ALL" is selected, send the email + if "ALL" in allowed_countries or country in allowed_countries: + print(f"IP {ip} is in the alert countries list. Sending email...") + send_email(placeholders) + else: + print(f"IP {ip} is NOT in the alert countries list. No email sent.") + sys.exit(0) # Exit normally without error + +if __name__ == "__main__": + if len(sys.argv) != 4: + print("Usage: geoip_notify.py ", file=sys.stderr) + sys.exit(1) + + ip = sys.argv[1] + allowed_countries = sys.argv[2] + placeholders = sys.argv[3] + + main(ip, allowed_countries, placeholders) diff --git a/pkg/web/handlers.go b/pkg/web/handlers.go index cf6dcca..c4eb0fb 100644 --- a/pkg/web/handlers.go +++ b/pkg/web/handlers.go @@ -1,3 +1,19 @@ +// Fail2ban UI - A Swiss made, management interface for Fail2ban. +// +// Copyright (C) 2025 Swissmakers GmbH (https://swissmakers.ch) +// +// Licensed under the GNU General Public License, Version 3 (GPL-3.0) +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package web import ( diff --git a/pkg/web/routes.go b/pkg/web/routes.go index 9878065..674d693 100644 --- a/pkg/web/routes.go +++ b/pkg/web/routes.go @@ -1,3 +1,19 @@ +// Fail2ban UI - A Swiss made, management interface for Fail2ban. +// +// Copyright (C) 2025 Swissmakers GmbH (https://swissmakers.ch) +// +// Licensed under the GNU General Public License, Version 3 (GPL-3.0) +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package web import ( diff --git a/pkg/web/templates/index.html b/pkg/web/templates/index.html index 08df4e5..63ea496 100644 --- a/pkg/web/templates/index.html +++ b/pkg/web/templates/index.html @@ -1,3 +1,20 @@ +