Skip to content

Commit

Permalink
Update securing swag
Browse files Browse the repository at this point in the history
  • Loading branch information
quietsy committed Feb 5, 2025
1 parent 97fe877 commit 93ae47a
Showing 1 changed file with 79 additions and 121 deletions.
200 changes: 79 additions & 121 deletions docs/secure.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,49 @@
# Securing SWAG
[SWAG](https://github.com/linuxserver/docker-swag) - Secure Web Application Gateway (formerly known as linuxserver/letsencrypt) is a full fledged web server and reverse proxy with Nginx, PHP7, Certbot (Let's Encrypt™ client) and Fail2Ban built in. SWAG allows you to expose applications to the internet, doing so comes with a risk and there are security measures that help reduce that risk. This article details how to configure SWAG and enhance it's security.

[SWAG](https://github.com/linuxserver/docker-swag) is a reverse proxy that allows you to expose your self-hosted apps to the world, but that comes with risks.

We can mitigate some risks by fine-tuning SWAG and how we access it:

- Access your apps through Wireguard instead of exposing them.
- Prevent accessing some apps via the internet while exposing others.
- Set up brute-force protection via Crowdsec/Fail2Ban.
- Set up geoblock to whitelist/blacklist countries via DBIP/Maxmind.
- Prevent your apps from appearing in search results.
- Set up SSO via Authelia/Authentik.
- Monitor SWAG via a dashboard.

## Requirements

- A working instance of [SWAG](https://github.com/linuxserver/docker-swag)
- A working instance of [SWAG](https://github.com/linuxserver/docker-swag).

## Monitor SWAG
Use monitoring solutions such as [SWAG Dashboard](https://github.com/linuxserver/docker-mods/tree/swag-dashboard) to keep an eye on the traffic going through SWAG and check for suspicious activity such as:
## VPN

- A lot of hits from a country unrelated to your users
- A lot of requests to a specific page or static file
- Referers that shouldn't refer to your domain
- A lot of hits on status codes that are not 2xx
The most effective security you can implement is to stop exposing your apps entirely, and instead access them via [WireGuard](https://github.com/linuxserver/docker-wireguard).

### Requirements

- A working instance of [WireGuard](https://github.com/linuxserver/docker-wireguard).
- [Split DNS](https://docs.linuxserver.io/general/split-dns/) - the source IP on requests needs to be local for SWAG to work without being exposed.
- [DNS Validation](https://docs.linuxserver.io/general/swag/#create-container-via-dns-validation-with-a-wildcard-cert) - allows you to get an SSL certificate without port forwarding.

Once you've set up wireguard, split DNS, and DNS validation, you can remove the port forwarding on your router and change your domain's DNS records to `127.0.0.1` instead of your WAN IP on the DNS provider.

## Internal Applications
Internal applications can be proxied through SWAG in order to use `app.mydomain.com` instead of ip:port, and block them externally so only your local network could access them.

If you want to share some apps with others and don't want to give them full VPN access, only expose what you must expose, keep the rest internal.

### Requirements

- [Split DNS](https://docs.linuxserver.io/general/split-dns/) - the source IP on requests needs to be local for allow/deny to work properly.

Create a file called `nginx/internal.conf` with the following configuration:

```Nginx
allow 192.168.1.0/24; #Replace with your LAN subnet
allow 192.168.1.0/24; # Replace with your LAN subnet
deny all;
```

Utilize the lan filter in your configuration by adding the following line inside every location block for every application you want to protect.
Utilize the LAN filter in your configuration by adding the following line inside every location block for every application you want to protect.
```
include /config/nginx/internal.conf;
```
Expand All @@ -38,9 +58,9 @@ server {
server_name collabora.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
include /config/nginx/internal.conf;
location / {
include /config/nginx/internal.conf;
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app collabora;
Expand All @@ -53,49 +73,60 @@ server {

Repeat the process for all internal applications and for every location block.

One way to securely access internal applications from the internet is through a VPN, for example WireGuard:
## Brute-Force Protection

[WireGuard Container](https://hub.docker.com/r/linuxserver/wireguard)
### Crowdsec

[WireGuard on OPNSense](https://blog.linuxserver.io/2019/11/16/setting-up-wireguard-on-opnsense-android/)
[Crowdsec](https://www.linuxserver.io/blog/blocking-malicious-connections-with-crowdsec-and-swag) is a free, open-source and collaborative IPS; it's like fail2ban but you share your bans with all of the other users to try and pre-emptively block malicious hosts.

### Fail2Ban

## Fail2Ban
Fail2Ban is an intrusion prevention software that protects external applications from brute-force attacks. Attackers that fail to login to your applications a certain number of times will get blocked from accessing all of your applications.
Fail2Ban looks for failed login attempts in log files, counts the failed attempts in a short period, and bans the IP address of the attacker.

Mount the application logs to SWAG's container by adding a volume for the log to the compose yaml:

```
- /path/to/nextcloud/nextcloud.log:/nextcloud/nextcloud.log:ro
```
If the application has multiple log files with dates, mount the entire folder:
```
- /path/to/jellyfin/log:/jellyfin:ro
- /path/to/nextcloud/logs:/nextcloud:ro
```

Note that you should only mount the parent folders, log files can rotate.

Recreate the container with the log mount, then create a file called `nextcloud.local` under `fail2ban/filter.d`:

```nginx
[Definition]
failregex=^.*Login failed: '?.*'? \(Remote IP: '?<ADDR>'?\).*$
^.*\"remoteAddr\":\"<ADDR>\".*Trusted domain error.*$
ignoreregex =
```
The configuration file containes a pattern by which failed login attempts are matched. Test the pattern by failing to login to nextcloud and look for the entry corresponding to your failed attempt.

Before making your own Fail2Ban filter for apps search online for existing one, most chances someone already made it.

The filter containes a pattern by which failed login attempts are matched. Test the pattern by failing to login to nextcloud and look for the entry corresponding to your failed attempt.

```
{"reqId":"k5j5H7K3eskXt3hCLSc4i","level":2,"time":"2020-10-14T22:56:14+00:00","remoteAddr":"1.2.3.4","user":"--",
"app":"no app in context","method":"POST","url":"/login","message":"Login failed: username (Remote IP: 5.5.5.5)",
"userAgent":"Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/5.6.7.8 Mobile
Safari/537.36","version":"19.0.4.2"}
```

Test the pattern in `nextcloud.local` by running the following command on the docker host:

```
docker exec swag fail2ban-regex /nextcloud/nextcloud.log /config/fail2ban/filter.d/nextcloud.local
```

If the pattern works, you will see matches corresponding to the amount of failed login attempts:

```
Lines: 92377 lines, 0 ignored, 2 matched, 92375 missed
[processed in 7.51 sec]
```

The final step is to activate the jail, add the following to `fail2ban/jail.local`:

```nginx
[nextcloud]
enabled = true
Expand All @@ -104,7 +135,9 @@ filter = nextcloud
logpath = /nextcloud/nextcloud.log
action = iptables-allports[name=nextcloud]
```

The logpath is slightly different for applications that have multiple log files with dates:

```nginx
[jellyfin]
enabled = true
Expand All @@ -117,131 +150,56 @@ action = iptables-allports[name=jellyfin]
Repeat the process for every external application, you can find Fail2Ban configurations for most applications on the internet.

If you need to unban an IP address that was blocked, run the following command on the docker host:

```
docker exec swag fail2ban-client unban <ip address>
```

This great mod sends a discord notification when Fail2Ban blocks an attack: [f2bdiscord](https://github.com/linuxserver/docker-mods/tree/swag-f2bdiscord).

## Geoblock
Geoblock reduces the attack surface of SWAG by restricting access based on countries.

Enable geoblock using either [DBIP mod](https://github.com/linuxserver/docker-mods/tree/swag-dbip) or [Maxmind mod](https://github.com/linuxserver/docker-mods/tree/swag-maxmind), follow the mod's instructions to set it up.

The mods come with 3 definitions for `$geo-whitelist`, `$geo-blacklist`, `$lan-ip`.

An example for allowing a single country:
```Nginx
map $geoip2_data_country_iso_code $geo-whitelist {
default no;
UK yes; #Replace with your country code list https://dev.maxmind.com/geoip/legacy/codes/iso3166/
}
```
An example for blocking high risk countries: (GilbN's list based on the Spamhaus statistics and Aakamai’s state of the internet report)
```Nginx
map $geoip2_data_country_iso_code $geo-blacklist {
default yes; #If your country is listed below, remove it from the list
CN no; #China
RU no; #Russia
HK no; #Hong Kong
IN no; #India
IR no; #Iran
VN no; #Vietnam
TR no; #Turkey
EG no; #Egypt
MX no; #Mexico
JP no; #Japan
KR no; #South Korea
KP no; #North Korea
PE no; #Peru
BR no; #Brazil
UA no; #Ukraine
ID no; #Indonesia
TH no; #Thailand
}
```

Utilize the geoblock in your configuration by adding one of the following lines above your location section in every application you want to protect.

**Note that when using a whitelist filter, you also need to check if the source is a LAN IP, it's not required when using a blacklist filter.**
```nginx
if ($lan-ip = yes) { set $geo-whitelist yes; }
if ($geo-whitelist = no) { return 404; }
```
Or
```nginx
if ($geo-blacklist = no) { return 404; }
```
Geoblock significantly reduces the attack surface of SWAG by restricting access based on countries.

Example:
Follow the instructions of one of the following mods to set it up:

```Nginx
server {
listen 443 ssl;
listen [::]:443 ssl;
- [DBIP mod](https://github.com/linuxserver/docker-mods/tree/swag-dbip)
- [Maxmind mod](https://github.com/linuxserver/docker-mods/tree/swag-maxmind)

server_name authelia.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
DBIP doesn't require an account, but Maxmind might be more accurate in some cases.

if ($lan-ip = yes) { set $geo-whitelist yes; } #Check for a LAN IP
if ($geo-whitelist = no) { return 404; } #Check the country filter
## Search Results

location / {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app authelia;
set $upstream_port 9091;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
}
```
### X-Robots-Tag

Add the lines to every external application based on your needs.
You can prevent your apps from appearing in search engines results and being crawled by web crawlers.

Note that not all search engines and web crawlers respect this tag, but it significantly reduces the amount.

## NGINX Configuration
### X-Robots-Tag
You can prevent applications from appearing in results of search engines and web crawlers, regardless of whether other sites link to it. It doesn't work on all search engines and web crawlers, but it significantly reduces the amount.
Add the following to `ssl.conf` to enable it on **all** apps:

Add the X-Robots-Tag config line to `ssl.conf` to enable it on **all** of your applications:
```
add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive";
```

Disable on a specific application and allow search engines to display it by add the following line to the application config inside the server tag:
```
add_header X-Robots-Tag "";
```

### HSTS
HTTP Strict Transport Security (HSTS) is a web security policy mechanism that helps to protect websites against man-in-the-middle attacks such as protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should automatically interact with it using only HTTPS connections, which provide Transport Layer Security (TLS/SSL), unlike the insecure HTTP used alone.
To disable on a specific app, add the following line to the app's proxy-conf inside the server tag:

**HSTS requires a working SSL certificate on your domains before enabling it.**

Enable HSTS by uncommenting the HSTS config line in ssl.conf:
```
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Robots-Tag "";
```

#### Optional - Strengthening HSTS
After enabling the HSTS header, users are still vulnerable to attack if they access an HSTS‑protected website over HTTP when they have:
## SSO

- Never before visited the site
- Recently reinstalled their operating system
- Recently reinstalled their browser
- Switched to a new browser
- Switched to a new device (for example, mobile phone)
- Deleted their browser’s cache
- Not visited the site recently and the max-age time has passed
Setting up SSO will provide an additional layer of security and protect you against login bypass exploits in apps.

To address this, Google maintains a “HSTS preload list” of web domains and subdomains that use HSTS and have submitted their names to [HSTS Preload](https://hstspreload.org/). This domain list is distributed and hardcoded into major web browsers. Clients that access web domains in this list automatically use HTTPS and refuse to access the site using HTTP.
- [Authelia](https://blog.linuxserver.io/2020/08/26/setting-up-authelia/)
- [Authentik](https://goauthentik.io/)

Be aware that once you set the STS header or submit your domains to the HSTS preload list, it is impossible to remove it. It’s a one‑way decision to make your domains available over HTTPS.
Note that api endpoints shouldn't have SSO for them to function properly.

## Monitor

## Authelia
Authelia is an open-source authentication and authorization server providing 2-factor authentication and single sign-on (SSO) for your applications via a web portal. Refer to this [blog post to configure Authelia](https://blog.linuxserver.io/2020/08/26/setting-up-authelia/).

Use monitoring solutions such as [SWAG Dashboard](https://github.com/linuxserver/docker-mods/tree/swag-dashboard) to keep an eye on the traffic going through SWAG and check for suspicious activity such as:

- Many hits from a country unrelated to your users.
- Many requests to a specific page or static file.
- Referers that shouldn't refer to your domain.
- Many hits on status codes that are not 2xx.

0 comments on commit 93ae47a

Please sign in to comment.