openresty整合modsecurity实现简单的防止DDOS攻击

openresty配置

确保已经正确整合openresty和modsecurity
完整配置如下,增加了白名单和黑名单已经限制请求次数

load_module modules/ngx_http_modsecurity_module.so;
#user  nobody;
worker_processes  4;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
	underscores_in_headers on;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip on;  # 启用 Gzip 压缩
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_vary on;  # 向响应头添加 `Vary: Accept-Encoding`,以确保代理缓存的正确性
    gzip_min_length 1024;  # 设置压缩的最小文件大小,较小的文件可能不压缩
    gzip_proxied any;  # 启用代理后端的压缩响应
    gzip_comp_level 5;  # 设置压缩级别,范围是1-9,数值越大压缩比越高,但CPU消耗也更大


    geo $whitelist {
        default 0;               # 默认不是白名单中的IP
        #10.0.0.0/8 1;            # 白名单IP段
        #192.168.1.100 1;         # 白名单单个IP
    }

    geo $blacklist {
        default 0;               # 默认不是黑名单中的IP
        #192.168.1.0/24 1;        # 黑名单IP段
        #203.0.113.50 1;          # 黑名单单个IP
    }

    # 将白名单和黑名单组合起来判断
    map $whitelist$blacklist $access {
        "10" 0;                  # 在白名单中时,即使在黑名单中,仍然允许访问
        "11" 0;                  # 在白名单中时,即使在黑名单中,仍然允许访问
        "01" 1;                  # 不在白名单中,但在黑名单中,拒绝访问
        "00" 0;                  # 不在白名单或黑名单中,允许访问(默认行为,可以改为1表示拒绝)
    }

	# 调整为50MB的限流区,允许每秒10个请求,突发请求允许20个根据IP地址的数量自行调整限流区
	limit_req_zone $binary_remote_addr zone=req_zone:50m rate=10r/s;
	# 定义一个连接限制区,大小为10MB,最大连接数为10个
    limit_conn_zone $binary_remote_addr zone=conn_zone:50m;
	
	log_format custom '$remote_addr $host  $proxy_add_x_forwarded_for - $remote_user [$time_local] "$request" '
				   '$status $body_bytes_sent "$http_referer" '
				   '"$http_user_agent" "$http_x_forwarded_for"';

	access_log /app/openresty/nginx/logs/access.log custom;

    server {
        listen       8080;
        server_name  0.0.0.0;

        charset utf-8;
		# 在特定位置启用 ModSecurity
		modsecurity on;
		modsecurity_rules_file /app/openresty/nginx/conf/modsecurity.conf;

        #access_log  logs/host.access.log  main;
		if ($access) {
            return 403;          # 如果$access为1,则拒绝访问
        }

        location / {
            # 应用请求限流
            limit_req zone=req_zone burst=20 nodelay;

            # 应用连接限制
            limit_conn conn_zone 10;

			proxy_pass http://127.0.0.1:9080;
			proxy_set_header X-Real-IP $remote_addr;
			proxy_set_header X-Forwarded-Host $host;
			proxy_set_header X-Forwarded-Port $server_port;
			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
			proxy_set_header X-Forwarded-Proto $scheme;
			# 关键部分:重写后端服务器的重定向URL
			proxy_redirect default;
			
			# 错误处理
            error_page 503 /error503.html;

        }
		location = /error503.html {
            internal;
            default_type text/html;
            return 503 "Service Unavailable";
        }
    }

}

配置modsecurity

修改modsecurity.conf添加规则

Include /app/openresty/nginx/modsec/crs/crs-setup.conf
Include /app/openresty/nginx/modsec/crs/rules/*.conf
# -- Rule engine initialization ----------------------------------------------

# Enable ModSecurity, attaching it to every transaction. Use detection
# only to start with, because that minimises the chances of post-installation
# disruption.
#
SecRuleEngine on


# -- Request body handling ---------------------------------------------------

# Allow ModSecurity to access request bodies. If you don't, ModSecurity
# won't be able to see any POST parameters, which opens a large security
# hole for attackers to exploit.
#
SecRequestBodyAccess On


# Enable XML request body parser.
# Initiate XML Processor in case of xml content-type
#
SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \
     "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"

# Enable JSON request body parser.
# Initiate JSON Processor in case of JSON content-type; change accordingly
# if your application does not use 'application/json'
#
SecRule REQUEST_HEADERS:Content-Type "^application/json" \
     "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"

# Sample rule to enable JSON request body parser for more subtypes.
# Uncomment or adapt this rule if you want to engage the JSON
# Processor for "+json" subtypes
#
#SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \
#     "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"

# Maximum request body size we will accept for buffering. If you support
# file uploads then the value given on the first line has to be as large
# as the largest file you are willing to accept. The second value refers
# to the size of data, with files excluded. You want to keep that value as
# low as practical.
#
SecRequestBodyLimit 104857600
SecRequestBodyNoFilesLimit 52428800

# What to do if the request body size is above our configured limit.
# Keep in mind that this setting will automatically be set to ProcessPartial
# when SecRuleEngine is set to DetectionOnly mode in order to minimize
# disruptions when initially deploying ModSecurity.
#
SecRequestBodyLimitAction Reject

# Maximum parsing depth allowed for JSON objects. You want to keep this
# value as low as practical.
#
SecRequestBodyJsonDepthLimit 512

# Maximum number of args allowed per request. You want to keep this
# value as low as practical. The value should match that in rule 200007.
SecArgumentsLimit 1000

# If SecArgumentsLimit has been set, you probably want to reject any
# request body that has only been partly parsed. The value used in this
# rule should match what was used with SecArgumentsLimit
SecRule &ARGS "@ge 1000" \
"id:'200007', phase:2,t:none,log,deny,status:400,msg:'Failed to fully parse request body due to large argument count',severity:2"

# Verify that we've correctly processed the request body.
# As a rule of thumb, when failing to process a request body
# you should reject the request (when deployed in blocking mode)
# or log a high-severity alert (when deployed in detection-only mode).
#
SecRule REQBODY_ERROR "!@eq 0" \
"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"

# By default be strict with what we accept in the multipart/form-data
# request body. If the rule below proves to be too strict for your
# environment consider changing it to detection-only. You are encouraged
# _not_ to remove it altogether.
#
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
"id:'200003',phase:2,t:none,log,deny,status:400, \
msg:'Multipart request body failed strict validation: \
PE %{REQBODY_PROCESSOR_ERROR}, \
BQ %{MULTIPART_BOUNDARY_QUOTED}, \
BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
DB %{MULTIPART_DATA_BEFORE}, \
DA %{MULTIPART_DATA_AFTER}, \
HF %{MULTIPART_HEADER_FOLDING}, \
LF %{MULTIPART_LF_LINE}, \
SM %{MULTIPART_MISSING_SEMICOLON}, \
IQ %{MULTIPART_INVALID_QUOTING}, \
IP %{MULTIPART_INVALID_PART}, \
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"

# Did we see anything that might be a boundary?
#
# Here is a short description about the ModSecurity Multipart parser: the
# parser returns with value 0, if all "boundary-like" line matches with
# the boundary string which given in MIME header. In any other cases it returns
# with different value, eg. 1 or 2.
#
# The RFC 1341 descript the multipart content-type and its syntax must contains
# only three mandatory lines (above the content):
# * Content-Type: multipart/mixed; boundary=BOUNDARY_STRING
# * --BOUNDARY_STRING
# * --BOUNDARY_STRING--
#
# First line indicates, that this is a multipart content, second shows that
# here starts a part of the multipart content, third shows the end of content.
#
# If there are any other lines, which starts with "--", then it should be
# another boundary id - or not.
#
# After 3.0.3, there are two kinds of types of boundary errors: strict and permissive.
#
# If multipart content contains the three necessary lines with correct order, but
# there are one or more lines with "--", then parser returns with value 2 (non-zero).
#
# If some of the necessary lines (usually the start or end) misses, or the order
# is wrong, then parser returns with value 1 (also a non-zero).
#
# You can choose, which one is what you need. The example below contains the
# 'strict' mode, which means if there are any lines with start of "--", then
# ModSecurity blocked the content. But the next, commented example contains
# the 'permissive' mode, then you check only if the necessary lines exists in
# correct order. Whit this, you can enable to upload PEM files (eg "----BEGIN.."),
# or other text files, which contains eg. HTTP headers.
#
# The difference is only the operator - in strict mode (first) the content blocked
# in case of any non-zero value. In permissive mode (second, commented) the
# content blocked only if the value is explicit 1. If it 0 or 2, the content will
# allowed.
#

#
# See #1747 and #1924 for further information on the possible values for
# MULTIPART_UNMATCHED_BOUNDARY.
#
SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \
    "id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"


# PCRE Tuning
# We want to avoid a potential RegEx DoS condition
#
SecPcreMatchLimit 1000
SecPcreMatchLimitRecursion 1000

# Some internal errors will set flags in TX and we will need to look for these.
# All of these are prefixed with "MSC_".  The following flags currently exist:
#
# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded.
#
SecRule TX:/^MSC_/ "!@streq 0" \
        "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"


# -- Response body handling --------------------------------------------------

# Allow ModSecurity to access response bodies. 
# You should have this directive enabled in order to identify errors
# and data leakage issues.
# 
# Do keep in mind that enabling this directive does increases both
# memory consumption and response latency.
#
SecResponseBodyAccess On

# Which response MIME types do you want to inspect? You should adjust the
# configuration below to catch documents but avoid static files
# (e.g., images and archives).
#
SecResponseBodyMimeType text/plain text/html text/xml

# Buffer response bodies of up to 512 KB in length.
SecResponseBodyLimit 524288

# What happens when we encounter a response body larger than the configured
# limit? By default, we process what we have and let the rest through.
# That's somewhat less secure, but does not break any legitimate pages.
#
SecResponseBodyLimitAction ProcessPartial


# -- Filesystem configuration ------------------------------------------------

# The location where ModSecurity stores temporary files (for example, when
# it needs to handle a file upload that is larger than the configured limit).
# 
# This default setting is chosen due to all systems have /tmp available however, 
# this is less than ideal. It is recommended that you specify a location that's private.
#
SecTmpDir /app/openresty/nginx/sec_temp

# The location where ModSecurity will keep its persistent data.  This default setting 
# is chosen due to all systems have /tmp available however, it
# too should be updated to a place that other users can't access.
#
SecDataDir /app/openresty/nginx/sec_data


# -- File uploads handling configuration -------------------------------------

# The location where ModSecurity stores intercepted uploaded files. This
# location must be private to ModSecurity. You don't want other users on
# the server to access the files, do you?
#
#SecUploadDir /opt/modsecurity/var/upload/

# By default, only keep the files that were determined to be unusual
# in some way (by an external inspection script). For this to work you
# will also need at least one file inspection rule.
#
#SecUploadKeepFiles RelevantOnly

# Uploaded files are by default created with permissions that do not allow
# any other user to access them. You may need to relax that if you want to
# interface ModSecurity to an external program (e.g., an anti-virus).
#
#SecUploadFileMode 0600


# -- Debug log configuration -------------------------------------------------

# The default debug log configuration is to duplicate the error, warning
# and notice messages from the error log.
#
SecDebugLog /app/openresty/nginx/logs/modsec_debug.log
SecDebugLogLevel 1


# -- Audit log configuration -------------------------------------------------

# Log the transactions that are marked by a rule, as well as those that
# trigger a server error (determined by a 5xx or 4xx, excluding 404,  
# level response status codes).
#
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"

# Log everything we know about a transaction.
SecAuditLogParts ABIJDEFHZ

# Use a single file for logging. This is much easier to look at, but
# assumes that you will use the audit log only ocassionally.
#
SecAuditLogType Serial
SecAuditLog /app/openresty/nginx/logs/modsec_audit.log

# Specify the path for concurrent audit logging.
#SecAuditLogStorageDir /opt/modsecurity/var/audit/


# -- Miscellaneous -----------------------------------------------------------

# Use the most commonly used application/x-www-form-urlencoded parameter
# separator. There's probably only one application somewhere that uses
# something else so don't expect to change this value.
#
SecArgumentSeparator &

# Settle on version 0 (zero) cookies, as that is what most applications
# use. Using an incorrect cookie version may open your installation to
# evasion attacks (against the rules that examine named cookies).
#
SecCookieFormat 0

# Specify your Unicode Code Point.
# This mapping is used by the t:urlDecodeUni transformation function
# to properly map encoded data to your language. Properly setting
# these directives helps to reduce false positives and negatives.
#
SecUnicodeMapFile unicode.mapping 20127

# Improve the quality of ModSecurity by sharing information about your
# current ModSecurity version and dependencies versions.
# The following information will be shared: ModSecurity version,
# Web Server version, APR version, PCRE version, Lua version, Libxml2
# version, Anonymous unique id for host.
# NB: As of April 2022, there is no longer any advantage to turning this
# setting On, as there is no active receiver for the information.
SecStatusEngine Off

# 设置请求体大小限制,防止大请求DDoS攻击
SecRequestBodyLimit 31457280  # 设置请求体最大大小为30MB
SecRequestBodyNoFilesLimit 31457280  # 非文件请求体限制为30MB

# 1. 设置全局变量
SecAction "id:900000,phase:1,nolog,pass,t:none,setvar:tx.ddos_protection_threshold=200" # 每分钟允许的最大请求数。
SecAction "id:900001,phase:1,nolog,pass,t:none,setvar:tx.ddos_protection_burst=300" #允许的突发请求数量。
SecAction "id:900002,phase:1,nolog,pass,t:none,setvar:tx.ddos_protection_block_time=600" # IP 被封禁的时间(秒)。
SecAction "id:900003,phase:1,nolog,pass,t:none,setvar:tx.ddos_connection_threshold=100" #每个 IP 允许的最大并发连接数。
SecAction "id:900004,phase:1,nolog,pass,t:none,setvar:tx.ddos_connection_burst=150" # 允许的最大并发连接数的突发数量。
SecAction "id:900005,phase:1,nolog,pass,t:none,setvar:tx.dos_counter_expire_time=60" # DDoS 请求计数器的过期时间(秒)。
SecAction "id:900006,phase:1,nolog,pass,t:none,setvar:tx.connection_counter_expire_time=60" # 并发连接计数器的过期时间(秒)


# 2. 基于请求频率的防护(每个 IP 每分钟最大请求数)
SecRule IP:DOS_COUNTER "@gt %{tx.ddos_protection_threshold}" \
    "id:900100,phase:2,log,deny,status:429,msg:'DDoS Attack Detected: Exceeded request threshold',chain"
    SecRule IP:DOS_COUNTER "@le %{tx.ddos_protection_burst}" \
    "setvar:ip.ddos_block=1,expirevar:ip.ddos_block=%{tx.ddos_protection_block_time},setvar:!ip.dos_counter"

# 3. 每次访问增加 IP 计数器并记录日志
SecRule REMOTE_ADDR "^.*$" \
    "id:900101,phase:2,log,pass,setvar:ip.dos_counter=+1,expirevar:ip.dos_counter=%{tx.dos_counter_expire_time},msg:'IP Address: %{REMOTE_ADDR}, DOS_COUNTER: %{ip.dos_counter}'"

# 4. 阻止已被标记为恶意的 IP
SecRule IP:DDOS_BLOCK "@eq 1" \
    "id:900102,phase:2,deny,status:403,log,msg:'Blocked IP due to suspected DDoS activity'"

# 5. 基于并发连接数的防护(每个 IP 的最大并发连接数)
SecRule IP:CONNECTION_COUNTER "@gt %{tx.ddos_connection_threshold}" \
    "id:900200,phase:2,deny,status:429,log,msg:'DDoS Attack Detected: Exceeded connection threshold',chain"
    SecRule IP:CONNECTION_COUNTER "@le %{tx.ddos_connection_burst}" \
    "setvar:ip.ddos_block=1,expirevar:ip.ddos_block=%{tx.ddos_protection_block_time},setvar:!ip.connection_counter"

# 6. 每次连接增加 IP 并发连接计数器
SecRule REMOTE_ADDR "^.*$" \
    "id:900201,phase:2,nolog,pass,setvar:ip.connection_counter=+1,expirevar:ip.connection_counter=%{tx.connection_counter_expire_time}"

# 7. 阻止已被标记为恶意的 IP(并发连接数)
SecRule IP:DDOS_BLOCK "@eq 1" \
    "id:900202,phase:2,deny,status:403,log,msg:'Blocked IP due to excessive connections'"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值