diff options
| author | MHSanaei <33454419+MHSanaei@users.noreply.github.com> | 2023-02-28 22:54:29 +0300 |
|---|---|---|
| committer | MHSanaei <33454419+MHSanaei@users.noreply.github.com> | 2023-02-28 22:54:29 +0300 |
| commit | 5317df21f3923bfa771ed6008d5b0f5f8911e498 (patch) | |
| tree | 3109ee9abe5bf8cf24eef806f033c6d0e5dd160f /web/job | |
| parent | 1a4ba4afd64531c466e5e4a0af7b45bcb3247339 (diff) | |
ip limit + export links
Diffstat (limited to 'web/job')
| -rw-r--r-- | web/job/check_clinet_ip_job.go | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/web/job/check_clinet_ip_job.go b/web/job/check_clinet_ip_job.go new file mode 100644 index 00000000..bf71116b --- /dev/null +++ b/web/job/check_clinet_ip_job.go @@ -0,0 +1,351 @@ +package job + +import ( + "x-ui/logger" + "x-ui/web/service" + "x-ui/database" + "x-ui/database/model" + "os" + ss "strings" + "regexp" + "encoding/json" + // "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 +} + +func (j *CheckClientIpJob) Run() { + logger.Debug("Check Client IP Job...") + processLogFile() + + // disAllowedIps = []string{"192.168.1.183","192.168.1.197"} + blockedIps := []byte(ss.Join(disAllowedIps,",")) + err := os.WriteFile("./bin/blockedIPs", blockedIps, 0755) + checkError(err) + +} + +func processLogFile() { + accessLogPath := GetAccessLogPath() + if(accessLogPath == "") { + logger.Warning("xray log not init in config.json") + return + } + + data, err := os.ReadFile(accessLogPath) + InboundClientIps := make(map[string][]string) + checkError(err) + + // clean log + if err := os.Truncate(GetAccessLogPath(), 0); err != nil { + checkError(err) + } + + lines := ss.Split(string(data), "\n") + for _, line := range lines { + ipRegx, _ := regexp.Compile(`[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+`) + emailRegx, _ := regexp.Compile(`email:.+`) + + matchesIp := ipRegx.FindString(line) + if(len(matchesIp) > 0) { + ip := string(matchesIp) + if( ip == "127.0.0.1" || ip == "1.1.1.1") { + continue + } + + matchesEmail := emailRegx.FindString(line) + if(matchesEmail == "") { + continue + } + matchesEmail = ss.Split(matchesEmail, "email: ")[1] + + if(InboundClientIps[matchesEmail] != nil) { + if(contains(InboundClientIps[matchesEmail],ip)){ + continue + } + InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail],ip) + + + + }else{ + InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail],ip) + } + } + + } + disAllowedIps = []string{} + + for clientEmail, ips := range InboundClientIps { + inboundClientIps,err := GetInboundClientIps(clientEmail) + sort.Sort(sort.StringSlice(ips)) + if(err != nil){ + addInboundClientIps(clientEmail,ips) + + }else{ + updateInboundClientIps(inboundClientIps,clientEmail,ips) + } + + } + + + // 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 { + + config, err := os.ReadFile("bin/config.json") + checkError(err) + + jsonConfig := map[string]interface{}{} + err = json.Unmarshal([]byte(config), &jsonConfig) + checkError(err) + if(jsonConfig["log"] != nil) { + jsonLog := jsonConfig["log"].(map[string]interface{}) + if(jsonLog["access"] != nil) { + + accessLogPath := jsonLog["access"].(string) + + return accessLogPath + } + } + return "" + +} +func checkError(e error) { + if e != nil { + logger.Warning("client ip job err:", e) + } +} +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} +func GetInboundClientIps(clientEmail string) (*model.InboundClientIps, error) { + db := database.GetDB() + InboundClientIps := &model.InboundClientIps{} + err := db.Model(model.InboundClientIps{}).Where("client_email = ?", clientEmail).First(InboundClientIps).Error + if err != nil { + return nil, err + } + return InboundClientIps, nil +} +func addInboundClientIps(clientEmail string,ips []string) error { + inboundClientIps := &model.InboundClientIps{} + jsonIps, err := json.Marshal(ips) + checkError(err) + + inboundClientIps.ClientEmail = clientEmail + inboundClientIps.Ips = string(jsonIps) + + + db := database.GetDB() + tx := db.Begin() + + defer func() { + if err == nil { + tx.Commit() + } else { + tx.Rollback() + } + }() + + err = tx.Save(inboundClientIps).Error + if err != nil { + return err + } + return nil +} +func updateInboundClientIps(inboundClientIps *model.InboundClientIps,clientEmail string,ips []string) error { + + jsonIps, err := json.Marshal(ips) + checkError(err) + + inboundClientIps.ClientEmail = clientEmail + inboundClientIps.Ips = string(jsonIps) + + // check inbound limitation + inbound, err := GetInboundByEmail(clientEmail) + checkError(err) + + if inbound.Settings == "" { + logger.Debug("wrong data ",inbound) + return nil + } + + settings := map[string][]model.Client{} + json.Unmarshal([]byte(inbound.Settings), &settings) + clients := settings["clients"] + + for _, client := range clients { + if client.Email == clientEmail { + + limitIp := client.LimitIP + + if(limitIp < len(ips) && limitIp != 0 && inbound.Enable) { + + disAllowedIps = append(disAllowedIps,ips[limitIp:]...) + } + } + } + logger.Debug("disAllowedIps ",disAllowedIps) + sort.Sort(sort.StringSlice(disAllowedIps)) + + db := database.GetDB() + err = db.Save(inboundClientIps).Error + if err != nil { + return err + } + return 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 GetInboundByEmail(clientEmail string) (*model.Inbound, error) { + db := database.GetDB() + var inbounds *model.Inbound + err := db.Model(model.Inbound{}).Where("settings LIKE ?", "%" + clientEmail + "%").Find(&inbounds).Error + if err != nil { + return nil, err + } + return inbounds, nil +} + +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 { + ipRegx, _ := regexp.Compile(`[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+`) + portRegx, _ := regexp.Compile(`(?:(:))([0-9]..[^.][0-9]+)`) + + for _, row := range c.Status().Stdout { + + data := strings.Split(row," ") + + destIp,destPort,srcIp,srcPort := "","","","" + + + destIp = string(ipRegx.FindString(data[0])) + + destPort = portRegx.FindString(data[0]) + destPort = strings.Replace(destPort,":","",-1) + + + srcIp = string(ipRegx.FindString(data[1])) + + srcPort = portRegx.FindString(data[1]) + srcPort = strings.Replace(srcPort,":","",-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 + } + + ips = append(ips,ip.String()) + + } + } + logger.Debug("System IPs : ",ips) + + return ips, nil +} + + +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 +} |
