Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/web/job
diff options
context:
space:
mode:
authorMHSanaei <33454419+MHSanaei@users.noreply.github.com>2023-02-28 22:54:29 +0300
committerMHSanaei <33454419+MHSanaei@users.noreply.github.com>2023-02-28 22:54:29 +0300
commit5317df21f3923bfa771ed6008d5b0f5f8911e498 (patch)
tree3109ee9abe5bf8cf24eef806f033c6d0e5dd160f /web/job
parent1a4ba4afd64531c466e5e4a0af7b45bcb3247339 (diff)
ip limit + export links
Diffstat (limited to 'web/job')
-rw-r--r--web/job/check_clinet_ip_job.go351
1 files changed, 351 insertions, 0 deletions
diff --git a/web/job/check_clinet_ip_job.go b/web/job/check_clinet_ip_job.go
new file mode 100644
index 00000000..bf71116b
--- /dev/null
+++ b/web/job/check_clinet_ip_job.go
@@ -0,0 +1,351 @@
+package job
+
+import (
+ "x-ui/logger"
+ "x-ui/web/service"
+ "x-ui/database"
+ "x-ui/database/model"
+ "os"
+ ss "strings"
+ "regexp"
+ "encoding/json"
+ // "strconv"
+ "strings"
+ "time"
+ "net"
+ "github.com/go-cmd/cmd"
+ "sort"
+)
+
+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("./bin/blockedIPs", 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("bin/config.json")
+ 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)
+
+ 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(){
+
+ 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," ")
+
+ destIp,destPort,srcIp,srcPort := "","","",""
+
+
+ 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
+}