diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index 26b56cc72..a175e38da 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -166,3 +166,7 @@ CutWebsiteLogSuccess: "{{ .name }} website log cut successfully, backup path {{ #toolbox ErrNotExistUser: "The current user does not exist. Please modify and retry!" ErrBanAction: "Setting failed, the current {{ .name }} service is unavailable, please check and try again!" + +#waf +ErrScope: "Modification of this configuration is not supported" +ErrStateChange: "State modification failed" \ No newline at end of file diff --git a/backend/i18n/lang/zh-Hant.yaml b/backend/i18n/lang/zh-Hant.yaml index df28f8727..a5293e109 100644 --- a/backend/i18n/lang/zh-Hant.yaml +++ b/backend/i18n/lang/zh-Hant.yaml @@ -167,3 +167,7 @@ CutWebsiteLogSuccess: "{{ .name }} 網站日誌切割成功,備份路徑 {{ .p #toolbox ErrNotExistUser: "當前使用者不存在,請修改後重試!" ErrBanAction: "設置失敗,當前 {{ .name }} 服務不可用,請檢查後重試!" + +#waf +ErrScope: "不支援修改此配置" +ErrStateChange: "狀態修改失敗" diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index c6d0e132b..9594919cf 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -165,4 +165,8 @@ CutWebsiteLogSuccess: "{{ .name }} 网站日志切割成功,备份路径 {{ .p #toolbox ErrNotExistUser: "当前用户不存在,请修改后重试!" -ErrBanAction: "设置失败,当前 {{ .name }} 服务不可用,请检查后重试!" \ No newline at end of file +ErrBanAction: "设置失败,当前 {{ .name }} 服务不可用,请检查后重试!" + +#waf +ErrScope: "不支持修改此配置" +ErrStateChange: "状态修改失败" \ No newline at end of file diff --git a/frontend/src/components/config-card/index.vue b/frontend/src/components/config-card/index.vue new file mode 100644 index 000000000..dd63e4d59 --- /dev/null +++ b/frontend/src/components/config-card/index.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 6f9475743..07d70f665 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -2157,7 +2157,7 @@ const message = { request: 'request', count4xx: '4xx quantity', count5xx: '5xx quantity', - todayStatus: 'Today status', + todayStatus: 'Today Status', reqMap: 'Request map (30 days)', resource: 'source', count: 'Quantity', @@ -2167,6 +2167,45 @@ const message = { interceptCount: 'Interception number', requestTrends: 'Request Trends (7 days)', interceptTrends: 'Intercept Trends (7 days)', + whiteList: 'whitelist', + blackList: 'blacklist', + ipBlackListHelper: 'IPs in the blacklist cannot access the website', + ipWhiteListHelper: 'IPs in the whitelist are not restricted by any rules', + uaBlackListHelper: 'Requests carrying User-Agent in the blacklist will be intercepted', + uaWhiteListHelper: 'Requests carrying User-Agent in the whitelist are not restricted by any rules', + urlBlackListHelper: 'Requests for URLs in the blacklist will be intercepted', + urlWhiteListHelper: 'Requests for URLs in the whitelist are not restricted by any rules', + ccHelper: + 'If any website has been requested more than {1} times in {0} seconds, this IP will be blocked for {2} seconds', + blockTime: 'Block time', + attackHelper: + 'The cumulative interception exceeds {1} times within {0} seconds, block this IP for {2} seconds', + notFoundHelper: + 'The cumulative request returned 404 more than {1} times within {0} seconds, block this IP for {2} seconds', + frequencyLimit: 'frequency limit', + regionLimit: 'region limit', + defaultRule: 'Default rule', + accessFrequencyLmit: 'Access frequency limit', + attackLimit: 'Attack frequency limit', + notFoundLimit: '404 frequency limit', + urlLimit: 'URL frequency limit', + urlLimitHelper: 'Set access frequency for a single URL', + sqliDefense: 'SQL injection defense', + sqliHelper: 'Identify SQL injection in requests and intercept', + xssHelper: 'Identify XSS in the request and intercept it', + xssDefense: 'XSS Defense', + uaDefense: 'Malicious User-Agent Rule', + uaHelper: 'Contains common malicious crawler rules', + argsDefense: 'Malicious parameter rules', + argsHelper: 'Prohibit malicious parameters in requests', + cookieDefense: 'Malicious Cookie Rule', + cookieHelper: 'Prohibit malicious cookies from being carried in requests', + headerDefense: 'Malicious Header Rule', + headerHelper: 'Prohibit requests from containing malicious headers', + httpRule: 'HTTP request method rules', + httpHelper: 'Restrict the request method type of the website', + geoRule: 'Regional access restrictions', + geoHelper: 'Restrict access to your website from certain regions', }, monitor: { name: 'Website Monitoring', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 907a75814..01930af17 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -2025,6 +2025,42 @@ const message = { interceptCount: '攔截數', requestTrends: '請求趨勢(7天)', interceptTrends: '攔截趨勢(7天)', + whiteList: '白名單', + blackList: '黑名單', + ipBlackListHelper: '黑名單中的 IP 無法存取網站', + ipWhiteListHelper: '白名單中的 IP 不受任何規則限制', + uaBlackListHelper: '攜帶黑名單中的 User-Agent 的請求將被攔截', + uaWhiteListHelper: '攜帶白名單中的 User-Agent 的請求不受任何規則限制', + urlBlackListHelper: '請求黑名單中的 URL 將被攔截', + urlWhiteListHelper: '請求白名單中的 URL 請求不受任何規則限制', + ccHelper: '{0} 秒內累積請求任意網站超過 {1} 次,封鎖此 IP {2} 秒', + blockTime: '封鎖時間', + attackHelper: '{0} 秒內累計攔截超過 {1} 次,封鎖此 IP {2} 秒', + notFoundHelper: '{0} 秒內累計請求回傳 404 超過 {1} 次,封鎖此 IP {2} 秒', + frequencyLimit: '頻率限制', + regionLimit: '地區限制', + defaultRule: '預設規則', + accessFrequencyLmit: '存取頻率限制', + attackLimit: '攻擊頻率限制', + notFoundLimit: '404 頻率限制', + urlLimit: 'URL 頻率限制', + urlLimitHelper: '為單一 URL 設定存取頻率', + sqliDefense: 'SQL 注入防禦', + sqliHelper: '辨識請求中的 SQL 注入並攔截', + xssHelper: '辨識請求中的 XSS 並攔截', + xssDefense: 'XSS 防禦', + uaDefense: '惡意 User-Agent 規則', + uaHelper: '包含常見的惡意爬蟲規則', + argsDefense: '惡意參數規則', + argsHelper: '在禁止請求中攜帶惡意參數', + cookieDefense: '惡意 Cookie 規則', + cookieHelper: '禁止請求中攜帶惡意 Cookie', + headerDefense: '惡意 Header 規則', + headerHelper: '禁止請求中攜帶惡意 Header', + httpRule: 'HTTP 請求方法規則', + httpHelper: '限制網站的請求方法類型', + geoRule: '地區存取限制', + geoHelper: '限制某些地區造訪你的網站', }, monitor: { name: '網站監控', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index fd39316b4..40867161a 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -2026,6 +2026,42 @@ const message = { interceptCount: '拦截数', requestTrends: '请求趋势(7日)', interceptTrends: '拦截趋势(7日)', + whiteList: '白名单', + blackList: '黑名单', + ipBlackListHelper: '黑名单中的 IP 无法访问网站', + ipWhiteListHelper: '白名单中的 IP 不受任何规则限制', + uaBlackListHelper: '携带黑名单中的 User-Agent 的请求将被拦截', + uaWhiteListHelper: '携带白名单中的 User-Agent 的请求不受任何规则限制', + urlBlackListHelper: '请求黑名单中的 URL 将被拦截', + urlWhiteListHelper: '请求白名单中的 URL 请求不受任何规则限制', + ccHelper: '{0} 秒内累计请求任意网站超过 {1} 次,封锁此 IP {2} 秒', + blockTime: '封禁时间', + attackHelper: '{0} 秒内累计拦截超过 {1} 次,封锁此 IP {2} 秒', + notFoundHelper: '{0} 秒内累计请求返回 404 超过 {1} 次,封锁此 IP {2} 秒', + frequencyLimit: '频率限制', + regionLimit: '地区限制', + defaultRule: '默认规则', + accessFrequencyLmit: '访问频率限制', + attackLimit: '攻击频率限制', + notFoundLimit: '404 频率限制', + urlLimit: 'URL 频率限制', + urlLimitHelper: '为单个 URL 设置访问频率', + sqliDefense: 'SQL 注入防御', + sqliHelper: '识别请求中的 SQL 注入并拦截', + xssHelper: '识别请求中的 XSS 并拦截', + xssDefense: 'XSS 防御', + uaDefense: '恶意 User-Agent 规则', + uaHelper: '包含常见的恶意爬虫规则', + argsDefense: '恶意参数规则', + argsHelper: '禁止请求中携带恶意参数', + cookieDefense: '恶意 Cookie 规则', + cookieHelper: '禁止请求中携带恶意 Cookie', + headerDefense: '恶意 Header 规则', + headerHelper: '禁止请求中携带恶意 Header', + httpRule: 'HTTP 请求方法规则', + httpHelper: '限制网站的请求方法类型', + geoRule: '地区访问限制', + geoHelper: '限制某些地区访问你的网站', }, monitor: { name: '网站监控', diff --git a/plugins/openresty/waf/conf/global.json b/plugins/openresty/waf/conf/global.json index a50d57ef4..2c36408b5 100644 --- a/plugins/openresty/waf/conf/global.json +++ b/plugins/openresty/waf/conf/global.json @@ -117,7 +117,7 @@ "action": "deny", "code": 403, "type": "fileExtCheck", - "extList": [ + "rules": [ "php", "jsp", "asp", diff --git a/plugins/openresty/waf/lib/lib.lua b/plugins/openresty/waf/lib/lib.lua index 96bc74cae..936251775 100644 --- a/plugins/openresty/waf/lib/lib.lua +++ b/plugins/openresty/waf/lib/lib.lua @@ -531,7 +531,7 @@ function _M.post_check() local match = ngx_re_match(m[0], 'Content-Disposition: form-data; (.+)filename="(.+)\\.(.*)"', 'ijo') if match then local extension = match[3] - for _, ext in ipairs(rule.extList) do + for _, ext in ipairs(rule.rules) do if extension == ext then exec_action(rule) end diff --git a/plugins/openresty/waf/lib/utils.lua b/plugins/openresty/waf/lib/utils.lua index b1dc1f299..b7d163fe3 100644 --- a/plugins/openresty/waf/lib/utils.lua +++ b/plugins/openresty/waf/lib/utils.lua @@ -1,3 +1,4 @@ +local geoip = require "geoip" local sub_str = string.sub local pairs = pairs local insert_table = table.insert @@ -6,6 +7,7 @@ local ipairs = ipairs local type = type local find_str = string.find local gmatch_str = string.gmatch +local cjson = require "cjson" local _M = {} @@ -114,6 +116,32 @@ function _M.get_real_ip() return "unknown" end +function _M.get_geo_ip(ip) + if _M.is_intranet_address(ip) then + return { + country = { ["zh"] = "内网", ["en"] = "intranet" }, + province = { ["zh"] = "内网", ["en"] = "intranet" }, + city = { ["zh"] = "内网", ["en"] = "intranet" }, + longitude = 0, + latitude = 0, + iso = "local" + } + else + geoip.init() + local geo_res = geoip.lookup(ip) + local msg = "访问 IP " .. ip + if geo_res.country then + msg = msg .. " 国家 " .. cjson.encode(geo_res.country) + end + if geo_res.province then + msg = msg .. " 省份 " .. cjson.encode(geo_res.province) + end + ngx.log(ngx.ERR, msg) + return geo_res + + end +end + function _M.get_header(headerKey) return ngx.req.get_headers(20000)[headerKey] end diff --git a/plugins/openresty/waf/log_and_traffic.lua b/plugins/openresty/waf/log_and_traffic.lua index 7ce81e4aa..a485e40e8 100644 --- a/plugins/openresty/waf/log_and_traffic.lua +++ b/plugins/openresty/waf/log_and_traffic.lua @@ -215,6 +215,16 @@ if config.is_waf_on() then count_not_found() local is_attack = ngx.ctx.is_attack + if not ngx.ctx.ip then + ngx.ctx.ip = utils.get_real_ip() + ngx.ctx.geoip = utils.get_geo_ip(ngx.ctx.ip) + local ua = utils.get_header("user-agent") + if not ua then + ua = "" + end + end + + local wafdb = utils.get_wafdb(config.waf_db_path) if wafdb ~= nil then count_req_status(wafdb,is_attack) diff --git a/plugins/openresty/waf/waf.lua b/plugins/openresty/waf/waf.lua index 96d0dadbb..39acc1ddd 100644 --- a/plugins/openresty/waf/waf.lua +++ b/plugins/openresty/waf/waf.lua @@ -42,20 +42,7 @@ local function get_website_key() return s_name end -local function get_geo_ip(ip) - if utils.is_intranet_address(ip) then - return { - country = { ["zh"] = "内网", ["en"] = "intranet" }, - province = { ["zh"] = "内网", ["en"] = "intranet" }, - city = { ["zh"] = "内网", ["en"] = "intranet" }, - longitude = 0, - latitude = 0, - iso = "local" - } - else - return geoip.lookup(ip) - end -end + local function init() local ip = utils.get_real_ip() @@ -64,21 +51,10 @@ local function init() if not ua then ua = "" end - - geoip.init() ngx.ctx.ua = ua - ngx.ctx.geoip = get_geo_ip(ip) - - local msg = "访问 IP " .. ip - if ngx.ctx.geoip.country then - msg = msg .. " 国家 " .. cjson.encode(ngx.ctx.geoip.country) - end - if ngx.ctx.geoip.province then - msg = msg .. " 省份 " .. cjson.encode(ngx.ctx.geoip.province) - end - ngx.log(ngx.ERR, msg) - + ngx.ctx.geoip = utils.get_geo_ip(ip) + ngx.ctx.website_key = get_website_key() ngx.ctx.method = ngx.req.get_method() ngx.ctx.content_type = utils.get_header("content-type") @@ -144,8 +120,6 @@ local function waf_api() end end -ngx.log(ngx.ERR,"access waf") - if config.is_waf_on() then init() waf_api()