mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-04-19 21:42:24 +00:00
iplimit - accept all email format
This commit is contained in:
parent
ac84553a68
commit
569d99512c
1 changed files with 51 additions and 60 deletions
|
@ -9,7 +9,6 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"x-ui/database"
|
"x-ui/database"
|
||||||
|
@ -37,11 +36,17 @@ func (j *CheckClientIpJob) Run() {
|
||||||
|
|
||||||
shouldClearAccessLog := false
|
shouldClearAccessLog := false
|
||||||
iplimitActive := j.hasLimitIp()
|
iplimitActive := j.hasLimitIp()
|
||||||
f2bInstalled := j.checkFail2BanInstalled(iplimitActive)
|
f2bInstalled := j.checkFail2BanInstalled()
|
||||||
isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive)
|
isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive)
|
||||||
|
|
||||||
if iplimitActive && f2bInstalled && isAccessLogAvailable {
|
if iplimitActive {
|
||||||
shouldClearAccessLog = j.processLogFile()
|
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) {
|
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)
|
logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
// get access log path to open it
|
|
||||||
accessLogPath, err := xray.GetAccessLogPath()
|
accessLogPath, err := xray.GetAccessLogPath()
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
// reopen the access log file for reading
|
|
||||||
file, err := os.Open(accessLogPath)
|
file, err := os.Open(accessLogPath)
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
// copy access log content to persistent file
|
|
||||||
_, err = io.Copy(logAccessP, file)
|
_, err = io.Copy(logAccessP, file)
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
// close the file after copying content
|
|
||||||
logAccessP.Close()
|
logAccessP.Close()
|
||||||
file.Close()
|
file.Close()
|
||||||
|
|
||||||
// clean access log
|
|
||||||
err = os.Truncate(accessLogPath, 0)
|
err = os.Truncate(accessLogPath, 0)
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
j.lastClear = time.Now().Unix()
|
j.lastClear = time.Now().Unix()
|
||||||
|
@ -105,74 +105,69 @@ func (j *CheckClientIpJob) hasLimitIp() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *CheckClientIpJob) processLogFile() bool {
|
func (j *CheckClientIpJob) processLogFile() bool {
|
||||||
accessLogPath, err := xray.GetAccessLogPath()
|
|
||||||
j.checkError(err)
|
|
||||||
|
|
||||||
file, err := os.Open(accessLogPath)
|
ipRegex := regexp.MustCompile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
|
||||||
j.checkError(err)
|
emailRegex := regexp.MustCompile(`email: (.+)$`)
|
||||||
|
|
||||||
InboundClientIps := make(map[string][]string)
|
accessLogPath, _ := xray.GetAccessLogPath()
|
||||||
|
file, _ := os.Open(accessLogPath)
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
inboundClientIps := make(map[string]map[string]struct{}, 100)
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
scanner := bufio.NewScanner(file)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
|
|
||||||
ipRegx, _ := regexp.Compile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
|
ipMatches := ipRegex.FindStringSubmatch(line)
|
||||||
emailRegx, _ := regexp.Compile(`email: (\S+)$`)
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
matchesEmail := emailRegx.FindString(line)
|
|
||||||
if matchesEmail == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
matchesEmail = strings.Split(matchesEmail, "email: ")[1]
|
|
||||||
|
|
||||||
if InboundClientIps[matchesEmail] != nil {
|
|
||||||
if j.contains(InboundClientIps[matchesEmail], ip) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
|
||||||
} else {
|
|
||||||
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ip := ipMatches[1]
|
||||||
|
|
||||||
|
if ip == "127.0.0.1" || ip == "::1" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
emailMatches := emailRegex.FindStringSubmatch(line)
|
||||||
|
if len(emailMatches) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
email := emailMatches[1]
|
||||||
|
|
||||||
|
if _, exists := inboundClientIps[email]; !exists {
|
||||||
|
inboundClientIps[email] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
inboundClientIps[email][ip] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
j.checkError(scanner.Err())
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
shouldCleanLog := false
|
shouldCleanLog := false
|
||||||
|
for email, uniqueIps := range inboundClientIps {
|
||||||
|
|
||||||
for clientEmail, ips := range InboundClientIps {
|
ips := make([]string, 0, len(uniqueIps))
|
||||||
inboundClientIps, err := j.getInboundClientIps(clientEmail)
|
for ip := range uniqueIps {
|
||||||
sort.Strings(ips)
|
ips = append(ips, ip)
|
||||||
if err != nil {
|
|
||||||
j.addInboundClientIps(clientEmail, ips)
|
|
||||||
} else {
|
|
||||||
shouldCleanLog = j.updateInboundClientIps(inboundClientIps, clientEmail, ips)
|
|
||||||
}
|
}
|
||||||
|
sort.Strings(ips)
|
||||||
|
|
||||||
|
inboundClientIps, err := j.getInboundClientIps(email)
|
||||||
|
if err != nil {
|
||||||
|
j.addInboundClientIps(email, ips)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldCleanLog = j.updateInboundClientIps(inboundClientIps, email, ips) || shouldCleanLog
|
||||||
}
|
}
|
||||||
|
|
||||||
return shouldCleanLog
|
return shouldCleanLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *CheckClientIpJob) checkFail2BanInstalled(iplimitActive bool) bool {
|
func (j *CheckClientIpJob) checkFail2BanInstalled() bool {
|
||||||
cmd := "fail2ban-client"
|
cmd := "fail2ban-client"
|
||||||
args := []string{"-h"}
|
args := []string{"-h"}
|
||||||
err := exec.Command(cmd, args...).Run()
|
err := exec.Command(cmd, args...).Run()
|
||||||
|
return err == nil
|
||||||
if iplimitActive && err != nil {
|
|
||||||
logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool {
|
func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool {
|
||||||
|
@ -253,7 +248,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
|
||||||
inboundClientIps.ClientEmail = clientEmail
|
inboundClientIps.ClientEmail = clientEmail
|
||||||
inboundClientIps.Ips = string(jsonIps)
|
inboundClientIps.Ips = string(jsonIps)
|
||||||
|
|
||||||
// Fetch inbound settings by client email
|
|
||||||
inbound, err := j.getInboundByEmail(clientEmail)
|
inbound, err := j.getInboundByEmail(clientEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("failed to fetch inbound settings for email %s: %s", clientEmail, err)
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal settings to get client limits
|
|
||||||
settings := map[string][]model.Client{}
|
settings := map[string][]model.Client{}
|
||||||
json.Unmarshal([]byte(inbound.Settings), &settings)
|
json.Unmarshal([]byte(inbound.Settings), &settings)
|
||||||
clients := settings["clients"]
|
clients := settings["clients"]
|
||||||
shouldCleanLog := false
|
shouldCleanLog := false
|
||||||
j.disAllowedIps = []string{}
|
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)
|
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 open IP limit log file: %s", err)
|
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.SetOutput(logIpFile)
|
||||||
log.SetFlags(log.LstdFlags)
|
log.SetFlags(log.LstdFlags)
|
||||||
|
|
||||||
// Check client IP limits
|
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.Email == clientEmail {
|
if client.Email == clientEmail {
|
||||||
limitIp := client.LimitIP
|
limitIp := client.LimitIP
|
||||||
|
|
Loading…
Reference in a new issue