Skip to content
This repository has been archived by the owner on Jan 4, 2023. It is now read-only.

Commit

Permalink
Merge pull request #6 from hossinasaadi/limit-connection
Browse files Browse the repository at this point in the history
Limit connection for inbound (IP Restricts )
  • Loading branch information
hossinasaadi authored Nov 4, 2022
2 parents b5a6882 + 131a9ae commit b95a8ec
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 15 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand All @@ -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=
Expand Down
150 changes: 137 additions & 13 deletions web/job/check_clinet_ip_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ import (
"encoding/json"
"gorm.io/gorm"
"strconv"

"strings"
"time"
"net"
"github.com/go-cmd/cmd"
"sort"
)

type CheckClientIpJob struct {
xrayService service.XrayService
inboundService service.InboundService
}
var job *CheckClientIpJob

var disAllowedIps []string

func NewCheckClientIpJob() *CheckClientIpJob {
job = new(CheckClientIpJob)
return job
Expand Down Expand Up @@ -84,6 +89,8 @@ func processLogFile() {
}

var inboundsClientIps []*model.InboundClientIps
disAllowedIps = []string{}

for clientEmail, ips := range InboundClientIps {
inboundClientIps := GetInboundClientIps(clientEmail, ips)
if inboundClientIps != nil {
Expand All @@ -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, 700 *time.Millisecond)
time.Sleep(60 * time.Second)
stop <- true

}
func GetAccessLogPath() string {

Expand Down Expand Up @@ -157,9 +172,17 @@ 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 = 2
}
disAllowedIps = append(disAllowedIps,ips[limitIp - 1:]...)

}
logger.Debug("disAllowedIps ",disAllowedIps)
sort.Sort(sort.StringSlice(disAllowedIps))

return inboundClientIps
}
Expand Down Expand Up @@ -190,17 +213,118 @@ 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)
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")

if err == nil {
job.xrayService.SetToNeedRestart()
<-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)
}
}
}

return err
}

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
}
4 changes: 2 additions & 2 deletions web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

This comment has been minimized.

Copy link
@kavkav123

kavkav123 Nov 8, 2022

This must be 120 secounds or more for weak VPSs
Or better way is to bring it out of code and make it to parameter of application to change it simply by user.

s.cron.AddJob("@every 30s", job.NewCheckClientIpJob())

// 每一天提示一次流量情况,上海时间8点30
var entry cron.EntryID
Expand Down

0 comments on commit b95a8ec

Please sign in to comment.