Securing SWAG
Warning
Little disclaimer. Some information was copied from virtualize.link, because I find information provided by its author useful. Make sure to check the source or star the repo.
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.
Requirements
- A working instance of SWAG
Monitor SWAG
Use monitoring solutions such as SWAG Dashboard to keep an eye on the traffic going through SWAG and check for suspicious activity such as:
- 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
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.
Create a file called nginx/internal.conf
with the following configuration:
Utilize the lan filter in your configuration by adding the following line inside every location block for every application you want to protect.
Example:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name collabora.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
location / {
include /config/nginx/internal.conf;
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app collabora;
set $upstream_port 9980;
set $upstream_proto https;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
}
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:
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:
If the application has multiple log files with dates, mount the entire folder: Recreate the container with the log mount, then create a file callednextcloud.local
under fail2ban/filter.d
:
[Definition]
failregex=^.*Login failed: '?.*'? \(Remote IP: '?<ADDR>'?\).*$
^.*\"remoteAddr\":\"<ADDR>\".*Trusted domain error.*$
ignoreregex =
{"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"}
nextcloud.local
by running the following command on the docker host:
If the pattern works, you will see matches corresponding to the amount of failed login attempts:
The final step is to activate the jail, add the following to fail2ban/jail.local
:
[nextcloud]
enabled = true
port = http,https
filter = nextcloud
logpath = /nextcloud/nextcloud.log
action = iptables-allports[name=nextcloud]
[jellyfin]
enabled = true
filter = jellyfin
port = http,https
logpath = /jellyfin/log*.log
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:
This great mod sends a discord notification when Fail2Ban blocks an attack: f2bdiscord.
Geoblock
Geoblock reduces the attack surface of SWAG by restricting access based on countries.
Enable geoblock using either DBIP mod or Maxmind mod, 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:
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/
}
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.
OrExample:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name authelia.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
if ($lan-ip = yes) { set $geo-whitelist yes; } #Check for a LAN IP
if ($geo-whitelist = no) { return 404; } #Check the country filter
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;
}
}
Add the lines to every external application based on your needs.
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 X-Robots-Tag config line to ssl.conf
to enable it on all of your applications:
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:
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.
HSTS requires a working SSL certificate on your domains before enabling it.
Enable HSTS by uncommenting the HSTS config line in ssl.conf:
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:
- 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
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. 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.
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.
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.