diff options
| author | Hamidreza Ghavami <hamid.r.gh.1998@gmail.com> | 2023-04-13 22:37:13 +0300 |
|---|---|---|
| committer | Hamidreza Ghavami <hamid.r.gh.1998@gmail.com> | 2023-04-13 22:37:13 +0300 |
| commit | a3b170d6c49c5ce866e791bd5fdc867e0090dbfe (patch) | |
| tree | c9c2beddc26540d99eefcdeaef380fa7fb24baab /web/job/check_client_ip_job.go | |
| parent | 4548755375b1a0d87812a1e32c0a2f63d9f0e5e2 (diff) | |
update and rename client ip job file
Diffstat (limited to 'web/job/check_client_ip_job.go')
| -rw-r--r-- | web/job/check_client_ip_job.go | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go new file mode 100644 index 00000000..95a652cd --- /dev/null +++ b/web/job/check_client_ip_job.go @@ -0,0 +1,349 @@ +package job + +import ( + "encoding/json" + "os" + "regexp" + ss "strings" + "x-ui/database" + "x-ui/database/model" + "x-ui/logger" + "x-ui/web/service" + "x-ui/xray" + // "strconv" + "github.com/go-cmd/cmd" + "net" + "sort" + "strings" + "time" +) + +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(xray.GetBlockedIPsPath(), 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(xray.GetConfigPath()) + 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) + + // Trim any leading/trailing whitespace from clientEmail + clientEmail = strings.TrimSpace(clientEmail) + + 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() { + var destIp, destPort, srcIp, srcPort string + + 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, " ") + + if len(data) < 2 { + continue // Skip this row if it doesn't have at least two elements + } + + 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 +} |
