USE THIS GUIDE WITH CATION! Performance impact if not deployed correctly.
Pangolin provides an excellent solution for secure tunneled reverse proxying, but like any internet-facing service, it can be vulnerable to various attacks. One of the most common and dangerous is the SYN flood attack, which can overwhelm your server by flooding it with connection requests.
This guide will walk you through implementing robust protections for your Pangolin infrastructure against SYN flood attacks and other security threats.
Understanding the Pangolin Stack
Before diving into security measures, let’s understand the key components of the Pangolin stack:
- Pangolin: The core management server that provides the web UI, API, and authentication
- Gerbil: WireGuard interface management server that creates and manages tunnels
- Traefik: The reverse proxy that handles incoming connections and routes traffic
- Badger: Traefik middleware that validates authentication
- CrowdSec: Security automation tool already included in the stack for threat detection
The most vulnerable parts of this stack are the publicly exposed services, particularly Traefik and Gerbil, which handle incoming connections on ports 80, 443, and 51820 (WireGuard).
What is a SYN Flood Attack?
A SYN flood attack exploits the TCP three-way handshake process:
- Client sends a SYN packet to the server
- Server responds with a SYN-ACK packet
- Client completes the handshake with an ACK packet
In a SYN flood attack, attackers send numerous SYN packets without completing the handshake. This leaves connections half-open, consuming server resources until they time out. When these half-open connections exhaust the server’s capacity, legitimate connections can’t be established.
Security Measures Already in Place
Your Pangolin stack already includes some security components:
- CrowdSec integration: Detects and blocks various attack patterns
- Traefik secure headers: Configured in the
middlewares-secure-headers.yaml - TLS configuration: Secure SSL/TLS settings in
tls-opts.yaml
Let’s enhance these with additional protections specifically targeting SYN flood attacks.
Step-by-Step Implementation Guide
Step 1: Optimize Kernel Parameters
SSH into your server and modify the kernel parameters to better handle SYN flood attempts:
sudo nano /etc/sysctl.conf
Add or uncomment these lines:
# Protection against IP spoofing
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
# SYN flood protection
net.ipv4.tcp_syncookies=1
net.ipv4.tcp_max_syn_backlog=4096
net.ipv4.tcp_synack_retries=3
# Additional network hardening
net.ipv4.tcp_timestamps=0
net.ipv4.tcp_sack=0
net.ipv4.tcp_dsack=0
Apply the changes:
sudo sysctl -p
Step 2: Create SYN Flood Protection Firewall Rules
Create a dedicated file for SYN flood protection rules:
sudo mkdir -p ./config/traefik/rules/security
sudo nano ./config/traefik/rules/security/syn-flood-protection.yaml
Now, we need to modify the UFW firewall rules to implement SYN flood protection. First, check if UFW is active:
sudo ufw status
If not active, enable it:
sudo ufw allow 22/tcp
sudo ufw enable
Now, edit the UFW before rules:
sudo nano /etc/ufw/before.rules
Add these lines before the final COMMIT line:
# SYN flood protection chains
:SYN_FLOOD_PROTECTION - [0:0]
:fail2ban - [0:0]
# Check fail2ban chain first
-A ufw-before-input -j fail2ban
-A fail2ban -j RETURN
# Apply SYN flood protection to HTTP and HTTPS ports
-A ufw-before-input -p tcp --syn --dport 80 -j SYN_FLOOD_PROTECTION
-A ufw-before-input -p tcp --syn --dport 443 -j SYN_FLOOD_PROTECTION
# Drop excessive SYN packets and log them
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j DROP
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 80 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name http_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j LOG --log-prefix "[UFW SYN Flood Detected] "
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 443 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name https_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j DROP
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 443 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name https_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j LOG --log-prefix "[UFW SYN Flood Detected] "
# Allow normal SYN packets after checks
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 443 -m conntrack --ctstate NEW -j ACCEPT
# Block invalid packets
*mangle
:PREROUTING ACCEPT [0:0]
-A PREROUTING -m conntrack --ctstate INVALID -j DROP
-A PREROUTING -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j DROP
COMMIT
Do the same for IPv6 by editing /etc/ufw/before6.rules with similar rules, adjusting the chain name to ufw6-before-input.
Reload UFW:
sudo ufw reload
Step 3: Configure Fail2ban for Persistent Protection
Install Fail2ban if not already installed:
sudo apt install fail2ban -y
Create a custom filter for SYN flood detection:
sudo nano /etc/fail2ban/filter.d/synflood.conf
Add this content:
[Definition]
failregex = .*UFW SYN Flood Detected.*SRC=<HOST>.*DPT=\d+.*
ignoreregex =
Create a local jail configuration:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
Add this section under the # JAILS heading:
[synflood]
enabled = true
filter = synflood
action = iptables[type=allports, name=synflood, chain=fail2ban, protocol=tcp]
logpath = /var/log/syslog
maxretry = 5
findtime = 600
bantime = 86400
Start Fail2ban:
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
Step 4: Enhance Traefik Security Configuration
Add rate limiting by creating a file:
sudo nano ./config/traefik/rules/security/rate-limit.yaml
With this content:
http:
middlewares:
global-rate-limit:
rateLimit:
average: 100
burst: 50
Update your Traefik entry points configuration to apply these middlewares. Edit the traefik_config.yml file:
sudo nano ./config/traefik/traefik_config.yml
Find the entryPoints section and add the middleware to the websecure entry point:
entryPoints:
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: "30m"
http:
middlewares:
- crowdsec@file
- middlewares-compress@file
- middlewares-secure-headers@file
- global-rate-limit@file # Add this line
Step 5: Configure CrowdSec for Improved Attack Detection
CrowdSec is already included in your stack. Let’s make sure it’s properly configured to detect SYN flood attacks.
First, add a custom CrowdSec scenario:
sudo mkdir -p ./config/crowdsec/scenarios
sudo nano ./config/crowdsec/scenarios/syn-flood.yaml
Add this content:
type: leaky
name: pangolin-syn-flood
description: "Detect SYN flood attacks"
filter: "evt.Parsed.program == 'kernel' && evt.Parsed.message contains '[UFW SYN Flood Detected]'"
leakspeed: "10s"
capacity: 5
labels:
service: pangolin
type: syn-flood
remediation: true
groupby: evt.Meta.source_ip
blackhole: 1m
reprocess: false
Next, create an appropriate CrowdSec profile:
sudo nano ./config/crowdsec/profiles/syn-flood.yaml
With this content:
name: syn-flood-ban
filters:
- 'decision.labels.type == "syn-flood"'
decisions:
- type: ban
duration: 1h
on_success: break
Step 6: Restart the Stack
After making all these changes, restart the Pangolin stack:
sudo docker compose down
sudo docker compose up -d
Step 7: Monitoring and Testing
Monitor your logs for any signs of attacks:
# Check UFW logs
sudo grep "SYN Flood Detected" /var/log/syslog
# Check Fail2ban status
sudo fail2ban-client status synflood
# Check CrowdSec decisions
sudo docker exec -it crowdsec cscli decisions list
Additional Security Enhancements
1. Secure WireGuard (Gerbil)
WireGuard, managed by Gerbil, uses port 51820 by default. Protect it by adding:
sudo nano /etc/ufw/before.rules
Add before the final COMMIT:
# WireGuard rate limiting
-A ufw-before-input -p udp --dport 51820 -m hashlimit --hashlimit-name wireguard_limit --hashlimit-above 5/second --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-srcmask 32 -j DROP
-A ufw-before-input -p udp --dport 51820 -m hashlimit --hashlimit-name wireguard_limit --hashlimit-above 5/second --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-srcmask 32 -j LOG --log-prefix "[UFW WireGuard Flood] "
2. Add Cloudflare as a Protective Layer
If possible, consider putting your Pangolin instance behind Cloudflare for additional DDoS protection:
- Set up Cloudflare for your domain
- Configure Traefik to trust Cloudflare IP addresses (already configured in your
traefik_config.yml) - Enable Cloudflare’s security features like “I’m Under Attack” mode if needed
3. Secure Database and Logs
Ensure your database and logs are properly secured:
sudo chmod 700 ./config/db
sudo chmod 600 ./config/db/db.sqlite
sudo chmod 700 ./config/logs
Monitoring for Attacks
Set up monitoring for potential attacks:
- Regular log checking: Create a cron job to check logs for patterns of attacks
sudo crontab -e
Add:
0 * * * * grep "SYN Flood Detected" /var/log/syslog | wc -l > /tmp/syn_flood_count.txt
- Email alerts: Configure Fail2ban to send email notifications when IPs are banned
Edit /etc/fail2ban/jail.local:
[DEFAULT]
destemail = your-email@example.com
sender = fail2ban@your-server.com
mta = sendmail
action = %(action_mwl)s
How to Unban IP Addresses
If a legitimate IP gets banned, you can unban it using:
# For Fail2ban
sudo fail2ban-client set synflood unbanip [IP_ADDRESS]
# For CrowdSec
sudo docker exec -it crowdsec cscli decisions delete --ip [IP_ADDRESS]
Conclusion
By implementing these measures, your Pangolin stack should now be well-protected against SYN flood attacks and other common security threats. The combination of kernel optimizations, firewall rules, Fail2ban, and CrowdSec provides multiple layers of defense, ensuring your reverse proxy remains available even during attack attempts.
Remember to regularly:
- Monitor system logs for suspicious activity
- Keep all components updated to the latest versions
- Review and adjust rate limits based on your specific traffic patterns
This security setup balances protection against attacks with allowing legitimate traffic through, ensuring your Pangolin deployment remains both secure and functional.
Adding Additional Open Ports with Protection to Your Pangolin Stack
When expanding your Pangolin setup to accommodate additional services, you’ll need to open more ports while ensuring they remain protected from attacks. Here’s how to add new TCP and UDP ports with proper security measures in place:
Opening New TCP Ports with SYN Flood Protection
Step 1: Add the Port to UFW’s Allowed List
First, allow the new port through UFW. For example, to open TCP port 8080:
sudo ufw allow 8080/tcp
Step 2: Apply SYN Flood Protection to the New TCP Port
Edit your UFW before rules file:
sudo nano /etc/ufw/before.rules
You need to add two types of rules for each new TCP port:
- A rule to redirect traffic to your SYN_FLOOD_PROTECTION chain
- Rules within the SYN_FLOOD_PROTECTION chain to handle the new port
Find your existing SYN_FLOOD_PROTECTION section and add these lines:
# Redirect the new port to SYN_FLOOD_PROTECTION chain
-A ufw-before-input -p tcp --syn --dport 8080 -j SYN_FLOOD_PROTECTION
# Then inside the SYN_FLOOD_PROTECTION chain section, add:
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 8080 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name tcp8080_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j DROP
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 8080 -m conntrack --ctstate NEW -m hashlimit --hashlimit-name tcp8080_limit --hashlimit-above 10/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 32 -j LOG --log-prefix "[UFW SYN Flood Detected] "
-A SYN_FLOOD_PROTECTION -p tcp --syn --dport 8080 -m conntrack --ctstate NEW -j ACCEPT
Make sure to give each port a unique hashlimit name (like tcp8080_limit in this example).
Step 3: Apply the Same Rules to IPv6 (if used)
If you’re using IPv6, edit the IPv6 rules file:
sudo nano /etc/ufw/before6.rules
Add similar rules, changing the chain name to ufw6-before-input.
Opening New UDP Ports with Rate Limiting
UDP doesn’t use SYN packets like TCP, so SYN flood protection isn’t relevant. However, you should still apply rate limiting to prevent UDP flood attacks.
Step 1: Allow the UDP Port in UFW
For example, to open UDP port 5000:
sudo ufw allow 5000/udp
Step 2: Add Rate Limiting for the UDP Port
Edit the before.rules file again:
sudo nano /etc/ufw/before.rules
Add these lines before the final COMMIT:
# UDP rate limiting for port 5000
-A ufw-before-input -p udp --dport 5000 -m hashlimit --hashlimit-name udp5000_limit --hashlimit-above 15/second --hashlimit-burst 25 --hashlimit-mode srcip --hashlimit-srcmask 32 -j DROP
-A ufw-before-input -p udp --dport 5000 -m hashlimit --hashlimit-name udp5000_limit --hashlimit-above 15/second --hashlimit-burst 25 --hashlimit-mode srcip --hashlimit-srcmask 32 -j LOG --log-prefix "[UFW UDP Flood Detected] "
Note that UDP typically needs higher limits than TCP because it’s a connectionless protocol and legitimate applications often send more packets.
Step 3: Again, Apply to IPv6 if Needed
Apply the same rules to the before6.rules file if using IPv6.
Step 4: Update Fail2ban Configuration (Optional)
If you want Fail2ban to monitor and block IPs attempting to flood your new ports, edit the synflood filter:
sudo nano /etc/fail2ban/filter.d/synflood.conf
The existing filter should work as-is since it looks for the log prefix “[UFW SYN Flood Detected]” which we’re using for all ports.
Step 5: Configure Docker to Expose the Ports
If your services are running in Docker containers, update your docker-compose.yml file to expose the new ports. For the Gerbil service, which handles networking for Traefik:
gerbil:
# Existing configuration...
ports:
# Existing ports...
- 51820:51820/udp # WireGuard
- 443:443 # HTTPS
- 80:80 # HTTP
# Add your new ports
- 8080:8080 # New TCP port
- 5000:5000/udp # New UDP port
Step 6: Apply the Changes
Reload UFW to apply the new firewall rules:
sudo ufw reload
Restart Fail2ban to ensure it picks up any changes:
sudo systemctl restart fail2ban
If you modified your docker-compose.yml file, restart the affected containers:
sudo docker compose down
sudo docker compose up -d
Step 7: Test the New Ports
Verify that your ports are open and working correctly:
# Check if the port is listening
sudo netstat -tuln | grep 8080
# Test TCP connection
nc -zv your-server-ip 8080
# For UDP ports
nc -zuv your-server-ip 5000
Special Considerations for Raw TCP/UDP Resources in Pangolin
If you’re using Pangolin’s raw TCP/UDP resource feature (as described in the documentation you provided), you need a few additional steps:
- Configure the EntryPoint in Traefik for each port:
sudo nano ./config/traefik/traefik_config.yml
Add your new EntryPoints in the format described in the Pangolin documentation:
entryPoints:
# Existing entrypoints...
# Add TCP entrypoint
tcp-8080:
address: ":8080/tcp"
# Add UDP entrypoint
udp-5000:
address: ":5000/udp"
- Restart Traefik to apply the configuration changes:
sudo docker compose restart traefik
Monitoring Your New Ports
Keep an eye on traffic to your new ports:
# Monitor connections to a specific port
sudo netstat -anp | grep :8080
# Check logs for potential attacks on the new ports
sudo grep "UFW SYN Flood Detected" /var/log/syslog | grep "DPT=8080"
sudo grep "UFW UDP Flood Detected" /var/log/syslog | grep "DPT=5000"
By following these steps, you’ll have properly opened and protected new TCP and UDP ports in your Pangolin stack, maintaining the security measures we established earlier against potential flood attacks.