From 40007b0866982e9baee3e365814f45b02a6fc8c3 Mon Sep 17 00:00:00 2001 From: bunkerity Date: Mon, 27 Jun 2022 15:52:01 +0200 Subject: [PATCH] add slack to official plugins and init work on EXTERNAL_PLUGIN_URLS setting --- CHANGELOG.md | 3 +- README.md | 1 + core/jobs/jobs/download-plugins.py | 88 ++++++++++++++++++++++++++++++ core/jobs/plugin.json | 6 ++ core/misc/plugin.json | 9 +++ docs/plugins.md | 9 +++ 6 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 core/jobs/jobs/download-plugins.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 165a1d16..d36cb419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,11 @@ - Fix permissions check when filename has a space - Fix static config (SERVER_NAME not empty) support when using autoconf/swarm/k8s - Fix config files overwrite when using Docker autoconf +- Add EXTERNAL_PLUGIN_URLS setting to automatically download and install external plugins - Add log_default() plugin hook - Add various certbot-dns examples - Force NGINX version dependencies in Linux packages DEB/RPM -- Add Discord to supported official plugins +- Add Discord and Slack to list of official plugins ## v1.4.1 - 2022/16/06 diff --git a/README.md b/README.md index a0711d5a..2ab0a2a8 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,7 @@ Here is the list of "official" plugins that we maintain (see the [bunkerweb-plug | **ClamAV** | 0.1 | Automatically scans uploaded files with the ClamAV antivirus engine and denies the request when a file is detected as malicious. | [bunkerweb-plugins/clamav](https://github.com/bunkerity/bunkerweb-plugins/tree/main/clamav) | | **CrowdSec** | 0.1 | CrowdSec bouncer for BunkerWeb. | [bunkerweb-plugins/crowdsec](https://github.com/bunkerity/bunkerweb-plugins/tree/main/crowdsec) | | **Discord** | 0.1 | Send security notifications to a Discord channel using a Webhook. | [bunkerweb-plugins/discord](https://github.com/bunkerity/bunkerweb-plugins/tree/main/discord) | +| **Slack** | 0.1 | Send security notifications to a Slack channel using a Webhook. | [bunkerweb-plugins/slack](https://github.com/bunkerity/bunkerweb-plugins/tree/main/slack) | | **VirusTotal** | 0.1 | Automatically scans uploaded files with the VirusTotal API and denies the request when a file is detected as malicious. | [bunkerweb-plugins/virustotal](https://github.com/bunkerity/bunkerweb-plugins/tree/main/virustotal) | You will find more information in the [plugins section](https://docs.bunkerweb.io/latest/plugins) of the documentation. diff --git a/core/jobs/jobs/download-plugins.py b/core/jobs/jobs/download-plugins.py new file mode 100644 index 00000000..52c2e9c9 --- /dev/null +++ b/core/jobs/jobs/download-plugins.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 + +import sys +sys.path.append("/opt/bunkerweb/deps/python") +sys.path.append("/opt/bunkerweb/utils") + +from requests import get +from zipfile import ZipFile +from io import BytesIO +from os import getenv, makedirs, chmod, stat +from os.path import isfile, dirname +from stat import S_IEXEC +from uuid import uuid4 +from glob import glob +from json import loads +from shutil import copytree, rmtree +from traceback import format_exc + +from logger import log + +status = 0 + +def install_plugin(plugin_dir) : + # Load plugin.json + metadata = {} + with open(plugin_dir + "plugin.json", "r") as f : + metadata = loads(f.read()) + # Don't go further if plugin is already installed + if isfile("/data/plugins/" + metadata["id"] + "/plugin.json") : + log("JOBS", "ℹ️", "Skipping installation of plugin " + metadata["id"] + " (already installed)") + return + # Copy the plugin + copytree(plugin_dir, "/data/plugins/" + metadata["id"]) + # Add u+x permissions to jobs files + for job_file in glob(plugin_dir + "jobs/*") : + st = stat(job_file) + chmod(job_file, st.st_mode | S_IEXEC) + +try : + + # Check if we have plugins to download + plugin_urls = getenv("EXTERNAL_PLUGIN_URLS", "") + if plugin_urls == "" : + log("JOBS", "ℹ️", "No external plugins to download") + + # Loop on URLs + for plugin_url in plugin_urls.split(" ") : + + # Download ZIP file + try : + req = get(plugin_url) + except : + log("JOBS", "❌", "Exception while downloading plugin(s) from " + plugin_url + " :") + print(format_exc()) + status = 2 + continue + + # Extract it to tmp folder + temp_dir = "/opt/bunkerweb/tmp/plugins-" + str(uuid4()) + "/" + try : + makedirs(temp_dir, exist_ok=True) + with ZipFile(BytesIO(req.content)) as zf : + zf.extractall(path=temp_dir) + except : + log("JOBS", "❌", "Exception while decompressing plugin(s) from " + plugin_url + " :") + print(format_exc()) + status = 2 + continue + + # Install plugins + try : + for plugin_dir in glob(temp_dir + "**/plugin.json", recursive=True) : + install_plugin(dirname(plugin_dir) + "/") + except : + log("JOBS", "❌", "Exception while installing plugin(s) from " + plugin_url + " :") + print(format_exc()) + status = 2 + continue + +except : + status = 2 + log("JOBS", "❌", "Exception while running download-plugins.py :") + print(format_exc()) + +for plugin_tmp in glob("/opt/bunkerweb/tmp/plugins-*/") : + rmtree(plugin_tmp) + +sys.exit(status) diff --git a/core/jobs/plugin.json b/core/jobs/plugin.json index ecf71791..b2e90762 100644 --- a/core/jobs/plugin.json +++ b/core/jobs/plugin.json @@ -18,6 +18,12 @@ "file": "mmdb-asn.py", "every": "week", "reload": true + }, + { + "name": "download-plugins", + "file": "download-plugins.py", + "every": "once", + "reload": false } ] } diff --git a/core/misc/plugin.json b/core/misc/plugin.json index a66070a0..e8dd77eb 100644 --- a/core/misc/plugin.json +++ b/core/misc/plugin.json @@ -139,6 +139,15 @@ "label": "Open file cache valid time", "regex": "^\\d+(ms|s|m|h|d|w|M|y)$", "type": "text" + }, + "EXTERNAL_PLUGIN_URLS" : { + "context": "global", + "default": "", + "help": "List of external plugins URLs (direct download to .zip file) to download and install (URLs are separated with space).", + "id": "external-plugin-urls", + "label": "External plugin URLs", + "regex": "^.*$", + "type": "text" } } } diff --git a/docs/plugins.md b/docs/plugins.md index bd99bb32..be746ce4 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -11,10 +11,19 @@ Here is the list of "official" plugins that we maintain (see the [bunkerweb-plug | **ClamAV** | 0.1 | Automatically scans uploaded files with the ClamAV antivirus engine and denies the request when a file is detected as malicious. | [bunkerweb-plugins/clamav](https://github.com/bunkerity/bunkerweb-plugins/tree/main/clamav) | | **CrowdSec** | 0.1 | CrowdSec bouncer for BunkerWeb. | [bunkerweb-plugins/crowdsec](https://github.com/bunkerity/bunkerweb-plugins/tree/main/crowdsec) | | **Discord** | 0.1 | Send security notifications to a Discord channel using a Webhook. | [bunkerweb-plugins/discord](https://github.com/bunkerity/bunkerweb-plugins/tree/main/discord) | +| **Slack** | 0.1 | Send security notifications to a Slack channel using a Webhook. | [bunkerweb-plugins/slack](https://github.com/bunkerity/bunkerweb-plugins/tree/main/slack) | | **VirusTotal** | 0.1 | Automatically scans uploaded files with the VirusTotal API and denies the request when a file is detected as malicious. | [bunkerweb-plugins/virustotal](https://github.com/bunkerity/bunkerweb-plugins/tree/main/virustotal) | ## How to use a plugin +### Automatic + +If you want to quickly install external plugins, you can use the `EXTERNAL_PLUGIN_URLS` setting. It takes a list of URLs, separated with space, pointing to compressed (zip format) archives containing one or more plugin(s). + +Just use the following value if you want to automatically install the official plugins : `EXTERNAL_PLUGIN_URLS=https://github.com/bunkerity/bunkerweb-plugins/archive/refs/tags/v0.2.zip` + +### Manual + The first step is to install the plugin by putting the plugin files inside the corresponding `plugins` data folder, the procedure depends on your integration : === "Docker"