我有一个在 PHP-FPM/Nginx/MariaDB 上运行的网站,通过 Virtualmin 托管在 8GB RAM、4 核 Xeon 3.1ghz CPU 上,隐藏在 Cloudflare 后面。
最近,我们遭遇了一些简单的 DoS 攻击,一个人在其浏览器控制台上运行如下脚本:
setInterval(function() { fetch("/") }, 1)
能够轻松关闭网站。我尝试过微调 PHP-FPM 配置以应对高流量,但没有成功,我尝试过增加 MariaDB 的 RAM,但同样没有成功。
这是我的 PHP-FPM 配置:
listen.mode = 0660
pm = static
pm.max_children = 50
pm.start_servers = 16
pm.min_spare_servers = 8
pm.max_spare_servers = 16
php_value[memory_limit] = 128M
php_value[max_execution_time] = 300
我是不是漏掉了什么?一个人不可能只用一个简单的 Javascript 命令就能搞垮整个网站。
答案1
也许一些 iptables 规则有帮助
### 1: Drop invalid packets ###
/sbin/iptables -t mangle -A PREROUTING -m conntrack --ctstate INVALID -j DROP
### 2: Drop TCP packets that are new and are not SYN ###
/sbin/iptables -t mangle -A PREROUTING -p tcp ! --syn -m conntrack --ctstate NEW -j DROP
### 3: Drop SYN packets with suspicious MSS value ###
/sbin/iptables -t mangle -A PREROUTING -p tcp -m conntrack --ctstate NEW -m tcpmss ! --mss 536:65535 -j DROP
### 4: Block packets with bogus TCP flags ###
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,SYN FIN,SYN -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,ACK FIN -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ACK,URG URG -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ACK,FIN FIN -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ACK,PSH PSH -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL ALL -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL NONE -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP
### 5: Block spoofed packets ###
/sbin/iptables -t mangle -A PREROUTING -s 224.0.0.0/3 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 169.254.0.0/16 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 172.16.0.0/12 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 192.0.2.0/24 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 192.168.0.0/16 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 10.0.0.0/8 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 0.0.0.0/8 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 240.0.0.0/5 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 127.0.0.0/8 ! -i lo -j DROP
### 6: Drop ICMP (you usually don't need this protocol) ###
/sbin/iptables -t mangle -A PREROUTING -p icmp -j DROP
### 7: Drop fragments in all chains ###
/sbin/iptables -t mangle -A PREROUTING -f -j DROP
### 8: Limit connections per source IP ###
/sbin/iptables -A INPUT -p tcp -m connlimit --connlimit-above 111 -j REJECT --reject-with tcp-reset
### 9: Limit RST packets ###
/sbin/iptables -A INPUT -p tcp --tcp-flags RST RST -m limit --limit 2/s --limit-burst 2 -j ACCEPT
/sbin/iptables -A INPUT -p tcp --tcp-flags RST RST -j DROP
### 10: Limit new TCP connections per second per source IP ###
/sbin/iptables -A INPUT -p tcp -m conntrack --ctstate NEW -m limit --limit 60/s --limit-burst 20 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -m conntrack --ctstate NEW -j DROP
### 11: Use SYNPROXY on all ports (disables connection limiting rule) ###
#/sbin/iptables -t raw -A PREROUTING -p tcp -m tcp --syn -j CT --notrack
#/sbin/iptables -A INPUT -p tcp -m tcp -m conntrack --ctstate INVALID,UNTRACKED -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
#/sbin/iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
### SSH brute-force protection ###
/sbin/iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -m recent --set
/sbin/iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 10 -j DROP
### Protection against port scanning ###
/sbin/iptables -N port-scanning
/sbin/iptables -A port-scanning -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s --limit-burst 2 -j RETURN
/sbin/iptables -A port-scanning -j DROP
并在 php fpm 中使用它
; Set appropriate permissions for Unix sockets
listen.mode = 0660
; Use a dynamic process manager
pm = dynamic
; Adjust the maximum number of child processes
pm.max_children = 50
; Start with a moderate number of servers
pm.start_servers = 10
; Keep a few spare servers to handle increased load
pm.min_spare_servers = 5
pm.max_spare_servers = 15
; Set a reasonable memory limit for PHP processes
php_value[memory_limit] = 256M
; Increase the maximum execution time for PHP scripts
php_value[max_execution_time] = 600
答案2
也许 IDPS 使用 suricata。Suricata 对数据包进行深度检查,并且可以与 cf 集成
#!/bin/bash
# Cloudflare API credentials
CF_API_KEY="your_api_key"
CF_API_EMAIL="[email protected]"
CF_ZONE_ID="your_zone_id"
# Suricata log path
SURICATA_LOG_PATH="/var/log/suricata/eve.json"
# Install Suricata
sudo apt-get update
sudo apt-get install suricata -y
# Enable Suricata service
sudo systemctl enable suricata
sudo systemctl start suricata
# Backup the original Suricata configuration file
sudo cp /etc/suricata/suricata.yaml /etc/suricata/suricata.yaml.bak
# Modify Suricata configuration for basic setup
sudo sed -i 's|#default-rule-path: /etc/suricata/rules|default-rule-path: /etc/suricata/rules|' /etc/suricata/suricata.yaml
sudo sed -i 's|#rule-files:|rule-files:|' /etc/suricata/suricata.yaml
sudo sed -i 's|# - suricata.rules| - suricata.rules|' /etc/suricata/suricata.yaml
# Download a basic set of Suricata rules
sudo suricata-update enable-source et/open
sudo suricata-update
# Restart Suricata to apply changes
sudo systemctl restart suricata
# Configure Logpush for Suricata logs with Cloudflare
echo "cloudflare:
token: $CF_API_KEY
email: $CF_API_EMAIL
zone: $CF_ZONE_ID
dataset: suricata" | sudo tee -a /etc/suricata/suricata.yaml
# Restart Suricata to apply Logpush configuration
sudo systemctl restart suricata
echo "Suricata installed, configured, and integrated with Cloudflare DDoS prevention."
然后你可以配置一些IDPS规则
#!/bin/bash
# Cloudflare API credentials
CF_API_KEY="your_api_key"
CF_API_EMAIL="[email protected]"
CF_ZONE_ID="your_zone_id"
# Path to Suricata custom rules directory
CUSTOM_RULES_DIR="/etc/suricata/rules/custom"
# Create custom rules directory if it doesn't exist
sudo mkdir -p "$CUSTOM_RULES_DIR"
# Create a custom rule file (e.g., custom-ddos.rules)
sudo tee "$CUSTOM_RULES_DIR/custom-ddos.rules" > /dev/null <<'EOF'
# Custom rule to detect and log potential DDoS-like behavior and notify Cloudflare
alert ip any any -> any any (msg:"Potential DDoS-like Behavior"; \
threshold: type limit, track by_src, seconds 60, count 100, \
sid:1000001; \
flowbits: set,ddos_detected; \
flowbits: set,cf_notify;)
alert ip any any -> any any (msg:"DDoS Detection: Log and Notify Cloudflare"; \
flowbits: isset,ddos_detected; \
flowbits: isset,cf_notify; \
metadata: cloudflare_rule_id 123456; \
flowbits: unset,cf_notify; \
flowbits: unset,ddos_detected; \
flowbits: set,cf_logged; \
sid:1000002;)
EOF
# Restart Suricata to apply the new rule
sudo systemctl restart suricata
echo "Custom DDoS prevention rule added."
# Logpush endpoint
LOGPUSH_ENDPOINT="https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/logpush/jobs"
# Log to Cloudflare when DDoS-like behavior is detected
tail -n0 -F /var/log/suricata/eve.json | while read -r line; do
if echo "$line" | grep -q "flowbits: set,cf_logged"; then
continue
fi
if echo "$line" | grep -q "flowbits: set,ddos_detected"; then
# Log to Cloudflare using Logpush API
curl -X POST "$LOGPUSH_ENDPOINT" \
-H "X-Auth-Key: $CF_API_KEY" \
-H "X-Auth-Email: $CF_API_EMAIL" \
-H "Content-Type: application/json" \
--data-raw "{\"fields\": {\"message\": \"$line\"}}"
# Set flowbit to indicate that the log has been sent to Cloudflare
sudo echo "$line" | suricatasc -c "flowbit-set,cf_logged"
fi
done
您还可以添加针对 http 流量的规则
alert tcp any any -> $HOME_NET 80 (msg: "Possible DDoS attack"; flags: S; flow: stateless; threshold: type both, track by_dst, count 200, seconds 1; sid:1000001; rev:1; \
flowbits: set,ddos_detected; flowbits: set,cf_notify; flowbits: set,drop_traffic;)