diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go index c38112f6..344160e1 100644 --- a/web/job/check_client_ip_job.go +++ b/web/job/check_client_ip_job.go @@ -10,10 +10,8 @@ import ( "regexp" "sort" "strings" - "sync" "time" - "x-ui/config" "x-ui/database" "x-ui/database/model" "x-ui/logger" @@ -21,17 +19,11 @@ import ( ) type CheckClientIpJob struct { + lastClear int64 disAllowedIps []string } var job *CheckClientIpJob -var ipFiles = []string{ - xray.GetIPLimitLogPath(), - xray.GetIPLimitBannedLogPath(), - xray.GetIPLimitBannedPrevLogPath(), - xray.GetAccessPersistentLogPath(), - xray.GetAccessPersistentPrevLogPath(), -} func NewCheckClientIpJob() *CheckClientIpJob { job = new(CheckClientIpJob) @@ -39,54 +31,43 @@ func NewCheckClientIpJob() *CheckClientIpJob { } func (j *CheckClientIpJob) Run() { - var wg sync.WaitGroup - - if j.checkFail2BanInstalled() { - j.openLogFiles(ipFiles) + if j.lastClear == 0 { + j.lastClear = time.Now().Unix() } + f2bInstalled := j.checkFail2BanInstalled() + accessLogPath := xray.GetAccessLogPath() + clearAccessLog := false + if j.hasLimitIp() { - if j.checkFail2BanInstalled() && xray.GetAccessLogPath() == "./access.log" { - j.processLogFile() + if f2bInstalled && accessLogPath == "./access.log" { + clearAccessLog = j.processLogFile() } else { - if !j.checkFail2BanInstalled() { + if !f2bInstalled { logger.Warning("fail2ban is not installed. IP limiting may not work properly.") } - switch xray.GetAccessLogPath() { + switch accessLogPath { case "none": logger.Warning("Access log is set to 'none', check your Xray Configs") case "": logger.Warning("Access log doesn't exist in your Xray Configs") + default: + logger.Warning("Current access.log path is not compatible with IP Limit") } } } - if !j.checkFail2BanInstalled() && xray.GetAccessLogPath() == "./access.log" { - wg.Add(1) - go func() { - defer wg.Done() - j.clearLogTime() - }() - wg.Wait() - } -} - -func (j *CheckClientIpJob) clearLogTime() { - ticker := time.NewTicker(time.Hour) - defer ticker.Stop() - - for range ticker.C { + if clearAccessLog || accessLogPath == "./access.log" && time.Now().Unix() - j.lastClear > 3600 { j.clearAccessLog() } } func (j *CheckClientIpJob) clearAccessLog() { - accessLogPath := xray.GetAccessLogPath() - logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) j.checkError(err) - defer logAccessP.Close() // reopen the access log file for reading + accessLogPath := xray.GetAccessLogPath() file, err := os.Open(accessLogPath) j.checkError(err) @@ -95,12 +76,13 @@ func (j *CheckClientIpJob) clearAccessLog() { 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() } func (j *CheckClientIpJob) hasLimitIp() bool { @@ -139,22 +121,11 @@ func (j *CheckClientIpJob) checkFail2BanInstalled() bool { return err == nil } -func (j *CheckClientIpJob) openLogFiles(ipFiles []string) { - for i := 0; i < len(ipFiles); i++ { - err := os.MkdirAll(config.GetLogFolder(), 0770) - j.checkError(err) - file, err := os.OpenFile(ipFiles[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) - j.checkError(err) - defer file.Close() - } -} - -func (j *CheckClientIpJob) processLogFile() { +func (j *CheckClientIpJob) processLogFile() bool { accessLogPath := xray.GetAccessLogPath() file, err := os.Open(accessLogPath) j.checkError(err) - defer file.Close() InboundClientIps := make(map[string][]string) @@ -190,6 +161,7 @@ func (j *CheckClientIpJob) processLogFile() { } j.checkError(scanner.Err()) + file.Close() shouldCleanLog := false @@ -203,12 +175,7 @@ func (j *CheckClientIpJob) processLogFile() { } } - // added delay before cleaning logs to reduce chance of logging IP that already has been banned - time.Sleep(time.Second * 2) - - if shouldCleanLog { - j.clearAccessLog() - } + return shouldCleanLog } func (j *CheckClientIpJob) checkError(e error) { @@ -286,7 +253,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun j.disAllowedIps = []string{} // create iplimit log file channel - logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { logger.Errorf("failed to create or open ip limit log file: %s", err) } @@ -319,9 +286,8 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun db := database.GetDB() err = db.Save(inboundClientIps).Error - if err != nil { - return shouldCleanLog - } + j.checkError(err) + return shouldCleanLog } diff --git a/web/job/clear_logs_job.go b/web/job/clear_logs_job.go index c6312006..c4cb3c87 100644 --- a/web/job/clear_logs_job.go +++ b/web/job/clear_logs_job.go @@ -1,6 +1,7 @@ package job import ( + "io" "os" "x-ui/logger" "x-ui/xray" @@ -28,21 +29,23 @@ func (j *ClearLogsJob) Run() { for i := 0; i < len(logFiles); i++ { if i > 0 { // copy to previous logs - logFilePrev, err := os.OpenFile(logFilesPrev[i-1], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + logFilePrev, err := os.OpenFile(logFilesPrev[i-1], os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { logger.Warning("clear logs job err:", err) } - logFile, err := os.ReadFile(logFiles[i]) + logFile, err := os.Open(logFiles[i]) if err != nil { logger.Warning("clear logs job err:", err) } - _, err = logFilePrev.Write(logFile) + _, err = io.Copy(logFilePrev, logFile) if err != nil { logger.Warning("clear logs job err:", err) } - defer logFilePrev.Close() + + logFile.Close() + logFilePrev.Close() } err := os.Truncate(logFiles[i], 0) diff --git a/x-ui.sh b/x-ui.sh index 91efe7cf..a94852f2 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -953,9 +953,15 @@ create_iplimit_jails() { # Uncomment 'allowipv6 = auto' in fail2ban.conf sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf + #On Debian 12+ fail2ban's default backend should be changed to systemd + if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then + sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf + fi + cat << EOF > /etc/fail2ban/jail.d/3x-ipl.conf [3x-ipl] enabled=true +backend=auto filter=3x-ipl action=3x-ipl logpath=${iplimit_log_path} diff --git a/xray/process.go b/xray/process.go index e37a0649..03d2eced 100644 --- a/xray/process.go +++ b/xray/process.go @@ -202,6 +202,12 @@ func (p *process) Start() (err error) { if err != nil { return common.NewErrorf("Failed to generate xray configuration file: %v", err) } + + err = os.MkdirAll(config.GetLogFolder(), 0770) + if err != nil { + logger.Warningf("Something went wrong: %s", err) + } + configPath := GetConfigPath() err = os.WriteFile(configPath, data, fs.ModePerm) if err != nil {