Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .assets/Example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ language: shell

branches:
only:
- <baseimagename>-<modname> #replace variables, omit brackets
- letsencrypt-f2bdiscord #replace variables, omit brackets

services:
- docker

env:
global:
- DOCKERHUB="linuxserver/mods" #don't modify
- BASEIMAGE="baseimagename" #replace
- MODNAME="modname" #replace
- BASEIMAGE="letsencrypt" #replace
- MODNAME="f2bdiscord" #replace

jobs:
include:
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
FROM scratch
LABEL maintainer="Roxedus"


# copy local files
COPY root/ /
21 changes: 0 additions & 21 deletions Dockerfile.complex

This file was deleted.

45 changes: 33 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,38 @@
# Rsync - Docker mod for openssh-server
# F2B Discord Notification - Docker mod which allows Fail2Ban Discord embeds

This mod adds rsync to openssh-server, to be installed/updated during container start.
This mod enhances the Letsencrypt container adding better Fail2Ban notifications for discord.

In openssh-server docker arguments, set an environment variable `DOCKER_MODS=linuxserver/mods:openssh-server-rsync`
## Configuration

If adding multiple mods, enter them in an array separated by `|`, such as `DOCKER_MODS=linuxserver/mods:openssh-server-rsync|linuxserver/mods:openssh-server-mod2`
### Enable

# Mod creation instructions
In letsencrypt docker arguments, set an environment variable `DOCKER_MODS=linuxserver/mods:letsencrypt-f2bdiscord` to enable.

* Ask the team to create a new branch named `<baseimagename>-<modname>`. Baseimage should be the name of the image the mod will be applied to. The new branch will be based on the `template` branch.
* Fork the repo, checkout the newly created branch.
* Edit the `Dockerfile` for the mod. `Dockerfile.complex` is only an example and included for reference; it should be deleted when done.
* Inspect the `root` folder contents. Edit, add and remove as necessary.
* Edit this readme with pertinent info, delete these instructions.
* Finally edit the `travis.yml`. Customize the build branch, and the vars for `BASEIMAGE` and `MODNAME`.
* Submit PR against the branch created by the team.
### Mod configuration

**Environment variables used by this mod:**

[Discord webhook](https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks), it just need the last parts. `-e DISC_HOOK=40832456738934/7DcEpWr5V24OIEIELjg-KkHky86SrOgTqA`
[Your discord ID](https://support.discordapp.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-). `-e DISC_ME=120970603556503552`
[Map API Key](https://developer.mapquest.com/), get a key from mapquest. `-e DISC_API=YourKey`

#### Jail configuration example

```ini
[bitwarden]

filter = bitwarden
enabled = true
logpath = /fail2ban/bw/bitwarden.log
action = discordEmbed[bantime=24]
iptables-allports[name=Bitwarden]

```

Action arguments:

`bantime`(hour) is optional, but defaults to 24 when not set. Just reflects in the message, does not change the ban time

## Example

![Example picture](.assets/Example.png)
98 changes: 98 additions & 0 deletions root/AwesomeFolder/Fail2Ban.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env python3
import argparse
import collections
import datetime
import geoip2.database
import os
import requests

class Discord:
def __init__(self, data, action):
self.action = action
self.base = "https://discordapp.com/api/webhooks/"
self.data = data
self.token = os.getenv('DISC_HOOK', "") # If not setting enviroment variables, edit this
self.you = os.getenv('DISC_ME', "120970603556503552") # If not setting enviroment variables, edit this

def create_payload(self):
webhook = {
"username":"Fail2Ban",
"content": f"<@{self.you}>",
"embeds": [{}]
}
webhook["embeds"][0]["author"] = {"name": "Fail2Ban"}
webhook["embeds"][0]["timestamp"] = f"{datetime.datetime.utcnow()}"
if "ban" in self.action.action:
webhook["embeds"][0]["url"] = f"https://db-ip.com/{self.data['ip']}"
webhook["embeds"][0]["image"] = {"url": f"{self.data['map-img']}"}
webhook["embeds"][0]["fields"] = [{}]
webhook["embeds"][0]["fields"][0]["name"] = f":flag_{self.data['iso'].lower()}:"
webhook["embeds"][0]["fields"][0]["value"] = self.data["city"] or self.data["name"]
if self.action.action == "ban":
webhook["embeds"][0]["fields"].append({"name": f"Map", "value": f"[Link]({self.data['map-url']})"})
webhook["embeds"][0]["fields"].append({"name": f"Unban cmd", "value": f"fail2ban-client unban {self.data['ip']}"})
webhook["embeds"][0]["title"] = f"New ban on `{self.action.jail}`"
webhook["embeds"][0]["description"] = f"**{self.data['ip']}** got banned for `{self.action.time}` hours after `{self.action.fail}` tries"
webhook["embeds"][0]["color"] = 16194076
elif self.action.action == "unban":
webhook["embeds"][0]["title"] = f"Revoked ban on `{self.action.jail}`"
webhook["embeds"][0]["description"] = f"**{self.data['ip']}** is now unbanned"
webhook["embeds"][0]["color"] = 845872
elif self.action.action == "start":
webhook["content"] = ""
webhook["embeds"][0]["description"] = f"Started `{self.action.jail}`"
webhook["embeds"][0]["color"] = 845872
elif self.action.action == "stopped":
webhook["content"] = ""
webhook["embeds"][0]["description"] = f"Stopped `{self.action.jail}`"
webhook["embeds"][0]["color"] = 16194076
elif self.action.action == "test":
webhook["content"] = ""
webhook["embeds"][0]["description"] = f"I am working"
webhook["embeds"][0]["color"] = 845872
else:
return None
return webhook

def send(self, payload):
r = requests.post(url=f"{self.base}{self.token}", json=payload)

class Helpers:
def __init__(self, ip):
self.data = {"ip": ip}
self.map_api = os.getenv('DISC_API', "") # If not setting enviroment variables, edit this
self.reader = geoip2.database.Reader('/config/geoip2db/GeoLite2-City.mmdb')
self.f2b()
self.map()

def f2b(self):
r = self.reader.city(self.data['ip'])
self.data["iso"] = r.country.iso_code
self.data["name"] = r.country.name
self.data["city"] = r.city.name
self.data["lat"] = r.location.latitude
self.data["lon"] = r.location.longitude


def map(self):
img_params={"center":f"{self.data['lat']},{self.data['lon']}", "size":"500,300", "key": self.map_api}
img_r = requests.get('https://www.mapquestapi.com/staticmap/v5/map', params=img_params)
self.data["map-img"] = img_r.url
url_params={"center":f"{self.data['lat']},{self.data['lon']}", "size":"500,300"}
url_r = requests.get('https://mapquest.com/', params=url_params)
self.data["map-url"] = url_r.url

if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Discord notifier for F2B')
parser.add_argument('-a', '--action', help="Which F2B action triggered the script", required=True)
parser.add_argument('-i', '--ip', help="ip which triggered the action", default="1.1.1.1")
parser.add_argument('-j', '--jail', help="jail which triggered the action")
parser.add_argument('-t', '--time', help="The time the action is valid")
parser.add_argument('-f', '--fail', help="Amount of attempts done")

args = parser.parse_args()

data = Helpers(args.ip).data
disc = Discord(data, args)
if (payload := disc.create_payload()):
disc.send(payload)
23 changes: 23 additions & 0 deletions root/AwesomeFolder/discordEmbed.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Author: Roxedus
# Adapted Source: Gilbn from https://technicalramblings.com

[Definition]

# Notify on Startup
actionstart = python3 /config/fail2ban/Fail2Ban.py -a start -j <name>

# Notify on Shutdown
actionstop = python3 /config/fail2ban/Fail2Ban.py -a stopped -j <name>

#
actioncheck =

# Notify on Banned
actionban = python3 /config/fail2ban/Fail2Ban.py -a ban -j <name> -i <ip> -t <bantime> -f <failures>

# Notify on Unbanned
actionunban = python3 /config/fail2ban/Fail2Ban.py -a unban -j <name> -i <ip> -t <bantime> -f <failures>
[Init]

# Name of the jail in your jail.local file. default = [your-jail-name]
name = default
20 changes: 20 additions & 0 deletions root/etc/cont-init.d/49-F2BDiscord
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/with-contenv bash

if [ ! -d /usr/lib/python3.8/site-packages/geoip2 ]; then
echo '------------------------------------------------------------------------'
echo '| Running installation of required modules for letsencrypt-f2bdiscord'
echo '------------------------------------------------------------------------'
pip3 install --no-cache-dir -U \
requests \
argparse \
geoip2
fi

if [ ! -f /config/fail2ban/Fail2Ban.py ]; then
cp /AwesomeFolder/Fail2Ban.py /config/fail2ban/
chmod +x /config/fail2ban/Fail2Ban.py
fi

if [ ! -f /config/fail2ban/action.d/discordEmbed.conf ]; then
cp /AwesomeFolder/discordEmbed.conf /config/fail2ban/action.d
fi
27 changes: 0 additions & 27 deletions root/etc/cont-init.d/98-vpn-config

This file was deleted.

3 changes: 0 additions & 3 deletions root/etc/services.d/sshvpn/run

This file was deleted.