mirror of
https://github.com/fosrl/docs-v2.git
synced 2026-10-28 06:29:09 +01:00
port mintlify to fumadocs
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
---
|
||||
title: "CrowdSec"
|
||||
---
|
||||
|
||||
<Note>
|
||||
To install Crowdsec with the offical installer, start it with the `--crowdsec` flag. This will prompt for a Crowdsec install at the end of the process.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
This is a community guide and is not officially supported. If you have any issues, please reach out to the [author](https://github.com/Lokowitz).
|
||||
</Note>
|
||||
|
||||
CrowdSec is a modern, open-source, collaborative behavior detection engine, integrated with a global IP reputation network. It functions as a massively multiplayer firewall, analyzing visitor behavior and responding appropriately to various types of attacks.
|
||||
|
||||
## Installation
|
||||
|
||||
Crowdsec can be installed using the Pangolin Installer.
|
||||
|
||||
<Tip>
|
||||
Enabling CrowdSec turns on Traefik access logging so CrowdSec can analyze traffic. This means `config/traefik/logs/access.log` will grow over time. If you want to set up log rotation, see the [Traefik Access Log Rotation](/self-host/advanced/traefik-log-rotation) guide.
|
||||
</Tip>
|
||||
|
||||
## Configuration
|
||||
|
||||
By default, Crowdsec is installed with a basic configuration, which includes the [Crowdsec Bouncer Traefik plugin](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin).
|
||||
|
||||
### Choose the right logs
|
||||
|
||||
#### Syslog
|
||||
|
||||
For systems utilizing Syslog, the following volumes should be added to the `docker-compose.yml` file:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
crowdsec:
|
||||
volumes:
|
||||
- /var/log/auth.log:/var/log/auth.log:ro
|
||||
- /var/log/syslog:/var/log/syslog:ro
|
||||
```
|
||||
|
||||
Create a `syslog.yaml` file under `/config/crowdsec/acquis.d` with the following content:
|
||||
|
||||
```yaml
|
||||
filenames:
|
||||
- /var/log/auth.log
|
||||
- /var/log/syslog
|
||||
labels:
|
||||
type: syslog
|
||||
```
|
||||
|
||||
#### Journalctl
|
||||
|
||||
To log iptables to journalctl, execute the following command on your host system:
|
||||
|
||||
```bash
|
||||
iptables -A INPUT -j LOG --log-prefix "iptables: "
|
||||
```
|
||||
|
||||
Update the `docker-compose.yml` file as follows:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
crowdsec:
|
||||
image: crowdsecurity/crowdsec:latest-debian
|
||||
environment:
|
||||
COLLECTIONS: crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules crowdsecurity/linux crowdsecurity/iptables
|
||||
volumes:
|
||||
- ./config/crowdsec:/etc/crowdsec
|
||||
- ./config/crowdsec/db:/var/lib/crowdsec/data
|
||||
- ./config/traefik/logs:/var/log/traefik:ro
|
||||
- /var/log/journal:/var/log/host:ro
|
||||
```
|
||||
|
||||
Create a `journalctl.yaml` file under `/config/crowdsec/acquis.d` with the following content:
|
||||
|
||||
```yaml
|
||||
source: journalctl
|
||||
journalctl_filter:
|
||||
- "--directory=/var/log/host/"
|
||||
labels:
|
||||
type: syslog
|
||||
```
|
||||
|
||||
### Securing the Host System (SSH)
|
||||
|
||||
By default, only Traefik requests are secured through the Crowdsec bouncer. To extend protection to your host system (e.g., SSH), follow these steps to add a firewall bouncer:
|
||||
|
||||
1. Install the Crowdsec repositories. Refer to the [installation documentation](https://docs.crowdsec.net/docs/next/getting_started/install_crowdsec/#install-our-repositories):
|
||||
|
||||
```bash
|
||||
curl -s https://install.crowdsec.net | sudo sh
|
||||
```
|
||||
|
||||
2. Install the firewall bouncer. For Debian/Ubuntu systems using IPTables, refer to the [documentation](https://docs.crowdsec.net/u/bouncers/firewall/):
|
||||
|
||||
```bash
|
||||
sudo apt install crowdsec-firewall-bouncer-iptables
|
||||
```
|
||||
|
||||
3. Create an API key for the firewall bouncer to communicate with your CrowdSec Docker container. ("vps-firewall" is a placeholder name for the key):
|
||||
|
||||
```bash
|
||||
docker exec -it crowdsec cscli bouncers add vps-firewall
|
||||
```
|
||||
|
||||
4. Copy the displayed API key and insert it into the bouncer's configuration file:
|
||||
|
||||
```bash
|
||||
nano /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
|
||||
```
|
||||
|
||||
5. Restart the firewall bouncer:
|
||||
|
||||
```bash
|
||||
systemctl restart crowdsec-firewall-bouncer
|
||||
```
|
||||
|
||||
6. Update the `docker-compose.yml` file to expose communication port `8080` for the CrowdSec container and restart the container:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
crowdsec:
|
||||
ports:
|
||||
- 6060:6060 # Metrics port
|
||||
- 8080:8080 # Local API port
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Docker’s NAT-based port publishing feature automatically exposes all `ports:` defined in the `docker-compose` file on all network interfaces. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
|
||||
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
|
||||
</Warning>
|
||||
|
||||
7. Verify communication between the firewall bouncer and the CrowdSec container by running:
|
||||
|
||||
```bash
|
||||
docker exec crowdsec cscli metrics
|
||||
```
|
||||
|
||||
The output should look like this:
|
||||
|
||||
```bash
|
||||
+------------------------------------------------------------------+
|
||||
| Local API Bouncers Metrics |
|
||||
+---------------------------+----------------------+--------+------+
|
||||
| Bouncer | Route | Method | Hits |
|
||||
+---------------------------+----------------------+--------+------+
|
||||
| traefik-bouncer | /v1/decisions/stream | HEAD | 2 |
|
||||
| traefik-bouncer@10.0.4.20 | /v1/decisions | GET | 3 |
|
||||
| vps-firewall | /v1/decisions/stream | GET | 84 | <---------
|
||||
+---------------------------+----------------------+--------+------+
|
||||
```
|
||||
|
||||
## Custom Ban Page
|
||||
|
||||
To display a custom ban page to attackers, follow these steps:
|
||||
|
||||
1. Place a `ban.html` page in the `/config/traefik` directory. If you prefer not to create your own, you can download the official example:
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/refs/heads/main/ban.html
|
||||
```
|
||||
|
||||
2. Update the `/config/traefik/dynamic_config.yml` file to include the following:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
middlewares:
|
||||
crowdsec:
|
||||
plugin:
|
||||
crowdsec:
|
||||
banHTMLFilePath: /etc/traefik/ban.html
|
||||
```
|
||||
|
||||
## Custom Captcha Page
|
||||
|
||||
To use a custom captcha page, follow these steps:
|
||||
|
||||
1. Place a `captcha.html` page in the `/config/traefik` directory. If you don't want to create your own, you can download the official example:
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/refs/heads/main/captcha.html
|
||||
```
|
||||
|
||||
2. Update the `/config/traefik/dynamic_config.yml` file with the following configuration, replacing `<SERVICE>` with your captcha provider (MUST BE either `hcaptcha`, `recaptcha`, or `turnstile`), and `<KEY>` with the appropriate site and secret keys:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
middlewares:
|
||||
crowdsec:
|
||||
plugin:
|
||||
crowdsec:
|
||||
captchaHTMLFilePath: /etc/traefik/captcha.html
|
||||
captchaGracePeriodSeconds: 300
|
||||
captchaProvider: <SERVICE>
|
||||
captchaSiteKey: <KEY>
|
||||
captchaSecretKey: <KEY>
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
You can test your configuration by adding a temporary ban or captcha for your IP. The ban will last for one minute.
|
||||
|
||||
To add a ban:
|
||||
|
||||
```bash
|
||||
docker exec crowdsec cscli decisions add --ip <YOUR IP> -d 1m --type ban
|
||||
```
|
||||
|
||||
To trigger a captcha challenge:
|
||||
|
||||
```bash
|
||||
docker exec crowdsec cscli decisions add --ip <YOUR IP> -d 1m --type captcha
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: "GeoBlock"
|
||||
---
|
||||
|
||||
<Note>
|
||||
This is a community guide and is not officially supported. If you have any issues, please reach out to the [author](https://github.com/Lokowitz).
|
||||
</Note>
|
||||
|
||||
<Warning>
|
||||
Legacy guide. Pangolin now supports native geo-blocking. If you previously installed this plugin, follow [Remove GeoBlock Plugin](/self-host/community-guides/remove-geoblock-plugin) before enabling native geo-blocking.
|
||||
</Warning>
|
||||
|
||||
GeoBlock is a Traefik middleware that uses IP-based geolocation to allow or block traffic from specific countries. It helps enhance security and access control by restricting unwanted or potentially harmful connections based on geographic regions.
|
||||
|
||||
## Installation
|
||||
|
||||
To integrate GeoBlock into your Traefik setup, follow the steps below:
|
||||
|
||||
1. Add the following configuration to your `/config/traefik/traefik_config.yml` file:
|
||||
|
||||
```yaml
|
||||
entryPoints:
|
||||
websecure:
|
||||
http:
|
||||
middlewares:
|
||||
- geoblock@file
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
geoblock:
|
||||
moduleName: github.com/PascalMinder/geoblock
|
||||
version: v0.3.2
|
||||
```
|
||||
|
||||
2. Add the following configuration to your `/config/traefik/dynamic_config.yml` file. Setting `blackListMode: false` enables GeoBlock in whitelist mode, allowing only the specified countries. Remember to add the appropriate countries when traveling. A list of country codes can be found in the [documentation](https://github.com/PascalMinder/geoblock#full-plugin-sample-configuration).
|
||||
|
||||
```yaml
|
||||
http:
|
||||
middlewares:
|
||||
geoblock:
|
||||
plugin:
|
||||
geoblock:
|
||||
silentStartUp: false
|
||||
allowLocalRequests: true
|
||||
logLocalRequests: false # change to true to see logs and verify if it is working
|
||||
logAllowedRequests: false # change to true to see logs and verify if it is working
|
||||
logApiRequests: false # change to true to see logs and verify if it is working
|
||||
api: "https://get.geojs.io/v1/ip/country/{ip}"
|
||||
apiTimeoutMs: 500
|
||||
cacheSize: 25
|
||||
forceMonthlyUpdate: true
|
||||
allowUnknownCountries: false
|
||||
unknownCountryApiResponse: "nil"
|
||||
blackListMode: false
|
||||
countries:
|
||||
- DE # add/replace with your country code
|
||||
```
|
||||
|
||||
3. Restart Traefik to apply the changes:
|
||||
|
||||
```bash
|
||||
docker restart traefik
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
To monitor GeoBlock activities in the Traefik logs, enable logging by setting the following options to `true`:
|
||||
|
||||
```yaml
|
||||
logLocalRequests: true
|
||||
logAllowedRequests: true
|
||||
logApiRequests: true
|
||||
```
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: "GeoLite2 Automation"
|
||||
description: "A simple automation to download & update your GeoLite2 databases with geoipupdate"
|
||||
---
|
||||
|
||||
<Note>
|
||||
This is a community guide and is not officially supported. If you have any issues, please reach out to the [author](https://github.com/txwgnd).
|
||||
</Note>
|
||||
|
||||
This automation lets your system automatically download and update the `GeoLite2-Country` and `GeoLite2-ASN` databases from MaxMind. Pangolin uses these databases for geo-location features such as country or region rules, geo-blocking, analytics, and ASN blocking. It uses MaxMind's [geoipupdate](https://github.com/maxmind/geoipupdate/tree/main) Docker container to do this.
|
||||
|
||||
Maxmind's service is free of charge for development, personal or community use. [Quote](https://support.maxmind.com/knowledge-base/articles/create-a-maxmind-account#h_01G4G4NG5C63BQ6HRG6MSS50T3)
|
||||
|
||||
# Table of Contents
|
||||
1. **[Requirements](#1-requirements)**
|
||||
2. **[Maxmind Account](#2-maxmind-account)**
|
||||
3. **[API key creation](#3-api-key-creation)**
|
||||
4. **[Modification of Pangolin's `docker-compose.yml`](#4-modification-of-pangolins-docker-compose-yml)**
|
||||
5. **[Modification of Pangolin's `config.yml`](#5-modification-of-pangolins-config-yml)**
|
||||
|
||||
## 1. Requirements
|
||||
* A Maxmind account for API access
|
||||
* Pangolin version 1.11.0 or higher
|
||||
|
||||
## 2. Maxmind Account
|
||||
To be able to use Maxmind's service you need to request access to the GeoLite2 databases and create an account on their [website](https://www.maxmind.com/en/geolite2/signup?utm_source=kb&utm_medium=kb-link&utm_campaign=kb-create-account).
|
||||
|
||||
After you successfully created an account visit the mainpage again and login to your new account.
|
||||
|
||||
## 3. API key creation
|
||||
The next step is to create an API key for `geoipupdate`. You'll find an entry called `Manage license keys` in the menu on the left side. Head to this page and click on `Generate new license key`.
|
||||
|
||||
<Frame caption="Maxmind's Manage license keys page">
|
||||
<img src="/images/maxmind_manage-license-keys.jpeg" alt="Maxmind's Manage license keys page" />
|
||||
</Frame>
|
||||
|
||||
Give your new key a name. E.g. `Pangolin`.
|
||||
|
||||
<Frame caption="Choose a name for the key">
|
||||
<img src="/images/maxmind_create-key-page.jpeg" alt="Maxmind's key creation page" />
|
||||
</Frame>
|
||||
|
||||
After your key got created the webpage will show you your Account ID as well as the API key. Save the key now because it can only be seen once. Don't panic if something goes wrong, you can easily create new keys.
|
||||
|
||||
<Frame caption="Key successfully created">
|
||||
<img src="/images/maxmind_key-created.jpeg" alt="The key got created successfully" />
|
||||
</Frame>
|
||||
|
||||
After you clicked on `Return to list` you should see an overview of your keys bundled with some metadata.
|
||||
|
||||
## 4. Modification of Pangolin's `docker-compose.yml`
|
||||
Now login to your Pangolin host and navigate to `/pangolin` in your user directory:
|
||||
```bash
|
||||
cd pangolin
|
||||
```
|
||||
Shut down Pangolin with:
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
Open `docker-compose.yml` with your favorite text editor.
|
||||
E.g. nano:
|
||||
```bash
|
||||
nano docker-compose.yml
|
||||
```
|
||||
|
||||
Append this Docker compose service at the end of your stack and add your Account ID as well as your API key you created in the last step:
|
||||
```yaml
|
||||
services:
|
||||
(...)
|
||||
geoipupdate:
|
||||
container_name: geoipupdate
|
||||
image: ghcr.io/maxmind/geoipupdate
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- 'GEOIPUPDATE_ACCOUNT_ID=' # Account ID
|
||||
- 'GEOIPUPDATE_LICENSE_KEY=' # API key
|
||||
- 'GEOIPUPDATE_EDITION_IDS=GeoLite2-Country GeoLite2-ASN' # Which dbs should be downloaded
|
||||
- 'GEOIPUPDATE_FREQUENCY=72' # Update intervall in hours
|
||||
volumes:
|
||||
- './config/GeoLite2:/usr/share/GeoIP'
|
||||
```
|
||||
#### Note
|
||||
If you use the standard Pangolin deployment you shouldn't need to modify the path.
|
||||
This is the bare minimum to run the container. There are other optional environment variables available. Have a look at their [docs](https://dev.maxmind.com/geoip/updating-databases/?lang=en)!
|
||||
|
||||
Save and close the file, but don't restart the stack yet!
|
||||
|
||||
## 5. Modification of Pangolin's config.yml
|
||||
Navigate to `/config` within the same folder and open it with a text editor.
|
||||
```bash
|
||||
cd config
|
||||
```
|
||||
|
||||
Add these lines to the `server` object
|
||||
|
||||
```yaml
|
||||
server:
|
||||
maxmind_db_path: "./config/GeoLite2/GeoLite2-Country.mmdb"
|
||||
maxmind_asn_path: "./config/GeoLite2/GeoLite2-ASN.mmdb"
|
||||
```
|
||||
These entries tell the Pangolin application where to find the databases.
|
||||
|
||||
Save and close the file then navigate to the `pangolin` folder one level higher.
|
||||
|
||||
Restart your Pangolin stack with:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Et voilà, you are now able to define country rules and ASN rules for your resources! 🏁
|
||||
|
||||
btw: you can use these exact databases for your Traefik dashboard too -> [Community Guide](/self-host/community-guides/traefiklogsdashboard)
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
title: "Home Assistant Add-on"
|
||||
---
|
||||
|
||||
<Note>
|
||||
This is a community add-on and is not officially supported. If you have any issues, please reach out to the [author](https://github.com/Ferdinand99/home-assistant-newt-addon).
|
||||
</Note>
|
||||
|
||||
This Home Assistant add-on allows you to easily run **Newt** directly in Home Assistant. The add-on lets you configure **PANGOLIN_ENDPOINT**, **NEWT_ID**, and **NEWT_SECRET** via the Home Assistant interface.
|
||||
|
||||
## Features
|
||||
|
||||
- Easy installation via Home Assistant Add-on Store
|
||||
- Automated setup and execution of the Newt container
|
||||
- Supports `amd64`, `armv7`, `armhf`, and `aarch64` architectures
|
||||
- Automatic restart on crash
|
||||
|
||||
## Installation
|
||||
|
||||
### **1. Add the GitHub Repository as an Add-on Source**
|
||||
|
||||
- Go to **Settings → Add-ons → Add-on Store**.
|
||||
- Click the menu (three dots in the top right) and select **Repositories**.
|
||||
- Add the following URL:
|
||||
```
|
||||
https://github.com/Ferdinand99/home-assistant-newt-addon
|
||||
```
|
||||
or
|
||||
```
|
||||
https://git.opland.net/Ferdinand99/home-assistant-newt-addon/
|
||||
```
|
||||
|
||||
1. Click **Add** and wait for the repository to load.
|
||||
|
||||
### **2. Install and Start the Add-on**
|
||||
|
||||
1. Find **Newt Add-on** in the list and click **Install**.
|
||||
2. Go to the **Configuration** tab and enter your values for:
|
||||
- **PANGOLIN_ENDPOINT** (e.g., `https://example.com`)
|
||||
- **NEWT_ID**
|
||||
- **NEWT_SECRET**
|
||||
3. Click **Save** and then **Start**.
|
||||
4. Check the **Logs** tab to verify that everything is running correctly.
|
||||
|
||||
## **Configuration**
|
||||
|
||||
After installation, you can configure the add-on via the Home Assistant UI:
|
||||
|
||||
```yaml
|
||||
PANGOLIN_ENDPOINT: "https://example.com"
|
||||
NEWT_ID: "your_newt_id"
|
||||
NEWT_SECRET: "your_newt_secret"
|
||||
```
|
||||
|
||||
### **Docker Environment Variables**
|
||||
|
||||
The following environment variables are passed to the `Newt` container:
|
||||
|
||||
- `PANGOLIN_ENDPOINT`
|
||||
- `NEWT_ID`
|
||||
- `NEWT_SECRET`
|
||||
|
||||
## Exposing Home Assistant through addon
|
||||
1. Connect addon to your Pangolin by completing environment variables and starting the addon
|
||||
2. In Pangolin create new HTTP resource for your new Tunnel with subdomain
|
||||
3. Within the created Resource add new Target Configuration
|
||||
|
||||
| Method | IP / Hostname | Port |
|
||||
| --- | ----------- | --- |
|
||||
| HTTP | 127.0.0.1 | 8123 |
|
||||
|
||||
4. In Home Assistant's `configuration.yaml` add these two sections:
|
||||
```yaml
|
||||
http:
|
||||
use_x_forwarded_for: true
|
||||
trusted_proxies:
|
||||
- 127.0.0.1
|
||||
homeassistant:
|
||||
allowlist_external_urls:
|
||||
- "https://<subdomain>.example.com" # <-- Replace with URL of created resource in Pangolin
|
||||
```
|
||||
|
||||
4.5: If you want to use SSO Authentication in Pangolin you need to set up the `configuration.yaml` like this:
|
||||
```
|
||||
http:
|
||||
cors_allowed_origins:
|
||||
- https://google.com
|
||||
- https://www.home-assistant.io
|
||||
ip_ban_enabled: true
|
||||
login_attempts_threshold: 2
|
||||
use_x_forwarded_for: true
|
||||
trusted_proxies:
|
||||
- 127.0.0.1
|
||||
- Local IP of your NEWT instance
|
||||
- VPS IP
|
||||
```
|
||||
|
||||
You also need to set up `Resource rules` in the pangolin dashboard. [See rule overview here](/manage/access-control/rules).
|
||||
|
||||
Many thanks to steuerlexi for finding this out!
|
||||
|
||||
https://github.com/fosrl/pangolin/issues/757#issuecomment-2903774897
|
||||
|
||||
<Note>
|
||||
Please see [http](https://www.home-assistant.io/integrations/http/) documentation and [allowlist_external_urls](https://www.home-assistant.io/integrations/homeassistant/#external_url) on Home Assistant site.
|
||||
</Note>
|
||||
|
||||
5. Restart Home Assistant and your new Pangolin Proxy should be alive
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
#### **Add-on does not start?**
|
||||
|
||||
- Check the logs in Home Assistant (`Settings → Add-ons → Newt → Logs`).
|
||||
- Ensure that `PANGOLIN_ENDPOINT`, `NEWT_ID`, and `NEWT_SECRET` are set correctly.
|
||||
|
||||
#### **Changes in configuration do not take effect?**
|
||||
|
||||
- Restart the add-on after making changes.
|
||||
- Try removing the container manually:
|
||||
|
||||
```shell
|
||||
docker stop newt
|
||||
docker rm newt
|
||||
```
|
||||
|
||||
- Then start the add-on again.
|
||||
|
||||
#### **Docker not available?**
|
||||
|
||||
- Home Assistant OS manages Docker automatically, but check if the system has access to Docker by running:
|
||||
```shell
|
||||
docker info
|
||||
```
|
||||
|
||||
If this fails, there may be a restriction in Home Assistant OS.
|
||||
|
||||
## Useful Links
|
||||
|
||||
- [HA addon repo](https://github.com/Ferdinand99/home-assistant-newt-addon)
|
||||
- [Home Assistant](https://www.home-assistant.io/)
|
||||
- [Docker Docs](https://docs.docker.com/)
|
||||
@@ -0,0 +1,200 @@
|
||||
---
|
||||
title: "Metrics"
|
||||
---
|
||||
|
||||
<Note>
|
||||
This is a community guide and is not officially supported. If you have any issues, please reach out to the [author](https://github.com/Lokowitz).
|
||||
</Note>
|
||||
|
||||
This is a basic example of collecting metrics from Traefik and CrowdSec using Prometheus and visualizing them with Grafana dashboards.
|
||||
|
||||
<Warning>
|
||||
Important for users with low-powered server (1GB RAM):
|
||||
This setup will increase the use of your server RAM.
|
||||
</Warning>
|
||||
|
||||
## Configuration
|
||||
|
||||
### Traefik
|
||||
|
||||
For claiming metrics from Traefik we have to adjust some configuration files.
|
||||
|
||||
1. Update the `docker-compose.yml` file of the Pangolin stack to expose metrics port `8082` for the Prometheus connection:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
gerbil:
|
||||
ports:
|
||||
- 8082:8082
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Docker’s NAT-based port publishing feature automatically exposes all `ports:` defined in `docker-compose` file. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
|
||||
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
|
||||
</Warning>
|
||||
|
||||
2. Update the `/config/traefik/traefik_config.yml` file to include the following:
|
||||
|
||||
```yaml
|
||||
entryPoints:
|
||||
metrics:
|
||||
address: ":8082"
|
||||
|
||||
metrics:
|
||||
prometheus:
|
||||
buckets:
|
||||
- 0.1
|
||||
- 0.3
|
||||
- 1.2
|
||||
- 5.0
|
||||
entryPoint: metrics
|
||||
addEntryPointsLabels: true
|
||||
addRoutersLabels: true
|
||||
addServicesLabels: true
|
||||
```
|
||||
|
||||
3. Restart the Gerbil and Traefik container to apply the changes:
|
||||
|
||||
```bash
|
||||
sudo docker restart traefik gerbil
|
||||
```
|
||||
|
||||
### Crowdsec
|
||||
|
||||
For claiming metrics from Crowdsec we have to adjust the docker compose files.
|
||||
|
||||
1. Update the `docker-compose.yml` file of the Pangolin stack to expose metrics port `6060` for the Prometheus connection:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
crowdsec:
|
||||
ports:
|
||||
- 6060:6060
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Docker’s NAT-based port publishing feature automatically exposes all `ports:` defined in the `docker-compose` file on all network interfaces. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
|
||||
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
|
||||
</Warning>
|
||||
|
||||
|
||||
2. Restart the Crowdsec container to apply the changes:
|
||||
|
||||
```bash
|
||||
sudo docker restart crowdsec
|
||||
```
|
||||
|
||||
## Prometheus
|
||||
|
||||
1. Create a new Prometheus container or add it to `docker-compose.yml` of Pangolin stack:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
prometheus:
|
||||
container_name: prometheus
|
||||
image: prom/prometheus:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 9090:9090
|
||||
volumes:
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./config/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- ./config/prometheus/data:/prometheus
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Docker’s NAT-based port publishing feature automatically exposes all `ports:` defined in the `docker-compose` file on all network interfaces. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
|
||||
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
|
||||
</Warning>
|
||||
|
||||
|
||||
2. Create a `prometheus.yml` file in the `/config/prometheus` directory with the following content:
|
||||
|
||||
```yaml
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "prometheus"
|
||||
static_configs:
|
||||
- targets: ["localhost:9090"]
|
||||
|
||||
- job_name: traefik
|
||||
static_configs:
|
||||
- targets: ["172.17.0.1:8082"]
|
||||
|
||||
- job_name: crowdsec
|
||||
static_configs:
|
||||
- targets: ["172.17.0.1:6060"]
|
||||
```
|
||||
|
||||
3. Create a folder `data` in `/config/prometheus` and change the owner and owning group:
|
||||
|
||||
```bash
|
||||
chown nobody:nogroup data
|
||||
```
|
||||
|
||||
4. Start the Prometheus container:
|
||||
|
||||
```bash
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
## Grafana
|
||||
|
||||
1. Create a new Grafana container or add it to `docker-compose.yml` of Pangolin stack:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
container_name: grafana
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./config/grafana/data:/var/lib/grafana
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Docker’s NAT-based port publishing feature automatically exposes all `ports:` defined in the `docker-compose` file on all network interfaces. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
|
||||
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
|
||||
</Warning>
|
||||
|
||||
2. Start the Grafana container:
|
||||
|
||||
```bash
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
<Note>
|
||||
Default login credentials for Grafana admin user is admin:admin.
|
||||
</Note>
|
||||
|
||||
### Add Prometheus Connection
|
||||
|
||||
Add the Prometheus connection under Connections -> Add new connection.
|
||||
|
||||
Set `http://172.17.0.1:9090` as `Prometheus Server URL` and click `Save & test`.
|
||||
|
||||
### Add Dashboard
|
||||
|
||||
Add a Dashboard under Dashboard -> New -> Import and import a pre configured Dashboard or create your own.
|
||||
|
||||
#### Traefik
|
||||
|
||||
<Frame caption="Traefik Dashboard">
|
||||
<img src="/images/traefik_dashboard.png" alt="Traefik Dashboard"/>
|
||||
</Frame>
|
||||
|
||||
Template Import ID = 17346
|
||||
|
||||
https://grafana.com/grafana/dashboards/17346-traefik-official-standalone-dashboard/
|
||||
|
||||
#### Crowdsec
|
||||
|
||||
https://github.com/crowdsecurity/grafana-dashboards/tree/master
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
title: "Middleware Manager"
|
||||
---
|
||||
|
||||
<Note>
|
||||
This is a community guide and not officially supported. For issues, contributions, or bug reports, please use the [official GitHub repository](https://github.com/hhftechnology/middleware-manager).
|
||||
</Note>
|
||||
|
||||
## What is Middleware Manager?
|
||||
|
||||
The **Middleware Manager** is a microservice that extends your existing traefik deployments.
|
||||
It provides a **web UI** to attach Traefik middlewares to resources without editing Pangolin itself.
|
||||
|
||||
#### Security Warning
|
||||
|
||||
Middlewares can strengthen security but also create vulnerabilities if misconfigured.
|
||||
* Test in staging before production.
|
||||
* Misusing forward authentication can leak credentials.
|
||||
* Bad rate limiter configs may be bypassed.
|
||||
* Header misconfigurations can expose apps to XSS/CSRF.
|
||||
* Stacking too many middlewares impacts performance.
|
||||
* Always check provider references (`@http` vs `@file`).
|
||||
|
||||
---
|
||||
|
||||
### Key Use Cases
|
||||
* External authentication (Authelia, Authentik, JWT)
|
||||
* Security headers and CSP policies
|
||||
* Geographic IP blocking
|
||||
* Rate limiting / DDoS protection
|
||||
* Redirects & path rewrites
|
||||
* CrowdSec and other security tool integrations
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
* A running **Pangolin v1.0.0+**
|
||||
* Docker + Docker Compose
|
||||
* Basic Traefik knowledge
|
||||
* Admin access to your Pangolin host
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Add Middleware Manager Service
|
||||
|
||||
Update your `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
middleware-manager:
|
||||
image: hhftechnology/middleware-manager:latest
|
||||
container_name: middleware-manager
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./config/traefik/rules:/conf
|
||||
- ./config/middleware-manager/templates.yaml:/app/config/templates.yaml # Optional custom templates
|
||||
environment:
|
||||
- PANGOLIN_API_URL=http://pangolin:3001/api/v1
|
||||
- TRAEFIK_CONF_DIR=/conf
|
||||
- DB_PATH=/data/middleware.db
|
||||
- PORT=3456
|
||||
ports:
|
||||
- "3456:3456"
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Create Required Directories
|
||||
|
||||
```bash
|
||||
mkdir -p ./config/traefik/rules
|
||||
mkdir -p ./config/middleware-manager
|
||||
```
|
||||
|
||||
Move any dynamic configs into `./config/traefik/rules`.
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Update Traefik Volumes & Providers
|
||||
|
||||
In your `traefik` service:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./config/traefik:/etc/traefik:ro
|
||||
- ./config/letsencrypt:/letsencrypt
|
||||
- ./config/traefik/logs:/var/log/traefik
|
||||
- ./config/traefik/rules:/rules # required
|
||||
```
|
||||
|
||||
In `traefik_config.yml`:
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
file:
|
||||
directory: "/rules"
|
||||
watch: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Step 4: Start Services
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Access the UI
|
||||
|
||||
Middleware Manager runs at:
|
||||
👉 [http://localhost:3456](http://localhost:3456)
|
||||
|
||||
---
|
||||
|
||||
## Common Middleware Examples
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```yaml
|
||||
middlewares:
|
||||
- id: "rate-limit"
|
||||
type: "rateLimit"
|
||||
config:
|
||||
average: 100
|
||||
burst: 50
|
||||
```
|
||||
|
||||
### Security Headers
|
||||
|
||||
```yaml
|
||||
middlewares:
|
||||
- id: "security-headers"
|
||||
type: "headers"
|
||||
config:
|
||||
customResponseHeaders:
|
||||
Server: ""
|
||||
X-Powered-By: ""
|
||||
browserXSSFilter: true
|
||||
contentTypeNosniff: true
|
||||
forceSTSHeader: true
|
||||
stsSeconds: 63072000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* **Service does not exist** → Check `@http` or `@file` suffix in references
|
||||
* **Middleware does not exist** → Verify config and required plugins
|
||||
* **No changes applied** → Check Traefik logs, middleware priority, restart services
|
||||
* **UI not showing resources** → Confirm `PANGOLIN_API_URL` and network connectivity
|
||||
* **Database errors** → Check `./data` permissions, or reset `middleware.db`
|
||||
* **CrowdSec errors → Ensure the crowdsec container is running; middlewares fail if the service is down.
|
||||
* **Protecting Pangolin itself** → Apply middlewares (e.g. geoblock, headers) directly on the websecure entryPoint to cover all traffic.
|
||||
* **Applying to many services** → Attach middleware to entryPoints instead of individual resources to cover all subdomains at once.
|
||||
* **TCP / SMTP with STARTTLS** → Not supported. Traefik cannot handle STARTTLS negotiation (only implicit TLS like SMTPS on 465).
|
||||
|
||||
---
|
||||
|
||||
## Final Notes
|
||||
|
||||
The Middleware Manager gives you a UI to work with Traefik’s powerful middleware ecosystem.
|
||||
|
||||
* Start with simple configs → test thoroughly → expand gradually.
|
||||
* Use templates where possible.
|
||||
* Always validate in staging before production.
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: "Overview"
|
||||
---
|
||||
|
||||
<Note>
|
||||
These are community written guides and are not officially supported. If you have any issues, please reach out to the authors or the community on [Discord](https://pangolin.net/discord) or [Github discussions](https://github.com/orgs/fosrl/discussions).
|
||||
</Note>
|
||||
|
||||
The modular design of this system enables the extension of its functionality through the integration of existing Traefik plugins, such as Crowdsec and Geoblock.
|
||||
Additionally, Prometheus can collect metrics from both CrowdSec and Traefik, which can then be visualized in Grafana to monitor security events, request statistics, and traffic patterns in real time.
|
||||
|
||||
## Traefik plugins
|
||||
|
||||
For a complete list of available plugins, please refer to the [Plugin Catalog](https://plugins.traefik.io/plugins).
|
||||
|
||||
### Crowdsec Bouncer
|
||||
|
||||
When installing Crowdsec via the Pangolin installer, the Crowdsec Traefik Bouncer will be automatically installed and configured by default. The configuration can be customized to meet your specific requirements.
|
||||
|
||||
The CrowdSec Bouncer plugin for Traefik integrates CrowdSec’s security engine to block malicious traffic in real time. It runs as middleware within a Traefik container and enforces decisions based on CrowdSec’s threat intelligence. This helps protect services from bots, attackers, and abusive IPs dynamically.
|
||||
|
||||
For additional information, consult the following resources:
|
||||
|
||||
- [Traefik Plugin Catalog](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin)
|
||||
- [Github Repository](https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin)
|
||||
|
||||
### Geoblock
|
||||
|
||||
The GeoBlock plugin for Traefik is a middleware that restricts access based on the client’s geographic location. It runs within a Traefik container and uses IP-based geolocation to allow or block traffic from specific countries. This is useful for security, compliance, or access control in Traefik-managed services.
|
||||
|
||||
<Note>
|
||||
Pangolin now supports native geo-blocking. The GeoBlock plugin is considered legacy in Pangolin setups. If you previously installed it, follow [Remove GeoBlock Plugin](/self-host/community-guides/remove-geoblock-plugin) before enabling native geo-blocking.
|
||||
</Note>
|
||||
|
||||
For more details, please refer to the following resources:
|
||||
|
||||
- [Github Repository](https://github.com/PascalMinder/geoblock)
|
||||
|
||||
### Middleware Manager
|
||||
|
||||
The Middlware manager is a microservice that allows you to add custom middleware to Pangolin / Traefik resources.
|
||||
|
||||
For more details, please refer to the following resources:
|
||||
|
||||
- [Github Repository](https://github.com/hhftechnology/middleware-manager)
|
||||
|
||||
## Metrics
|
||||
|
||||
Currently you can claim metric data from Traefik and Crowdsec with Prometheus and visualize it within a Grafana Dashboard.
|
||||
|
||||
### Prometheus
|
||||
|
||||
Prometheus is an open-source monitoring and alerting toolkit designed for collecting and querying time-series metrics. It runs as a Docker container and uses a pull-based model to scrape data from configured endpoints. Prometheus integrates well with Grafana for visualization and Alertmanager for alert handling.
|
||||
|
||||
For more details, please refer to the following resources:
|
||||
|
||||
- [Homepage](https://prometheus.io/)
|
||||
- [Github Repository](https://github.com/prometheus/prometheus)
|
||||
|
||||
### Grafana
|
||||
|
||||
Grafana is an open-source analytics and visualization platform used to monitor and display time-series data. It runs as a Docker container and supports multiple data sources, including Prometheus, InfluxDB, and MySQL. Grafana provides interactive dashboards, alerting, and extensive customization options for data visualization.
|
||||
|
||||
For more details, please refer to the following resources:
|
||||
|
||||
- [Homepage](https://grafana.com/)
|
||||
- [Github Repository](https://github.com/grafana/grafana)
|
||||
|
||||
### Traefik Logs Dashboard
|
||||
|
||||
The Traefik Logs Dashboard is a real-time dashboard for analyzing Traefik logs with IP geolocation, status code analysis, and service metrics.
|
||||
|
||||
For more details, please refer to the following resources:
|
||||
|
||||
- [Github Repository](https://github.com/hhftechnology/traefik-log-dashboard)
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: "Remove GeoBlock Plugin"
|
||||
---
|
||||
|
||||
<Note>
|
||||
This is a community guide and is not officially supported. If you have any issues, please reach out to the community on [Discord](https://pangolin.net/discord) or [Github discussions](https://github.com/orgs/fosrl/discussions).
|
||||
</Note>
|
||||
|
||||
Pangolin now supports native geo-blocking. If you previously installed the Traefik GeoBlock plugin, remove it before enabling native geo-blocking to avoid duplicate blocking or startup errors.
|
||||
|
||||
<Note>
|
||||
After cleanup, follow [Enable Geo-location](/self-host/advanced/enable-geolocation) to configure the geo-location database used by native geo-blocking in Pangolin.
|
||||
</Note>
|
||||
|
||||
## Remove the GeoBlock plugin
|
||||
|
||||
<Steps>
|
||||
<Step title="Remove GeoBlock middleware references">
|
||||
Remove any references to `geoblock@file` from your Traefik entry points, routers, or labels.
|
||||
|
||||
Example removal in `/config/traefik/traefik_config.yml`:
|
||||
|
||||
```yaml
|
||||
entryPoints:
|
||||
websecure:
|
||||
http:
|
||||
middlewares:
|
||||
# Remove this line
|
||||
- geoblock@file
|
||||
```
|
||||
</Step>
|
||||
<Step title="Remove the plugin definition from Traefik static config">
|
||||
Delete the GeoBlock plugin block from `/config/traefik/traefik_config.yml`:
|
||||
|
||||
```yaml
|
||||
experimental:
|
||||
plugins:
|
||||
geoblock:
|
||||
moduleName: github.com/PascalMinder/geoblock
|
||||
version: v0.3.2
|
||||
```
|
||||
</Step>
|
||||
<Step title="Remove the middleware configuration from dynamic config">
|
||||
Delete the GeoBlock middleware section from `/config/traefik/dynamic_config.yml`:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
middlewares:
|
||||
geoblock:
|
||||
plugin:
|
||||
geoblock:
|
||||
...
|
||||
```
|
||||
</Step>
|
||||
<Step title="Restart Traefik">
|
||||
Restart Traefik to apply the changes:
|
||||
|
||||
```bash
|
||||
docker restart traefik
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Next steps
|
||||
|
||||
Follow [Enable Geo-location](/self-host/advanced/enable-geolocation) to configure the geo-location database used by native geo-blocking in Pangolin.
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "Bypass Rules"
|
||||
description: "Community bypass rules for common self hosted apps"
|
||||
---
|
||||
|
||||
This table compiles paths that need to be allowed for various apps to work with Pangolin authentication.
|
||||
|
||||
| App | Required Bypass Rules |
|
||||
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Media Management** | |
|
||||
| Radarr | `/api/*` |
|
||||
| Sonarr | `/api/*` |
|
||||
| Lidarr | `/api/*` |
|
||||
| **Media Servers** | |
|
||||
| Jellyfin (iOS) | `/system/info/public` |
|
||||
| Jellyfin (Roku) | `/System/Info/Public`<br />`/Users/AuthenticateByName`<br />`/Users/Public`<br />`/QuickConnect/Initiate`<br />`/QuickConnect/Connect`<br />`/Users/AuthenticateWithQuickConnect` |
|
||||
| Audiobookshelf | Audiobookshelf also supports `/audiobookshelf` by default. Each rule should also be applied to this path.<br />`/api/*`<br />`/login`<br />`/auth/*`<br />`/feed/*`<br />`/socket.io/`<br />`/status`<br />`/logout`<br />`/ping`<br />`/public/*`<br />The following is needed for public shares and is optional for clients:<br />`/share/*`<br />`/_nuxt/*.js`<br />`/_nuxt/fonts/*` |
|
||||
| **Management & Monitoring** | |
|
||||
| Tautulli | `/api/*` |
|
||||
| Harbour | `/api/*` |
|
||||
| Hoarder App | `/api/*` |
|
||||
| Uptime Kuma Manager | `/api/*`<br />`/socket.io/*` |
|
||||
| Beszel | `/api/beszel/agent-connect` |
|
||||
| MeshCentral | `/api/*`<br />`/meshrelay.ashx`<br />`/agent.ashx` |
|
||||
| **Security & Privacy** | |
|
||||
| AdGuard Home | `/api/*` |
|
||||
| Ente Auth | `*api*` |
|
||||
| Vaultwarden/Bitwarden | `/api/*`<br />`/identity/*`<br />`/wl/*`<br /> `/notifications/hub` required for websocket notifications enabled by default since v1.29.0 of Vaultwarden <br /> `/icons/*` For proper loading of Favicons (Optional) <br />Always Deny - Path - `/admin/*` |
|
||||
| **Cloud & Sync** | |
|
||||
| Nextcloud | `/` (Main interface)<br />`/index.php/*` (Core handler)<br />`/remote.php` (Remote access)<br />`/status.php` (Status checks)<br />`/ocs` (Collaboration Services API)<br />`/apps/*` (Applications)<br />`/remote.php/webdav/*` (WebDAV endpoint)<br />`/remote.php/dav/*` (CalDAV/CardDAV)<br />`/remote.php/caldav/*` (Calendar sync)<br />`/remote.php/carddav/*` (Contacts sync)<br />`/ocs/v1.php/*` (API endpoints)<br />`/ocs/v2.php/*` (API v2 endpoints)<br />`/login` (Authentication)<br />`/.well-known/*` (Service discovery)<br />`/.well-known/webfinger` (WebFinger protocol)<br />`/s/*` (Shared files/folders)<br />`/heartbeat` (Session keep-alive)<br />`/ocs-provider/*` (OCS capability discovery)<br />`/ocm-provider/*` (Federated sharing discovery)<br />`/push/*` (notify_push real-time notifications) |
|
||||
| Onlyoffice | `/cache/*`<br />`*/CommandService.ashx`<br />`*/converter/*`<br />`*/doc/*`<br />`*/downloadas/*`<br />`/downloadfile/*`<br />`*/fonts/*`<br />`/healthcheck`<br />`/methodology/*`<br />`*/plugins.json`<br />`*/sdkjs/*`<br />`*/sdkjs-plugins/*`<br />`*/themes.json`<br />`*/web-apps/*` |
|
||||
| **Photo Management** | |
|
||||
| Ente Photos | `*api*` |
|
||||
| Immich | `/api/*`<br />`/.well-known/immich` |
|
||||
| **File Management** | |
|
||||
| Filebrowser | `/static/*`<br />`/share/*` <br/> `/api/public/dl/*` <br/> `/api/public/share/*` |
|
||||
| **Notes & Knowledge Management** | |
|
||||
| Docmost | `/share/*`<br />`/api/*`<br />`/assets/index*/*`<br />`/icons/favicons-*`<br />Always Deny - Path - `/login/*` (optional)
|
||||
| Joplin Notes Server | `/api/*`<br />`/shares/*`<br />`/css/*`<br />`/images/*`<br />Always Deny - Path - `/login/*` (optional) |
|
||||
| Erugo | `/api/*`<br />`/shares/*`<br />`/build/*`<br />`/get-logo` |
|
||||
| Memos | `/api/*`<br />`/assets/*`<br />`/explore*`<br />`/memos.api.v1.*`<br />`/auth/callback*`<br />`/auth`<br />`/site.webmanifest`<br />`/logo.webp`<br />`/full-logo.webp`<br />`/android-chrome-192x192.png` |
|
||||
| Linkding | `/api/*`<br />`/bookmarks/*`<br />Always Deny - Path - `/admin/*` |
|
||||
| **Communication** | |
|
||||
| Matrix/Synapse (Clients) | `/_matrix/*`<br />`/_synapse/client/*` |
|
||||
| Matrix/Synapse (Federation) | `/_matrix/*` |
|
||||
| **Notifications** | |
|
||||
| Gotify | `/version`<br />`/message`<br />`/application`<br />`/client`<br />`/stream`<br />`/plugin`<br />`/health` |
|
||||
| **Home Automation** | |
|
||||
| Home Assistant | `/api/*`<br />`/auth/*`<br />`/frontend_latest/*`<br />`/lovelace/*`<br />`/static/*`<br />`/hacsfiles/*`<br />`/local/*`<br />`/manifest.json`<br />`/sw-modern.js` |
|
||||
| n8n | `/webhook-test/*/webhook`<br />`/webhook/*/webhook` |
|
||||
| **Project Management** | |
|
||||
| Jetbrains Youtrack | `/api/*`<br />`/hub/api/*`<br /> |
|
||||
| **Genealogy** | |
|
||||
| Gramps Web | `/api/*` |
|
||||
| **Analytics** | |
|
||||
| Liwan | `/script.js`<br /> `/api/send` |
|
||||
| Umami | `/script.js`<br /> `/api/send` |
|
||||
|
||||
<Note>
|
||||
These rules are examples and may need to be adjusted based on your specific
|
||||
app configuration and version.
|
||||
</Note>
|
||||
@@ -0,0 +1,214 @@
|
||||
---
|
||||
title: "Securing Traefik Post-Install"
|
||||
description: "Hardening steps after Traefik v3 install—split file provider rules, TLS options, security headers, HTTPS redirects, and EC384 ACME certificates."
|
||||
---
|
||||
|
||||
The steps I would take to secure Traefik and such post install.
|
||||
|
||||
Treat this as a template—adapt entrypoint names, paths, resolvers, and middleware references to your stack. Don't copy-paste the blocks wholesale without checking they match your setup.
|
||||
|
||||
A few I would recommend but this assumes you won't ever be doing http traffic.. (externally)
|
||||
|
||||
```yaml
|
||||
redirections: # Only caring for https based traffic.
|
||||
entryPoint:
|
||||
to: websecure # match your websecure entrypoint name.
|
||||
scheme: https
|
||||
permanent: true
|
||||
```
|
||||
|
||||
Into your traefik_dynamic though I would recommend actually splitting the files and using like,
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
file:
|
||||
directory: "/rules" # Path inside the container; must match the mount target (e.g. ./rules:/rules:ro below).
|
||||
watch: true
|
||||
```
|
||||
|
||||
By default it's using a file path which makes adding more file like stuff annoying,
|
||||
|
||||
```yaml
|
||||
traefik:
|
||||
image: traefik:v3.7
|
||||
container_name: traefik
|
||||
network_mode: service:gerbil
|
||||
restart: always
|
||||
secrets:
|
||||
- cf_dns_api_token
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./rules:/rules:ro # This
|
||||
```
|
||||
|
||||
Then we go in the folder relative of the compose,
|
||||
|
||||
You will need to move your traefik dynamic into this folder. Depending how you have set it up I would also recommend to be using wildcard certs with dns validation if you aren't already.
|
||||
|
||||
Anyways, `/rules/tls.yml`
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/traefik-v3-file-provider.json
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
minVersion: VersionTLS12
|
||||
maxVersion: VersionTLS13
|
||||
sniStrict: true # Clients without SNI or wrong hostname will fail; expected for strict TLS.
|
||||
# clientAuth:
|
||||
# # in PEM format. each file can contain multiple CAs.
|
||||
# caFiles:
|
||||
# - /certs/global/origin-pull-ca.pem
|
||||
# clientAuthType: RequireAndVerifyClientCert
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
||||
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
curvePreferences:
|
||||
- secp521r1
|
||||
- secp384r1
|
||||
- x25519
|
||||
```
|
||||
|
||||
`/rules/middlewares.yml`
|
||||
|
||||
Though would recommend to setup chains if you plan to have other middleware's to be aligned.. The above will score you an A+
|
||||
|
||||
```yaml
|
||||
http:
|
||||
middlewares:
|
||||
middlewares-secure-headers:
|
||||
headers:
|
||||
accessControlAllowMethods:
|
||||
- GET
|
||||
- OPTIONS
|
||||
- PUT
|
||||
hostsProxyHeaders:
|
||||
- "X-Forwarded-Host"
|
||||
#sslRedirect: true # Not used in Version 2.5
|
||||
stsSeconds: 63072000
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
forceSTSHeader: true
|
||||
frameDeny: true #overwritten by customFrameOptionsValue
|
||||
customFrameOptionsValue: "SAMEORIGIN"
|
||||
contentTypeNosniff: true
|
||||
browserXssFilter: false
|
||||
customBrowserXSSValue: "0"
|
||||
referrerPolicy: "same-origin"
|
||||
contentSecurityPolicy: "upgrade-insecure-requests"
|
||||
customResponseHeaders:
|
||||
X-Robots-Tag: "none, noarchive, nosnippet, notranslate, noimageindex"
|
||||
X-powered-by: ""
|
||||
server: ""
|
||||
Permissions-Policy: "camera=(), microphone=(), geolocation=()"
|
||||
|
||||
chain-secure:
|
||||
chain:
|
||||
middlewares:
|
||||
- middlewares-secure-headers
|
||||
```
|
||||
|
||||
In your `traefik_config.yml`
|
||||
|
||||
```yaml
|
||||
ocsp: {} # Let Traefik check for ocsp over clients,
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
http:
|
||||
redirections: # Only caring for https based traffic.
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
permanent: true
|
||||
# forwardedHeaders: # Don't need this unless you're using cloudflare infront or so
|
||||
# trustedIPs: *trustedIPs
|
||||
websecure:
|
||||
address: ":443"
|
||||
asDefault: true # Routers without an entryPoints list use websecure. Mostly useful for file entries an example shown at the very bottom.
|
||||
http3: # If you want to have QUIC you can have these just make sure to enable in the compose - 443:443/udp but if not you can skip this.
|
||||
advertisedPort: 443
|
||||
# transport: # Can lead to dos attacks if you're not careful.
|
||||
# respondingTimeouts:
|
||||
# readTimeout: "30m"
|
||||
http:
|
||||
middlewares:
|
||||
- middlewares-secure-headers@file # We don't include this on the web part as some stuff like HSTS and other headers aren't meant to be served on http at all and would go against spec like Fiesty duck's.
|
||||
tls:
|
||||
options: default # Can be named anything to align with the rules/tls.yml
|
||||
certResolver: dns # Match to your certResolver line in /config/traefik/traefik_config.yml
|
||||
|
||||
# forwardedHeaders: # Don't set this unless you know what you're doing.
|
||||
# trustedIPs: *trustedIPs # *trustedIPs must be a YAML anchor defined earlier in this file (e.g. Cloudflare IP ranges).
|
||||
|
||||
# proxyProtocol: # Don't set this unless you know what you're doing.
|
||||
# trustedIPs: *InternalIPs # *InternalIPs anchor—only enable if something in front actually sends PROXY protocol.
|
||||
```
|
||||
|
||||
Would also recommend to ask for EC384 certs which are more secure then RSA but also might limit older clients from connecting, You can do so via,
|
||||
|
||||
```yaml
|
||||
certificatesResolvers:
|
||||
dns:
|
||||
acme:
|
||||
storage: acme.json # Use a persistent volume path; file must be writable by Traefik and chmod'd to 600.
|
||||
keyType: 'EC384' # This asks LE for ECC certs over RSA, For reference a ECC384 is like asking for a RSA 7680-bit over like the normal RSA4096 or 2048's.
|
||||
dnsChallenge:
|
||||
provider: cloudflare # Change if you use another DNS host; provider must match your env/secrets setup.
|
||||
resolvers:
|
||||
- "1.1.1.1:53"
|
||||
- "1.0.0.1:53"
|
||||
```
|
||||
|
||||
## Pangolin dashboard (`/rules/pangolin.yml`)
|
||||
|
||||
Routers below use `chain-secure@file` from `/rules/middlewares.yml`.
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
next-router:
|
||||
rule: >
|
||||
(Host(`pangolin.{{ env "DOMAIN1" }}`))
|
||||
service: next-service
|
||||
middlewares:
|
||||
- chain-secure@file
|
||||
priority: 10
|
||||
|
||||
api-router:
|
||||
rule: >
|
||||
(Host(`pangolin.{{ env "DOMAIN1" }}`) && PathPrefix(`/api/v1`))
|
||||
service: api-service
|
||||
middlewares:
|
||||
- chain-secure@file
|
||||
priority: 100
|
||||
|
||||
services:
|
||||
next-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3002" # Next.js server
|
||||
|
||||
api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3000" # API/WebSocket server
|
||||
|
||||
tcp:
|
||||
serversTransports:
|
||||
pp-transport-v1:
|
||||
proxyProtocol:
|
||||
version: 1
|
||||
pp-transport-v2:
|
||||
proxyProtocol:
|
||||
version: 2
|
||||
```
|
||||
|
||||
What's not shown is like DANE-443 (TLSA), RoFS which I will write later along with the apparmor/seccomp profiles.
|
||||
@@ -0,0 +1,289 @@
|
||||
---
|
||||
title: "Traefik Log Dashboard (v2 – Agent Architecture)"
|
||||
---
|
||||
|
||||
<Note>
|
||||
This is a community guide and is not officially supported. For issues or advanced configuration, please visit the [official repository](https://github.com/hhftechnology/traefik-log-dashboard).
|
||||
</Note>
|
||||
|
||||
If you’re already using the **Pangolin stack with Traefik as your reverse proxy**, you already have robust routing in place.
|
||||
However, raw logs can be hard to interpret — making it difficult to visualize request patterns, latency, and geographic origins.
|
||||
|
||||
The **new Traefik Log Dashboard (v2)** introduces a **lightweight agent-based architecture** with **multi-instance scalability, enhanced GeoIP analytics, and a modern Next.js frontend** for real-time insights into your Traefik traffic.
|
||||
|
||||
---
|
||||
|
||||
## Highlights (New in v2)
|
||||
|
||||
* **Agent-based architecture**: The Go-powered agent parses logs, exposes metrics, and supports multiple Traefik instances.
|
||||
* **Multi-agent support**: Monitor multiple Traefik setups (e.g., production, staging) from one dashboard.
|
||||
* **Next.js 14 frontend**: Real-time charts, filters, and system stats in a responsive UI.
|
||||
* **Enhanced GeoIP**: Supports both **City** and **Country** MaxMind databases.
|
||||
* **System monitoring**: Built-in CPU, memory, and disk tracking.
|
||||
* **Bearer token authentication**: Secure access between dashboard and agents.
|
||||
* **Backward compatible** with existing Traefik log setups.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Docker + Docker Compose
|
||||
* Traefik v2.x or v3.x (logs in JSON format)
|
||||
* A working **Pangolin stack**
|
||||
* (Optional) MaxMind GeoLite2 databases (City + Country)
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Configure Traefik Logs
|
||||
|
||||
Ensure Traefik is outputting **JSON logs** and **access logs** are written to a file.
|
||||
|
||||
Update your `./config/traefik/traefik_config.yml`:
|
||||
|
||||
```yaml
|
||||
log:
|
||||
level: INFO
|
||||
filePath: "/var/log/traefik/traefik.log"
|
||||
format: json
|
||||
|
||||
accessLog:
|
||||
filePath: "/var/log/traefik/access.log"
|
||||
format: json
|
||||
fields:
|
||||
defaultMode: keep
|
||||
headers:
|
||||
defaultMode: keep
|
||||
````
|
||||
|
||||
> Tip: JSON format is required for accurate parsing by the new agent.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Add Dashboard and Agent Services
|
||||
|
||||
Extend your existing `docker-compose.yml` with the new services.
|
||||
|
||||
```yaml
|
||||
# Traefik Log Dashboard Agent
|
||||
traefik-agent:
|
||||
image: hhftechnology/traefik-log-dashboard-agent:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5000:5000"
|
||||
volumes:
|
||||
- ./data/positions:/data
|
||||
- ./config/traefik/logs:/logs:ro
|
||||
- ./config/maxmind:/geoip:ro
|
||||
environment:
|
||||
# Log Paths
|
||||
- TRAEFIK_LOG_DASHBOARD_ACCESS_PATH=/logs/access.log
|
||||
- TRAEFIK_LOG_DASHBOARD_ERROR_PATH=/logs/traefik.log
|
||||
|
||||
# Authentication
|
||||
- TRAEFIK_LOG_DASHBOARD_AUTH_TOKEN=YOUR_API_TOKEN
|
||||
|
||||
# System Monitoring
|
||||
- TRAEFIK_LOG_DASHBOARD_SYSTEM_MONITORING=true
|
||||
|
||||
# GeoIP Configuration
|
||||
- TRAEFIK_LOG_DASHBOARD_GEOIP_ENABLED=true
|
||||
- TRAEFIK_LOG_DASHBOARD_GEOIP_CITY_DB=/geoip/GeoLite2-City.mmdb
|
||||
- TRAEFIK_LOG_DASHBOARD_GEOIP_COUNTRY_DB=/geoip/GeoLite2-Country.mmdb
|
||||
|
||||
# Log Format
|
||||
- TRAEFIK_LOG_DASHBOARD_LOG_FORMAT=json
|
||||
|
||||
# Server Port
|
||||
- PORT=5000
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5000/api/logs/status"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
# Traefik Log Dashboard - Web UI
|
||||
traefik-dashboard:
|
||||
image: hhftechnology/traefik-log-dashboard:latest
|
||||
container_name: traefik-log-dashboard
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
# Agent Configuration
|
||||
- AGENT_API_URL=http://traefik-agent:5000
|
||||
- AGENT_API_TOKEN=YOUR_API_TOKEN
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
depends_on:
|
||||
traefik-agent:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
```
|
||||
|
||||
Please replace the YOUR_API_TOKEN with a secure token of your choice.
|
||||
|
||||
> Note: The new agent replaces both `log-dashboard-backend` and `log-dashboard-frontend` from the previous guide.
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Setup MaxMind GeoIP (City + Country)
|
||||
|
||||
GeoIP is optional but highly recommended for geographic analytics and maps.
|
||||
|
||||
### 1. Create a free MaxMind account
|
||||
|
||||
[GeoLite2 Signup](https://www.maxmind.com/en/geolite2/signup)
|
||||
Generate a license key and export it for Docker use:
|
||||
|
||||
```bash
|
||||
export MAXMIND_LICENSE_KEY=your_license_key_here
|
||||
mkdir -p ./config/maxmind
|
||||
```
|
||||
|
||||
### 2. Add the GeoIP Database Updater
|
||||
|
||||
Append this to your `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
# Optional: MaxMind GeoIP Database Updater
|
||||
maxmind-updater:
|
||||
image: alpine:latest
|
||||
restart: "no"
|
||||
volumes:
|
||||
- ./config/maxmind:/data
|
||||
environment:
|
||||
- MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY:-your-license-key-here}
|
||||
command: >
|
||||
sh -c "
|
||||
apk add --no-cache wget tar &&
|
||||
cd /data &&
|
||||
if [ ! -f GeoLite2-City.mmdb ] || [ \"$(find . -name 'GeoLite2-City.mmdb' -mtime +7)\" ]; then
|
||||
echo 'Updating GeoLite2-City database...'
|
||||
wget -O GeoLite2-City.tar.gz 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz' &&
|
||||
tar --wildcards -xzf GeoLite2-City.tar.gz --strip-components=1 '*/GeoLite2-City.mmdb' &&
|
||||
rm -f GeoLite2-City.tar.gz
|
||||
fi &&
|
||||
if [ ! -f GeoLite2-Country.mmdb ] || [ \"$(find . -name 'GeoLite2-Country.mmdb' -mtime +7)\" ]; then
|
||||
echo 'Updating GeoLite2-Country database...'
|
||||
wget -O GeoLite2-Country.tar.gz 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz' &&
|
||||
tar --wildcards -xzf GeoLite2-Country.tar.gz --strip-components=1 '*/GeoLite2-Country.mmdb' &&
|
||||
rm -f GeoLite2-Country.tar.gz
|
||||
fi &&
|
||||
echo 'GeoIP databases updated successfully.'
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Launch the Stack
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Access the Dashboard
|
||||
|
||||
* **Web UI** → [http://localhost:3000](http://localhost:3000)
|
||||
* Default data source: `traefik-agent:5000`
|
||||
|
||||
You should see real-time traffic metrics, GeoIP maps, error tracking, and system performance indicators.
|
||||
|
||||
---
|
||||
|
||||
## Key Features
|
||||
|
||||
**Real-time analytics** for request rates, response times, and errors
|
||||
**GeoIP maps** with both City and Country-level resolution
|
||||
**System health** (CPU, memory, disk)
|
||||
**Multi-agent support** (monitor multiple Traefik instances)
|
||||
**Secure API authentication** via token
|
||||
**Responsive modern UI**
|
||||
|
||||
---
|
||||
|
||||
## Advanced: Multi-Agent Setup
|
||||
|
||||
You can deploy multiple `traefik-agent` instances across environments and connect them all to a single dashboard.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
traefik-agent-prod:
|
||||
image: hhftechnology/traefik-log-dashboard-agent:latest
|
||||
ports: ["5000:5000"]
|
||||
environment:
|
||||
- TRAEFIK_LOG_DASHBOARD_AUTH_TOKEN=prod_token
|
||||
- TRAEFIK_LOG_DASHBOARD_ACCESS_PATH=/logs/access.log
|
||||
- TRAEFIK_LOG_DASHBOARD_GEOIP_ENABLED=true
|
||||
volumes:
|
||||
- /var/log/traefik/prod:/logs:ro
|
||||
- ./config/maxmind:/geoip:ro
|
||||
- ./data/positions-prod:/data
|
||||
|
||||
traefik-agent-staging:
|
||||
image: hhftechnology/traefik-log-dashboard-agent:latest
|
||||
ports: ["5001:5000"]
|
||||
environment:
|
||||
- TRAEFIK_LOG_DASHBOARD_AUTH_TOKEN=staging_token
|
||||
- TRAEFIK_LOG_DASHBOARD_ACCESS_PATH=/logs/access.log
|
||||
volumes:
|
||||
- /var/log/traefik/staging:/logs:ro
|
||||
- ./config/maxmind:/geoip:ro
|
||||
|
||||
traefik-dashboard:
|
||||
image: hhftechnology/traefik-log-dashboard:latest
|
||||
ports: ["3000:3000"]
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
```
|
||||
|
||||
Then, in the **Dashboard → Settings → Agents**, add each agent URL and token.
|
||||
|
||||
---
|
||||
|
||||
## Performance Tuning
|
||||
|
||||
| Setting | Description | Recommended |
|
||||
| ----------------------------------------- | -------------------------------- | ------------- |
|
||||
| `TRAEFIK_LOG_DASHBOARD_SYSTEM_MONITORING` | Enables system stats | `true` |
|
||||
| `TRAEFIK_LOG_DASHBOARD_LOG_FORMAT` | Log parsing format | `json` |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
| --------------------- | ------------------------ | ------------------------------------------------------------------- |
|
||||
| Dashboard not loading | Container not healthy | `docker compose ps` → check `health` |
|
||||
| No logs appearing | Wrong log path or format | Ensure `access.log` is JSON and volume mounted |
|
||||
| GeoIP missing | Missing databases | Run `maxmind-updater` or mount both `.mmdb` files |
|
||||
| Auth errors | Token mismatch | Verify `AGENT_API_TOKEN` matches `TRAEFIK_LOG_DASHBOARD_AUTH_TOKEN` |
|
||||
| Slow UI | Large logs | Use JSON logs + incremental read; prune logs periodically |
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
- Replaces the old `log-dashboard-backend` + `log-dashboard-frontend` with the new **agent-based architecture**
|
||||
- Supports **multiple Traefik instances**
|
||||
- Adds **GeoLite2 Country + City databases**
|
||||
- Integrates **real-time analytics + system monitoring**
|
||||
- Uses **MaxMind license key** for GeoIP updates
|
||||
- More stable with less memory
|
||||
|
||||
---
|
||||
|
||||
**Project Repository** → [https://github.com/hhftechnology/traefik-log-dashboard](https://github.com/hhftechnology/traefik-log-dashboard)
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user