From 569d99512cda4cb537c6603cf0a96ed9913c226f Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Mon, 28 Oct 2024 20:13:42 +0100 Subject: [PATCH] iplimit - accept all email format --- web/job/check_client_ip_job.go | 101 +++++++++++++++------------------ 1 file changed, 46 insertions(+), 55 deletions(-) diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go index c38550b7e1..08899010e4 100644 --- a/web/job/check_client_ip_job.go +++ b/web/job/check_client_ip_job.go @@ -9,7 +9,6 @@ import ( "os/exec" "regexp" "sort" - "strings" "time" "x-ui/database" @@ -37,11 +36,17 @@ func (j *CheckClientIpJob) Run() { shouldClearAccessLog := false iplimitActive := j.hasLimitIp() - f2bInstalled := j.checkFail2BanInstalled(iplimitActive) + f2bInstalled := j.checkFail2BanInstalled() isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive) - if iplimitActive && f2bInstalled && isAccessLogAvailable { - shouldClearAccessLog = j.processLogFile() + if iplimitActive { + if f2bInstalled && isAccessLogAvailable { + shouldClearAccessLog = j.processLogFile() + } else { + if !f2bInstalled { + logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.") + } + } } if shouldClearAccessLog || (isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600) { @@ -53,23 +58,18 @@ func (j *CheckClientIpJob) clearAccessLog() { logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644) j.checkError(err) - // get access log path to open it accessLogPath, err := xray.GetAccessLogPath() j.checkError(err) - // reopen the access log file for reading file, err := os.Open(accessLogPath) j.checkError(err) - // copy access log content to persistent file _, err = io.Copy(logAccessP, file) j.checkError(err) - // close the file after copying content logAccessP.Close() file.Close() - // clean access log err = os.Truncate(accessLogPath, 0) j.checkError(err) j.lastClear = time.Now().Unix() @@ -105,74 +105,69 @@ func (j *CheckClientIpJob) hasLimitIp() bool { } func (j *CheckClientIpJob) processLogFile() bool { - accessLogPath, err := xray.GetAccessLogPath() - j.checkError(err) - file, err := os.Open(accessLogPath) - j.checkError(err) + ipRegex := regexp.MustCompile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`) + emailRegex := regexp.MustCompile(`email: (.+)$`) + + accessLogPath, _ := xray.GetAccessLogPath() + file, _ := os.Open(accessLogPath) + defer file.Close() - InboundClientIps := make(map[string][]string) + inboundClientIps := make(map[string]map[string]struct{}, 100) scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() - ipRegx, _ := regexp.Compile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`) - emailRegx, _ := regexp.Compile(`email: (\S+)$`) + ipMatches := ipRegex.FindStringSubmatch(line) + if len(ipMatches) < 2 { + continue + } - matches := ipRegx.FindStringSubmatch(line) - if len(matches) > 1 { - ip := matches[1] - if ip == "127.0.0.1" || ip == "::1" { - continue - } + ip := ipMatches[1] - matchesEmail := emailRegx.FindString(line) - if matchesEmail == "" { - continue - } - matchesEmail = strings.Split(matchesEmail, "email: ")[1] + if ip == "127.0.0.1" || ip == "::1" { + continue + } - if InboundClientIps[matchesEmail] != nil { - if j.contains(InboundClientIps[matchesEmail], ip) { - continue - } - InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip) - } else { - InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip) - } + emailMatches := emailRegex.FindStringSubmatch(line) + if len(emailMatches) < 2 { + continue } - } + email := emailMatches[1] - j.checkError(scanner.Err()) - file.Close() + if _, exists := inboundClientIps[email]; !exists { + inboundClientIps[email] = make(map[string]struct{}) + } + inboundClientIps[email][ip] = struct{}{} + } shouldCleanLog := false + for email, uniqueIps := range inboundClientIps { - for clientEmail, ips := range InboundClientIps { - inboundClientIps, err := j.getInboundClientIps(clientEmail) + ips := make([]string, 0, len(uniqueIps)) + for ip := range uniqueIps { + ips = append(ips, ip) + } sort.Strings(ips) + + inboundClientIps, err := j.getInboundClientIps(email) if err != nil { - j.addInboundClientIps(clientEmail, ips) - } else { - shouldCleanLog = j.updateInboundClientIps(inboundClientIps, clientEmail, ips) + j.addInboundClientIps(email, ips) + continue } + + shouldCleanLog = j.updateInboundClientIps(inboundClientIps, email, ips) || shouldCleanLog } return shouldCleanLog } -func (j *CheckClientIpJob) checkFail2BanInstalled(iplimitActive bool) bool { +func (j *CheckClientIpJob) checkFail2BanInstalled() bool { cmd := "fail2ban-client" args := []string{"-h"} err := exec.Command(cmd, args...).Run() - - if iplimitActive && err != nil { - logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.") - return false - } - - return true + return err == nil } func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool { @@ -253,7 +248,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun inboundClientIps.ClientEmail = clientEmail inboundClientIps.Ips = string(jsonIps) - // Fetch inbound settings by client email inbound, err := j.getInboundByEmail(clientEmail) if err != nil { logger.Errorf("failed to fetch inbound settings for email %s: %s", clientEmail, err) @@ -265,14 +259,12 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun return false } - // Unmarshal settings to get client limits settings := map[string][]model.Client{} json.Unmarshal([]byte(inbound.Settings), &settings) clients := settings["clients"] shouldCleanLog := false j.disAllowedIps = []string{} - // Open log file for IP limits logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { logger.Errorf("failed to open IP limit log file: %s", err) @@ -282,7 +274,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun log.SetOutput(logIpFile) log.SetFlags(log.LstdFlags) - // Check client IP limits for _, client := range clients { if client.Email == clientEmail { limitIp := client.LimitIP