mirror of
https://github.com/Rayzggz/server_torii.git
synced 2025-06-17 20:51:22 +08:00
feat: HTTP FLOOD Speed Limit
add shared memory
This commit is contained in:
@ -25,7 +25,7 @@ type HCaptchaResponse struct {
|
|||||||
ErrorCodes []string `json:"error-codes"`
|
ErrorCodes []string `json:"error-codes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Captcha(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision) {
|
func Captcha(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision, sharedMem *dataType.SharedMemory) {
|
||||||
if !reqData.Captcha {
|
if !reqData.Captcha {
|
||||||
decision.Set(action.Continue)
|
decision.Set(action.Continue)
|
||||||
return
|
return
|
||||||
|
35
internal/check/HTTPFlood.go
Normal file
35
internal/check/HTTPFlood.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"server_torii/internal/action"
|
||||||
|
"server_torii/internal/config"
|
||||||
|
"server_torii/internal/dataType"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HTTPFlood(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision, sharedMem *dataType.SharedMemory) {
|
||||||
|
ipKey := reqData.RemoteIP
|
||||||
|
sharedMem.HTTPFloodSpeedLimitCounter.Add(ipKey, 1)
|
||||||
|
|
||||||
|
uriKey := reqData.RemoteIP + "|" + reqData.Uri
|
||||||
|
sharedMem.HTTPFloodSameURILimitCounter.Add(uriKey, 1)
|
||||||
|
|
||||||
|
for window, limit := range ruleSet.HTTPFloodRule.HTTPFloodSpeedLimit {
|
||||||
|
if sharedMem.HTTPFloodSpeedLimitCounter.Query(ipKey, window) > limit {
|
||||||
|
log.Printf("HTTPFlood rate limit exceeded: IP %s, window %d, limit %d", ipKey, window, limit)
|
||||||
|
//decision.SetResponse(action.Done, []byte("403"), nil)
|
||||||
|
decision.Set(action.Continue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for window, limit := range ruleSet.HTTPFloodRule.HTTPFloodSameURILimit {
|
||||||
|
if sharedMem.HTTPFloodSameURILimitCounter.Query(uriKey, window) > limit {
|
||||||
|
log.Printf("HTTPFlood URI rate limit exceeded: IP %s, URI %s, window %d, limit %d", ipKey, reqData.Uri, window, limit)
|
||||||
|
//decision.SetResponse(action.Done, []byte("403"), nil)
|
||||||
|
decision.Set(action.Continue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decision.Set(action.Continue)
|
||||||
|
}
|
@ -7,7 +7,7 @@ import (
|
|||||||
"server_torii/internal/dataType"
|
"server_torii/internal/dataType"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IPAllowList(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision) {
|
func IPAllowList(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision, sharedMem *dataType.SharedMemory) {
|
||||||
remoteIP := reqData.RemoteIP
|
remoteIP := reqData.RemoteIP
|
||||||
trie := ruleSet.IPAllowTrie
|
trie := ruleSet.IPAllowTrie
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"server_torii/internal/dataType"
|
"server_torii/internal/dataType"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IPBlockList(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision) {
|
func IPBlockList(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision, sharedMem *dataType.SharedMemory) {
|
||||||
remoteIP := reqData.RemoteIP
|
remoteIP := reqData.RemoteIP
|
||||||
trie := ruleSet.IPBlockTrie
|
trie := ruleSet.IPBlockTrie
|
||||||
ip := net.ParseIP(remoteIP)
|
ip := net.ParseIP(remoteIP)
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"server_torii/internal/dataType"
|
"server_torii/internal/dataType"
|
||||||
)
|
)
|
||||||
|
|
||||||
func URLAllowList(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision) {
|
func URLAllowList(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision, sharedMem *dataType.SharedMemory) {
|
||||||
url := reqData.Uri
|
url := reqData.Uri
|
||||||
list := ruleSet.URLAllowList
|
list := ruleSet.URLAllowList
|
||||||
if list.Match(url) {
|
if list.Match(url) {
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"server_torii/internal/dataType"
|
"server_torii/internal/dataType"
|
||||||
)
|
)
|
||||||
|
|
||||||
func URLBlockList(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision) {
|
func URLBlockList(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision, sharedMem *dataType.SharedMemory) {
|
||||||
url := reqData.Uri
|
url := reqData.Uri
|
||||||
list := ruleSet.URLBlockList
|
list := ruleSet.URLBlockList
|
||||||
if list.Match(url) {
|
if list.Match(url) {
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func VerifyBot(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision) {
|
func VerifyBot(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision, sharedMem *dataType.SharedMemory) {
|
||||||
ua := strings.ToLower(reqData.UserAgent)
|
ua := strings.ToLower(reqData.UserAgent)
|
||||||
|
|
||||||
var exptractRDNS []string
|
var exptractRDNS []string
|
||||||
|
@ -235,8 +235,8 @@ func loadHTTPFloodRule(file string, rule *dataType.HTTPFloodRule) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rule.HTTPFloodSpeedLimit = make(map[int]int)
|
rule.HTTPFloodSpeedLimit = make(map[int64]int64)
|
||||||
rule.HTTPFloodSameURILimit = make(map[int]int)
|
rule.HTTPFloodSameURILimit = make(map[int64]int64)
|
||||||
|
|
||||||
for _, s := range ymlRule.HTTPFloodSpeedLimit {
|
for _, s := range ymlRule.HTTPFloodSpeedLimit {
|
||||||
limit, seconds, err := utils.ParseRate(s)
|
limit, seconds, err := utils.ParseRate(s)
|
||||||
|
@ -27,9 +27,11 @@ type VerifyBotRule struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HTTPFloodRule struct {
|
type HTTPFloodRule struct {
|
||||||
HTTPFloodSpeedLimit map[int]int
|
HTTPFloodSpeedLimit map[int64]int64
|
||||||
HTTPFloodSameURILimit map[int]int
|
HTTPFloodSameURILimit map[int64]int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type SharedMemory struct {
|
type SharedMemory struct {
|
||||||
|
HTTPFloodSpeedLimitCounter *Counter
|
||||||
|
HTTPFloodSameURILimitCounter *Counter
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CheckFunc func(dataType.UserRequest, *config.RuleSet, *action.Decision)
|
type CheckFunc func(dataType.UserRequest, *config.RuleSet, *action.Decision, *dataType.SharedMemory)
|
||||||
|
|
||||||
func CheckMain(w http.ResponseWriter, userRequestData dataType.UserRequest, ruleSet *config.RuleSet, cfg *config.MainConfig) {
|
func CheckMain(w http.ResponseWriter, userRequestData dataType.UserRequest, ruleSet *config.RuleSet, cfg *config.MainConfig, sharedMem *dataType.SharedMemory) {
|
||||||
decision := action.NewDecision()
|
decision := action.NewDecision()
|
||||||
|
|
||||||
checkFuncs := make([]CheckFunc, 0)
|
checkFuncs := make([]CheckFunc, 0)
|
||||||
@ -23,10 +23,11 @@ func CheckMain(w http.ResponseWriter, userRequestData dataType.UserRequest, rule
|
|||||||
checkFuncs = append(checkFuncs, check.URLAllowList)
|
checkFuncs = append(checkFuncs, check.URLAllowList)
|
||||||
checkFuncs = append(checkFuncs, check.URLBlockList)
|
checkFuncs = append(checkFuncs, check.URLBlockList)
|
||||||
checkFuncs = append(checkFuncs, check.VerifyBot)
|
checkFuncs = append(checkFuncs, check.VerifyBot)
|
||||||
|
checkFuncs = append(checkFuncs, check.HTTPFlood)
|
||||||
checkFuncs = append(checkFuncs, check.Captcha)
|
checkFuncs = append(checkFuncs, check.Captcha)
|
||||||
|
|
||||||
for _, checkFunc := range checkFuncs {
|
for _, checkFunc := range checkFuncs {
|
||||||
checkFunc(userRequestData, ruleSet, decision)
|
checkFunc(userRequestData, ruleSet, decision, sharedMem)
|
||||||
if decision.State == action.Done {
|
if decision.State == action.Done {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// StartServer starts the HTTP server
|
// StartServer starts the HTTP server
|
||||||
func StartServer(cfg *config.MainConfig, ruleSet *config.RuleSet) error {
|
func StartServer(cfg *config.MainConfig, ruleSet *config.RuleSet, sharedMem *dataType.SharedMemory) error {
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
userRequestData := processRequestData(cfg, r)
|
userRequestData := processRequestData(cfg, r)
|
||||||
|
|
||||||
if strings.HasPrefix(userRequestData.Uri, cfg.WebPath) {
|
if strings.HasPrefix(userRequestData.Uri, cfg.WebPath) {
|
||||||
CheckTorii(w, r, userRequestData, ruleSet, cfg)
|
CheckTorii(w, r, userRequestData, ruleSet, cfg, sharedMem)
|
||||||
} else {
|
} else {
|
||||||
CheckMain(w, userRequestData, ruleSet, cfg)
|
CheckMain(w, userRequestData, ruleSet, cfg, sharedMem)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CheckTorii(w http.ResponseWriter, r *http.Request, reqData dataType.UserRequest, ruleSet *config.RuleSet, cfg *config.MainConfig) {
|
func CheckTorii(w http.ResponseWriter, r *http.Request, reqData dataType.UserRequest, ruleSet *config.RuleSet, cfg *config.MainConfig, sharedMem *dataType.SharedMemory) {
|
||||||
decision := action.NewDecision()
|
decision := action.NewDecision()
|
||||||
|
|
||||||
decision.SetCode(action.Continue, []byte("403"))
|
decision.SetCode(action.Continue, []byte("403"))
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseRate(s string) (int, int, error) {
|
func ParseRate(s string) (int64, int64, error) {
|
||||||
parts := strings.Split(s, "/")
|
parts := strings.Split(s, "/")
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return 0, 0, fmt.Errorf("unexpected rate format: %s", s)
|
return 0, 0, fmt.Errorf("unexpected rate format: %s", s)
|
||||||
@ -37,5 +37,5 @@ func ParseRate(s string) (int, int, error) {
|
|||||||
default:
|
default:
|
||||||
return 0, 0, fmt.Errorf("unexpected time unit: %s", string(unit))
|
return 0, 0, fmt.Errorf("unexpected time unit: %s", string(unit))
|
||||||
}
|
}
|
||||||
return limit, seconds, nil
|
return int64(limit), int64(seconds), nil
|
||||||
}
|
}
|
||||||
|
7
main.go
7
main.go
@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"server_torii/internal/config"
|
"server_torii/internal/config"
|
||||||
|
"server_torii/internal/dataType"
|
||||||
"server_torii/internal/server"
|
"server_torii/internal/server"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
@ -47,6 +48,10 @@ func main() {
|
|||||||
log.SetOutput(logFile)
|
log.SetOutput(logFile)
|
||||||
|
|
||||||
//allocate shared memory
|
//allocate shared memory
|
||||||
|
sharedMem := &dataType.SharedMemory{
|
||||||
|
HTTPFloodSpeedLimitCounter: dataType.NewCounter(64, 60),
|
||||||
|
HTTPFloodSameURILimitCounter: dataType.NewCounter(64, 60),
|
||||||
|
}
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
stop := make(chan os.Signal, 1)
|
stop := make(chan os.Signal, 1)
|
||||||
@ -54,7 +59,7 @@ func main() {
|
|||||||
|
|
||||||
serverErr := make(chan error, 1)
|
serverErr := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
serverErr <- server.StartServer(cfg, ruleSet)
|
serverErr <- server.StartServer(cfg, ruleSet, sharedMem)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
Reference in New Issue
Block a user