|
@@ -10,10 +10,8 @@ import (
|
|
"regexp"
|
|
"regexp"
|
|
"sort"
|
|
"sort"
|
|
"strings"
|
|
"strings"
|
|
- "sync"
|
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
- "x-ui/config"
|
|
|
|
"x-ui/database"
|
|
"x-ui/database"
|
|
"x-ui/database/model"
|
|
"x-ui/database/model"
|
|
"x-ui/logger"
|
|
"x-ui/logger"
|
|
@@ -21,17 +19,11 @@ import (
|
|
)
|
|
)
|
|
|
|
|
|
type CheckClientIpJob struct {
|
|
type CheckClientIpJob struct {
|
|
|
|
+ lastClear int64
|
|
disAllowedIps []string
|
|
disAllowedIps []string
|
|
}
|
|
}
|
|
|
|
|
|
var job *CheckClientIpJob
|
|
var job *CheckClientIpJob
|
|
-var ipFiles = []string{
|
|
|
|
- xray.GetIPLimitLogPath(),
|
|
|
|
- xray.GetIPLimitBannedLogPath(),
|
|
|
|
- xray.GetIPLimitBannedPrevLogPath(),
|
|
|
|
- xray.GetAccessPersistentLogPath(),
|
|
|
|
- xray.GetAccessPersistentPrevLogPath(),
|
|
|
|
-}
|
|
|
|
|
|
|
|
func NewCheckClientIpJob() *CheckClientIpJob {
|
|
func NewCheckClientIpJob() *CheckClientIpJob {
|
|
job = new(CheckClientIpJob)
|
|
job = new(CheckClientIpJob)
|
|
@@ -39,54 +31,43 @@ func NewCheckClientIpJob() *CheckClientIpJob {
|
|
}
|
|
}
|
|
|
|
|
|
func (j *CheckClientIpJob) Run() {
|
|
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.hasLimitIp() {
|
|
- if j.checkFail2BanInstalled() && xray.GetAccessLogPath() == "./access.log" {
|
|
|
|
- j.processLogFile()
|
|
|
|
|
|
+ if f2bInstalled && accessLogPath == "./access.log" {
|
|
|
|
+ clearAccessLog = j.processLogFile()
|
|
} else {
|
|
} else {
|
|
- if !j.checkFail2BanInstalled() {
|
|
|
|
|
|
+ if !f2bInstalled {
|
|
logger.Warning("fail2ban is not installed. IP limiting may not work properly.")
|
|
logger.Warning("fail2ban is not installed. IP limiting may not work properly.")
|
|
}
|
|
}
|
|
- switch xray.GetAccessLogPath() {
|
|
|
|
|
|
+ switch accessLogPath {
|
|
case "none":
|
|
case "none":
|
|
logger.Warning("Access log is set to 'none', check your Xray Configs")
|
|
logger.Warning("Access log is set to 'none', check your Xray Configs")
|
|
case "":
|
|
case "":
|
|
logger.Warning("Access log doesn't exist in your Xray Configs")
|
|
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 || time.Now().Unix() - j.lastClear > 3600 {
|
|
j.clearAccessLog()
|
|
j.clearAccessLog()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
func (j *CheckClientIpJob) 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)
|
|
j.checkError(err)
|
|
- defer logAccessP.Close()
|
|
|
|
|
|
|
|
// reopen the access log file for reading
|
|
// reopen the access log file for reading
|
|
|
|
+ accessLogPath := xray.GetAccessLogPath()
|
|
file, err := os.Open(accessLogPath)
|
|
file, err := os.Open(accessLogPath)
|
|
j.checkError(err)
|
|
j.checkError(err)
|
|
|
|
|
|
@@ -95,12 +76,13 @@ func (j *CheckClientIpJob) clearAccessLog() {
|
|
j.checkError(err)
|
|
j.checkError(err)
|
|
|
|
|
|
// close the file after copying content
|
|
// close the file after copying content
|
|
|
|
+ logAccessP.Close()
|
|
file.Close()
|
|
file.Close()
|
|
|
|
|
|
// clean access log
|
|
// clean access log
|
|
err = os.Truncate(accessLogPath, 0)
|
|
err = os.Truncate(accessLogPath, 0)
|
|
j.checkError(err)
|
|
j.checkError(err)
|
|
-
|
|
|
|
|
|
+ j.lastClear = time.Now().Unix()
|
|
}
|
|
}
|
|
|
|
|
|
func (j *CheckClientIpJob) hasLimitIp() bool {
|
|
func (j *CheckClientIpJob) hasLimitIp() bool {
|
|
@@ -139,22 +121,11 @@ func (j *CheckClientIpJob) checkFail2BanInstalled() bool {
|
|
return err == nil
|
|
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()
|
|
accessLogPath := xray.GetAccessLogPath()
|
|
|
|
|
|
file, err := os.Open(accessLogPath)
|
|
file, err := os.Open(accessLogPath)
|
|
j.checkError(err)
|
|
j.checkError(err)
|
|
- defer file.Close()
|
|
|
|
|
|
|
|
InboundClientIps := make(map[string][]string)
|
|
InboundClientIps := make(map[string][]string)
|
|
|
|
|
|
@@ -190,6 +161,7 @@ func (j *CheckClientIpJob) processLogFile() {
|
|
}
|
|
}
|
|
|
|
|
|
j.checkError(scanner.Err())
|
|
j.checkError(scanner.Err())
|
|
|
|
+ file.Close()
|
|
|
|
|
|
shouldCleanLog := false
|
|
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) {
|
|
func (j *CheckClientIpJob) checkError(e error) {
|
|
@@ -286,7 +253,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
|
|
j.disAllowedIps = []string{}
|
|
j.disAllowedIps = []string{}
|
|
|
|
|
|
// create iplimit log file channel
|
|
// 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 {
|
|
if err != nil {
|
|
logger.Errorf("failed to create or open ip limit log file: %s", err)
|
|
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()
|
|
db := database.GetDB()
|
|
err = db.Save(inboundClientIps).Error
|
|
err = db.Save(inboundClientIps).Error
|
|
- if err != nil {
|
|
|
|
- return shouldCleanLog
|
|
|
|
- }
|
|
|
|
|
|
+ j.checkError(err)
|
|
|
|
+
|
|
return shouldCleanLog
|
|
return shouldCleanLog
|
|
}
|
|
}
|
|
|
|
|