From 3d0f25b66987dcab4526265747f081dd979374f2 Mon Sep 17 00:00:00 2001 From: Hossin Asaadi Date: Fri, 4 Nov 2022 07:56:25 -0400 Subject: [PATCH 1/4] add connection limit inbound --- go.mod | 1 + go.sum | 3 + web/job/check_clinet_ip_job.go | 142 ++++++++++++++++++++++++++++++++- 3 files changed, 144 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 44b6570b..0342d7d2 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/Workiva/go-datastructures v1.0.53 github.com/gin-contrib/sessions v0.0.3 github.com/gin-gonic/gin v1.7.1 + github.com/go-cmd/cmd v1.4.1 // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 github.com/nicksnyder/go-i18n/v2 v2.1.2 diff --git a/go.sum b/go.sum index c8ed240f..9b808dd8 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,8 @@ github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8= github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-cmd/cmd v1.4.1 h1:JUcEIE84v8DSy02XTZpUDeGKExk2oW3DA10hTjbQwmc= +github.com/go-cmd/cmd v1.4.1/go.mod h1:tbBenttXtZU4c5djS1o7PWL5pd2xAr5sIqH1kGdNiRc= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -72,6 +74,7 @@ github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7a github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= +github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= diff --git a/web/job/check_clinet_ip_job.go b/web/job/check_clinet_ip_job.go index 29e13a87..6d6b0b63 100644 --- a/web/job/check_clinet_ip_job.go +++ b/web/job/check_clinet_ip_job.go @@ -11,6 +11,10 @@ import ( "encoding/json" "gorm.io/gorm" "strconv" + "strings" + "time" + "net" + "github.com/go-cmd/cmd" ) @@ -19,7 +23,8 @@ type CheckClientIpJob struct { inboundService service.InboundService } var job *CheckClientIpJob - +var disAllowedIps []string + func NewCheckClientIpJob() *CheckClientIpJob { job = new(CheckClientIpJob) return job @@ -84,6 +89,8 @@ func processLogFile() { } var inboundsClientIps []*model.InboundClientIps + disAllowedIps = []string{} + for clientEmail, ips := range InboundClientIps { inboundClientIps := GetInboundClientIps(clientEmail, ips) if inboundClientIps != nil { @@ -93,6 +100,14 @@ func processLogFile() { err = AddInboundsClientIps(inboundsClientIps) checkError(err) + + // check if inbound connection is more than limited ip and drop connection + LimitDevice := func() { LimitDevice() } + + stop := schedule(LimitDevice, 1000 *time.Millisecond) + time.Sleep(10 * time.Second) + stop <- true + } func GetAccessLogPath() string { @@ -157,9 +172,16 @@ func GetInboundClientIps(clientEmail string, ips []string) *model.InboundClientI if err != nil { return nil } + if(limitIp < len(ips) && limitIp != 0 && inbound.Enable) { - DisableInbound(inbound.Id) + + if(limitIp == 1){ + limitIp = 0 + } + disAllowedIps = append(disAllowedIps,ips[limitIp:]...) + } + logger.Debug("disAllowedIps ",disAllowedIps) return inboundClientIps } @@ -204,3 +226,119 @@ func DisableInbound(id int) error { return err } + +func LimitDevice(){ + + localIp,err := LocalIP() + checkError(err) + + c := cmd.NewCmd("bash","-c","ss --tcp | grep -E '" + IPsToRegex(localIp) + "'| awk '{if($1==\"ESTAB\") print $4,$5;}'","| sort | uniq -c | sort -nr | head") + + <-c.Start() + if len(c.Status().Stdout) > 0 { + + for _, row := range c.Status().Stdout { + + data := strings.Split(row," ") + + dest,src := strings.Split(data[0],":"),strings.Split(data[1],":") + + destIp,destPort := dest[0],dest[1] + srcIp,srcPort := src[0],src[1] + + if(contains(disAllowedIps,srcIp)){ + dropCmd := cmd.NewCmd("bash","-c","ss -K dport = " + srcPort) + dropCmd.Start() + + logger.Debug("request droped : ",srcIp,srcPort,"to",destIp,destPort) + } + } + } + +} + +func LocalIP() ([]string, error) { + // get machine ips + + ifaces, err := net.Interfaces() + ips := []string{} + if err != nil { + return ips, err + } + for _, i := range ifaces { + addrs, err := i.Addrs() + if err != nil { + return ips, err + } + + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + + if isPrivateIP(ip) { + ips = append(ips,ip.String()) + } + } + } + logger.Debug("System IPs : ",ips) + + return ips, nil +} + +func isPrivateIP(ip net.IP) bool { + var privateIPBlocks []*net.IPNet + for _, cidr := range []string{ + // don't check loopback ips + //"127.0.0.0/8", // IPv4 loopback + //"::1/128", // IPv6 loopback + //"fe80::/10", // IPv6 link-local + "10.0.0.0/8", // RFC1918 + "172.16.0.0/12", // RFC1918 + "192.168.0.0/16", // RFC1918 + } { + _, block, _ := net.ParseCIDR(cidr) + privateIPBlocks = append(privateIPBlocks, block) + } + + for _, block := range privateIPBlocks { + if block.Contains(ip) { + return true + } + } + + return false +} + +func IPsToRegex(ips []string) (string){ + + regx := "" + for _, ip := range ips { + regx += "(" + strings.Replace(ip, ".", "\\.", -1) + ")" + + } + regx = "(" + strings.Replace(regx, ")(", ")|(.", -1) + ")" + + return regx +} + +func schedule(LimitDevice func(), delay time.Duration) chan bool { + stop := make(chan bool) + + go func() { + for { + LimitDevice() + select { + case <-time.After(delay): + case <-stop: + return + } + } + }() + + return stop +} From 87b301cea6477c02aeb33d0179424409707f202c Mon Sep 17 00:00:00 2001 From: Hossin Asaadi Date: Fri, 4 Nov 2022 08:47:47 -0400 Subject: [PATCH 2/4] fix run schedule run time --- web/job/check_clinet_ip_job.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/job/check_clinet_ip_job.go b/web/job/check_clinet_ip_job.go index 6d6b0b63..1da1f744 100644 --- a/web/job/check_clinet_ip_job.go +++ b/web/job/check_clinet_ip_job.go @@ -105,7 +105,7 @@ func processLogFile() { LimitDevice := func() { LimitDevice() } stop := schedule(LimitDevice, 1000 *time.Millisecond) - time.Sleep(10 * time.Second) + time.Sleep(60 * time.Second) stop <- true } From d564956134d04036789f0a5989113dd2c39189e3 Mon Sep 17 00:00:00 2001 From: Hossin Asaadi Date: Fri, 4 Nov 2022 09:08:46 -0400 Subject: [PATCH 3/4] ip sort and fix bug --- web/job/check_clinet_ip_job.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web/job/check_clinet_ip_job.go b/web/job/check_clinet_ip_job.go index 1da1f744..e9ec6886 100644 --- a/web/job/check_clinet_ip_job.go +++ b/web/job/check_clinet_ip_job.go @@ -15,7 +15,7 @@ import ( "time" "net" "github.com/go-cmd/cmd" - + "sort" ) type CheckClientIpJob struct { @@ -176,12 +176,13 @@ func GetInboundClientIps(clientEmail string, ips []string) *model.InboundClientI if(limitIp < len(ips) && limitIp != 0 && inbound.Enable) { if(limitIp == 1){ - limitIp = 0 + limitIp = 2 } - disAllowedIps = append(disAllowedIps,ips[limitIp:]...) + disAllowedIps = append(disAllowedIps,ips[limitIp - 1:]...) } logger.Debug("disAllowedIps ",disAllowedIps) + sort.Sort(sort.StringSlice(disAllowedIps)) return inboundClientIps } From 131a9aeefe2d75996454116e16d946a3f9a8da8b Mon Sep 17 00:00:00 2001 From: Hossin Asaadi Date: Fri, 4 Nov 2022 10:13:17 -0400 Subject: [PATCH 4/4] change job run time --- web/job/check_clinet_ip_job.go | 17 +---------------- web/web.go | 4 ++-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/web/job/check_clinet_ip_job.go b/web/job/check_clinet_ip_job.go index e9ec6886..e3396ad6 100644 --- a/web/job/check_clinet_ip_job.go +++ b/web/job/check_clinet_ip_job.go @@ -104,7 +104,7 @@ func processLogFile() { // check if inbound connection is more than limited ip and drop connection LimitDevice := func() { LimitDevice() } - stop := schedule(LimitDevice, 1000 *time.Millisecond) + stop := schedule(LimitDevice, 700 *time.Millisecond) time.Sleep(60 * time.Second) stop <- true @@ -213,21 +213,6 @@ func GetInboundByEmail(clientEmail string) (*model.Inbound, error) { return inbounds, nil } -func DisableInbound(id int) error { - db := database.GetDB() - result := db.Model(model.Inbound{}). - Where("id = ? and enable = ?", id, true). - Update("enable", false) - err := result.Error - logger.Warning("disable inbound with id:",id) - - if err == nil { - job.xrayService.SetToNeedRestart() - } - - return err -} - func LimitDevice(){ localIp,err := LocalIP() diff --git a/web/web.go b/web/web.go index 60a69da1..0793a390 100644 --- a/web/web.go +++ b/web/web.go @@ -296,8 +296,8 @@ func (s *Server) startTask() { // 每 30 秒检查一次 inbound 流量超出和到期的情况 s.cron.AddJob("@every 30s", job.NewCheckInboundJob()) - // check client ips from log file every 1 min - s.cron.AddJob("@every 1m", job.NewCheckClientIpJob()) + // check client ips from log file every 30 sec + s.cron.AddJob("@every 30s", job.NewCheckClientIpJob()) // 每一天提示一次流量情况,上海时间8点30 var entry cron.EntryID