diff --git a/README.md b/README.md index 0890bcb..788c41a 100644 --- a/README.md +++ b/README.md @@ -6,23 +6,24 @@ This mod adds a dashboard to SWAG powered by [Goaccess](https://goaccess.io/). # Enable -In the container's docker arguments, set an environment variable DOCKER_MODS=linuxserver/mods:swag-dashboard +In the container's docker arguments, set an environment variable `DOCKER_MODS=linuxserver/mods:swag-dashboard`. -If adding multiple mods, enter them in an array separated by |, such as DOCKER_MODS=linuxserver/mods:swag-dashboard|linuxserver/mods:swag-mod2 +If adding multiple mods, enter them in an array separated by `|`, such as `DOCKER_MODS=linuxserver/mods:swag-dashboard|linuxserver/mods:swag-mod2`. ## Internal access using `:81` -Add a mapping of `81:81` to swag's docker run command or compose +Add a mapping of `81:81` to swag's docker run command or compose. ## Internal access using `dashboard.domain.com` -Requires an internal DNS, add a rewrite of `dashboard.domain.com` to your server's IP address +Requires an internal DNS, add a rewrite of `dashboard.domain.com` to your server's IP address. ## External access using `dashboard.domain.com` Remove the allow/deny lines in `/config/nginx/proxy-confs/dashboard.subdomain.com`, and instead secure it some other way (like Authelia for example). -## Notes +## Usage + - The application discovery scans for a list of known services, as well as enabled custom proxy confs that contain the following format: ```yaml set $upstream_app ; @@ -31,9 +32,15 @@ Remove the allow/deny lines in `/config/nginx/proxy-confs/dashboard.subdomain.co proxy_pass $upstream_proto://$upstream_app:$upstream_port; ``` - Either [Swag Maxmind mod](https://github.com/linuxserver/docker-mods/tree/swag-maxmind) or [Swag DBIP mod](https://github.com/linuxserver/docker-mods/tree/swag-dbip) are required to enable the geo location graph. -- The host's fail2ban can be supported by mounting it to swag `- /path/to/host/fail2ban.sqlite3:/dashboard/fail2ban.sqlite3:ro` -- The host's logs can be supported by mounting it to swag `- /path/to/host/logs:/dashboard/logs:ro` - To clear the dashboard stats, you must remove the logs (/config/log/nginx) and **recreate** the container. +## Dashboard Support + +There's a stats endpoint for integration with dashboards under `https://dashboard.domain.com/?stats=true`. + +## External Support +- External fail2ban (not required when using swag's fail2ban) can be supported by mounting it to swag `- /path/to/host/fail2ban.sqlite3:/dashboard/fail2ban.sqlite3:ro`. +- External logs (not required when using swag's logs) can be supported by mounting it to swag `- /path/to/host/logs:/dashboard/logs:ro`. + # Example ![Example](.assets/example.png) diff --git a/root/dashboard/swag-proxies.py b/root/dashboard/swag-proxies.py index 0e6182d..1b2abc8 100644 --- a/root/dashboard/swag-proxies.py +++ b/root/dashboard/swag-proxies.py @@ -5,6 +5,7 @@ import json import os import re import socket +import sys import urllib3 PROXY_REGEX = r"\s+set \$upstream_app (?P\S+?);.*\n(\s+)set \$upstream_port (?P\d+);.*\n(\s+)set \$upstream_proto (?P\w+);.*" @@ -14,14 +15,14 @@ BASIC_AUTH_REGEX = r"\n\s+auth_basic.*" LDAP_REGEX = r"\n\s+include \/config\/nginx\/ldap-location\.conf;.*" -def find_apps(): +def find_apps(fast=False): apps = {} auths = collections.defaultdict(dict) file_paths = glob.glob("/config/nginx/**/**", recursive=True) auto_confs = glob.glob("/etc/nginx/http.d/*", recursive=True) file_paths.extend(auto_confs) for file_path in file_paths: - if not os.path.isfile(file_path): + if not os.path.isfile(file_path) or (fast and file_path.endswith(".sample")): continue file = open(file_path, "r") content = file.read() @@ -67,7 +68,8 @@ def is_available(url): urllib3.disable_warnings() -apps, auths = find_apps() +fast = (len(sys.argv) > 1) +apps, auths = find_apps(fast) discovered_apps = collections.defaultdict(dict) with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor: futures = {executor.submit(is_available, app): app for app in apps.keys()} diff --git a/root/dashboard/www/index.php b/root/dashboard/www/index.php index 810f7ad..0a75049 100644 --- a/root/dashboard/www/index.php +++ b/root/dashboard/www/index.php @@ -310,10 +310,49 @@ HTML; } - $goaccess = GetGoaccess(); - $status = GetHeader() . GetProxies() . GetF2B() . GetTemplates() . GetAnnouncements() . GetLinks() . "
"; - $page = str_replace("
", $status, $goaccess); - $ssl = GetCertificate() . "
"; - $page = str_replace("
", $ssl, $page); - echo $page; + function GetStats() { + $output = shell_exec("if test -f /lsiopy/bin/python3; then /lsiopy/bin/python3 /dashboard/swag-f2b.py; else python3 /dashboard/swag-f2b.py; fi"); + $jails = json_decode($output, true); + $banned = 0; + foreach($jails as $jail){ + $banned = $banned + $jail["bans"]; + } + + $output = shell_exec("if test -f /lsiopy/bin/python3; then /lsiopy/bin/python3 /dashboard/swag-proxies.py fast; else python3 /dashboard/swag-proxies.py fast; fi"); + $results = json_decode($output); + $proxied = 0; + $auth = 0; + foreach($results as $result => $data){ + if (!empty($data->locations)){ + $proxied++; + if ($data->auth_status == 1) { + $auth++; + } + } + } + + $output = shell_exec("/etc/s6-overlay/s6-rc.d/init-version-checks/run"); + $outdated = 0; + foreach(explode(PHP_EOL, $output) as $line) { + if(str_contains($line, "/config/")) { + $outdated++; + } + } + + return array("proxied" => "$proxied", "auth" => "$auth", "outdated" => "$outdated", "banned" => "$banned"); + } + + $stats = $_GET['stats'] == 'true' ? true : false; + if($stats) { + $page = GetStats(); + header("Content-Type: application/json"); + echo json_encode($page); + } else { + $goaccess = GetGoaccess(); + $status = GetHeader() . GetProxies() . GetF2B() . GetTemplates() . GetAnnouncements() . GetLinks() . "
"; + $page = str_replace("
", $status, $goaccess); + $ssl = GetCertificate() . "
"; + $page = str_replace("
", $ssl, $page); + echo $page; + } ?>