From 9a2188cb0c19cc70f7f1bda455f28114a218d84f Mon Sep 17 00:00:00 2001 From: Senis Date: Fri, 4 Nov 2022 08:26:56 +0800 Subject: [PATCH] update: Global User IP limit. --- .gitignore | 3 ++- common/limiter/limiter.go | 19 +++++++++++++++++- common/limiter/model.go | 9 +++++++++ go.mod | 3 +++ go.sum | 6 ++++++ main/config.yml.example | 6 ++++++ service/controller/config.go | 33 ++++++++++++++++++-------------- service/controller/control.go | 5 +++-- service/controller/controller.go | 9 +++++++-- 9 files changed, 73 insertions(+), 20 deletions(-) create mode 100644 common/limiter/model.go diff --git a/.gitignore b/.gitignore index dc2701d..4376aaf 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ main/cert main/config.yml ./vscode .idea/* -.DS_Store \ No newline at end of file +.DS_Store +*.bak \ No newline at end of file diff --git a/common/limiter/limiter.go b/common/limiter/limiter.go index 678422b..58e5e96 100644 --- a/common/limiter/limiter.go +++ b/common/limiter/limiter.go @@ -5,6 +5,7 @@ import ( "fmt" "sync" + "github.com/go-redis/redis/v8" "golang.org/x/time/rate" "github.com/XrayR-project/XrayR/api" @@ -26,6 +27,11 @@ type InboundInfo struct { type Limiter struct { InboundInfo *sync.Map // Key: Tag, Value: *InboundInfo + r *redis.Client + g struct { + limit int + expiry int + } } func New() *Limiter { @@ -34,7 +40,18 @@ func New() *Limiter { } } -func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo) error { +func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo, globalDeviceLimit *GlobalDeviceLimitConfig) error { + // global limit + if globalDeviceLimit.Limit > 0 { + l.r = redis.NewClient(&redis.Options{ + Addr: globalDeviceLimit.RedisAddr, + Password: globalDeviceLimit.RedisPassword, + DB: globalDeviceLimit.RedisDB, + }) + l.g.limit = globalDeviceLimit.Limit + l.g.expiry = globalDeviceLimit.Expiry + } + inboundInfo := &InboundInfo{ Tag: tag, NodeSpeedLimit: nodeSpeedLimit, diff --git a/common/limiter/model.go b/common/limiter/model.go new file mode 100644 index 0000000..a238882 --- /dev/null +++ b/common/limiter/model.go @@ -0,0 +1,9 @@ +package limiter + +type GlobalDeviceLimitConfig struct { + Limit int `mapstructure:"Limit"` + RedisAddr string `mapstructure:"RedisAddr"` // host:port + RedisPassword string `mapstructure:"RedisPassword"` + RedisDB int `mapstructure:"RedisDB"` + Expiry int `mapstructure:"Expiry"` // minute +} diff --git a/go.mod b/go.mod index d6b075f..d6a33e5 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/deckarep/golang-set v1.8.0 github.com/fsnotify/fsnotify v1.6.0 github.com/go-acme/lego/v4 v4.9.0 + github.com/go-redis/redis/v8 v8.11.5 github.com/go-resty/resty/v2 v2.7.0 github.com/imdario/mergo v0.3.13 github.com/r3labs/diff/v2 v2.15.1 @@ -42,6 +43,7 @@ require ( github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/civo/civogo v0.3.11 // indirect github.com/cloudflare/cloudflare-go v0.49.0 // indirect github.com/cpu/goacmedns v0.1.1 // indirect @@ -49,6 +51,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deepmap/oapi-codegen v1.9.1 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dnsimple/dnsimple-go v0.71.1 // indirect github.com/exoscale/egoscale v0.90.0 // indirect diff --git a/go.sum b/go.sum index 7c07e46..3da8ee9 100644 --- a/go.sum +++ b/go.sum @@ -132,6 +132,8 @@ github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -176,6 +178,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= @@ -237,6 +241,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= diff --git a/main/config.yml.example b/main/config.yml.example index d55874c..2a4a966 100644 --- a/main/config.yml.example +++ b/main/config.yml.example @@ -39,6 +39,12 @@ Nodes: WarnTimes: 0 # After (WarnTimes) consecutive warnings, the user will be limited. Set to 0 to punish overspeed user immediately. LimitSpeed: 0 # The speedlimit of a limited user (unit: mbps) LimitDuration: 0 # How many minutes will the limiting last (unit: minute) + GlobalDeviceLimitConfig: + Limit: 0 # The global device limit of a user, 0 means disable + RedisAddr: 127.0.0.1:6379 # The redis server address + RedisPassword: YOUR PASSWORD # Redis password + RedisDB: 0 # Redis DB + Expiry: 60 # Expiry time (minute) FallBackConfigs: # Support multiple fallbacks - SNI: # TLS SNI(Server Name Indication), Empty for any diff --git a/service/controller/config.go b/service/controller/config.go index 1791d74..d93843a 100644 --- a/service/controller/config.go +++ b/service/controller/config.go @@ -1,20 +1,25 @@ package controller +import ( + "github.com/XrayR-project/XrayR/common/limiter" +) + type Config struct { - ListenIP string `mapstructure:"ListenIP"` - SendIP string `mapstructure:"SendIP"` - UpdatePeriodic int `mapstructure:"UpdatePeriodic"` - CertConfig *CertConfig `mapstructure:"CertConfig"` - EnableDNS bool `mapstructure:"EnableDNS"` - DNSType string `mapstructure:"DNSType"` - DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"` - DisableGetRule bool `mapstructure:"DisableGetRule"` - EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"` - EnableFallback bool `mapstructure:"EnableFallback"` - DisableIVCheck bool `mapstructure:"DisableIVCheck"` - DisableSniffing bool `mapstructure:"DisableSniffing"` - AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"` - FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"` + ListenIP string `mapstructure:"ListenIP"` + SendIP string `mapstructure:"SendIP"` + UpdatePeriodic int `mapstructure:"UpdatePeriodic"` + CertConfig *CertConfig `mapstructure:"CertConfig"` + EnableDNS bool `mapstructure:"EnableDNS"` + DNSType string `mapstructure:"DNSType"` + DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"` + DisableGetRule bool `mapstructure:"DisableGetRule"` + EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"` + EnableFallback bool `mapstructure:"EnableFallback"` + DisableIVCheck bool `mapstructure:"DisableIVCheck"` + DisableSniffing bool `mapstructure:"DisableSniffing"` + AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"` + GlobalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig `mapstructure:"GlobalDeviceLimitConfig"` + FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"` } type AutoSpeedLimitConfig struct { diff --git a/service/controller/control.go b/service/controller/control.go index d29dfac..83dac27 100644 --- a/service/controller/control.go +++ b/service/controller/control.go @@ -12,6 +12,7 @@ import ( "github.com/xtls/xray-core/proxy" "github.com/XrayR-project/XrayR/api" + "github.com/XrayR-project/XrayR/common/limiter" ) func (c *Controller) removeInbound(tag string) error { @@ -131,8 +132,8 @@ func (c *Controller) resetTraffic(upCounterList *[]stats.Counter, downCounterLis } } -func (c *Controller) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo) error { - err := c.dispatcher.Limiter.AddInboundLimiter(tag, nodeSpeedLimit, userList) +func (c *Controller) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo, globalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig) error { + err := c.dispatcher.Limiter.AddInboundLimiter(tag, nodeSpeedLimit, userList, globalDeviceLimitConfig) return err } diff --git a/service/controller/controller.go b/service/controller/controller.go index ca60cbd..d142c8f 100644 --- a/service/controller/controller.go +++ b/service/controller/controller.go @@ -17,6 +17,7 @@ import ( "github.com/XrayR-project/XrayR/api" "github.com/XrayR-project/XrayR/app/mydispatcher" "github.com/XrayR-project/XrayR/common/legocmd" + "github.com/XrayR-project/XrayR/common/limiter" "github.com/XrayR-project/XrayR/common/serverstatus" ) @@ -89,8 +90,12 @@ func (c *Controller) Start() error { // sync controller userList c.userList = userInfo + // Init global device limit + if c.config.GlobalDeviceLimitConfig == nil { + c.config.GlobalDeviceLimitConfig = &limiter.GlobalDeviceLimitConfig{Limit: 0} + } // Add Limiter - if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo); err != nil { + if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo, c.config.GlobalDeviceLimitConfig); err != nil { log.Print(err) } // Add Rule Manager @@ -231,7 +236,7 @@ func (c *Controller) nodeInfoMonitor() (err error) { return nil } // Add Limiter - if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo); err != nil { + if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo, c.config.GlobalDeviceLimitConfig); err != nil { log.Print(err) return nil }