mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-05-15 12:48:05 +00:00
🚀 Some improvements for x-ui.sh and ip job (#665)
This commit is contained in:
parent
f726474a5d
commit
1028319386
17 changed files with 428 additions and 294 deletions
7
DockerEntrypoint.sh
Normal file
7
DockerEntrypoint.sh
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Start fail2ban
|
||||||
|
fail2ban-client -x -f start
|
||||||
|
|
||||||
|
# Run x-ui
|
||||||
|
exec /app/x-ui
|
|
@ -1,22 +1,28 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
if [ $1 == "amd64" ]; then
|
|
||||||
ARCH="64";
|
case $1 in
|
||||||
FNAME="amd64";
|
amd64)
|
||||||
elif [ $1 == "arm64" ]; then
|
ARCH="64"
|
||||||
|
FNAME="amd64"
|
||||||
|
;;
|
||||||
|
arm64)
|
||||||
ARCH="arm64-v8a"
|
ARCH="arm64-v8a"
|
||||||
FNAME="arm64";
|
FNAME="arm64"
|
||||||
else
|
;;
|
||||||
ARCH="64";
|
*)
|
||||||
FNAME="amd64";
|
ARCH="64"
|
||||||
fi
|
FNAME="amd64"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
mkdir -p build/bin
|
mkdir -p build/bin
|
||||||
cd build/bin
|
cd build/bin
|
||||||
|
|
||||||
wget "https://github.com/mhsanaei/xray-core/releases/latest/download/Xray-linux-${ARCH}.zip"
|
wget "https://github.com/mhsanaei/xray-core/releases/latest/download/Xray-linux-${ARCH}.zip"
|
||||||
unzip "Xray-linux-${ARCH}.zip"
|
unzip "Xray-linux-${ARCH}.zip"
|
||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat iran.dat
|
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat iran.dat
|
||||||
mv xray "xray-linux-${FNAME}"
|
mv xray "xray-linux-${FNAME}"
|
||||||
|
|
||||||
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
|
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
|
||||||
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
|
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
|
||||||
wget "https://github.com/bootmortis/iran-hosted-domains/releases/latest/download/iran.dat"
|
wget "https://github.com/bootmortis/iran-hosted-domains/releases/latest/download/iran.dat"
|
||||||
|
|
||||||
cd ../../
|
|
41
Dockerfile
41
Dockerfile
|
@ -1,20 +1,47 @@
|
||||||
#Build latest x-ui from source
|
# ========================================================
|
||||||
|
# Stage: Builder
|
||||||
|
# ========================================================
|
||||||
FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine AS builder
|
FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
RUN apk --no-cache --update add build-base gcc wget unzip
|
ENV CGO_ENABLED=1
|
||||||
|
|
||||||
|
RUN apk --no-cache --update add \
|
||||||
|
build-base \
|
||||||
|
gcc \
|
||||||
|
wget \
|
||||||
|
unzip
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN env CGO_ENABLED=1 go build -o build/x-ui main.go
|
|
||||||
|
RUN go build -o build/x-ui main.go
|
||||||
RUN ./DockerInit.sh "$TARGETARCH"
|
RUN ./DockerInit.sh "$TARGETARCH"
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
#Build app image using latest x-ui
|
# Stage: Final Image of 3x-ui
|
||||||
|
# ========================================================
|
||||||
FROM alpine
|
FROM alpine
|
||||||
ENV TZ=Asia/Tehran
|
ENV TZ=Asia/Tehran
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN apk add ca-certificates tzdata
|
RUN apk add --no-cache --update \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
fail2ban
|
||||||
|
|
||||||
COPY --from=builder /app/build/ /app/
|
COPY --from=builder /app/build/ /app/
|
||||||
|
COPY --from=builder /app/DockerEntrypoint.sh /app/
|
||||||
|
COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
|
||||||
|
|
||||||
|
# Configure fail2ban
|
||||||
|
RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
|
||||||
|
&& cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local \
|
||||||
|
&& sed -i "s/^\[ssh\]$/&\nenabled = false/" /etc/fail2ban/jail.local
|
||||||
|
|
||||||
|
RUN chmod +x \
|
||||||
|
/app/DockerEntrypoint.sh \
|
||||||
|
/app/x-ui \
|
||||||
|
/usr/bin/x-ui
|
||||||
|
|
||||||
VOLUME [ "/etc/x-ui" ]
|
VOLUME [ "/etc/x-ui" ]
|
||||||
ENTRYPOINT [ "/app/x-ui" ]
|
ENTRYPOINT [ "/app/DockerEntrypoint.sh" ]
|
||||||
|
|
|
@ -178,7 +178,7 @@ If you want to use routing to WARP follow steps as below:
|
||||||
2. Install WARP on **socks proxy mode**:
|
2. Install WARP on **socks proxy mode**:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bash <(curl -sSL https://gist.githubusercontent.com/hamid-gh98/dc5dd9b0cc5b0412af927b1ccdb294c7/raw/install_warp_proxy.sh)
|
bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Turn on the config you need in panel or [Copy and paste this file to Xray Configuration](./media/configs/traffic+block-ads+warp.json)
|
3. Turn on the config you need in panel or [Copy and paste this file to Xray Configuration](./media/configs/traffic+block-ads+warp.json)
|
||||||
|
@ -280,6 +280,7 @@ Reference syntax:
|
||||||
| XUI_DEBUG | `boolean` | `false` |
|
| XUI_DEBUG | `boolean` | `false` |
|
||||||
| XUI_BIN_FOLDER | `string` | `"bin"` |
|
| XUI_BIN_FOLDER | `string` | `"bin"` |
|
||||||
| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` |
|
| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` |
|
||||||
|
| XUI_LOG_FOLDER | `string` | `"/var/log"` |
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
@ -65,3 +65,11 @@ func GetDBFolderPath() string {
|
||||||
func GetDBPath() string {
|
func GetDBPath() string {
|
||||||
return fmt.Sprintf("%s/%s.db", GetDBFolderPath(), GetName())
|
return fmt.Sprintf("%s/%s.db", GetDBFolderPath(), GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetLogFolder() string {
|
||||||
|
logFolderPath := os.Getenv("XUI_LOG_FOLDER")
|
||||||
|
if logFolderPath == "" {
|
||||||
|
logFolderPath = "/var/log"
|
||||||
|
}
|
||||||
|
return logFolderPath
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
"x-ui/database/model"
|
"x-ui/database/model"
|
||||||
"x-ui/xray"
|
"x-ui/xray"
|
||||||
|
@ -26,7 +27,6 @@ var initializers = []func() error{
|
||||||
}
|
}
|
||||||
|
|
||||||
func initUser() error {
|
func initUser() error {
|
||||||
|
|
||||||
err := db.AutoMigrate(&model.User{})
|
err := db.AutoMigrate(&model.User{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -54,9 +54,11 @@ func initInbound() error {
|
||||||
func initSetting() error {
|
func initSetting() error {
|
||||||
return db.AutoMigrate(&model.Setting{})
|
return db.AutoMigrate(&model.Setting{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func initInboundClientIps() error {
|
func initInboundClientIps() error {
|
||||||
return db.AutoMigrate(&model.InboundClientIps{})
|
return db.AutoMigrate(&model.InboundClientIps{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func initClientTraffic() error {
|
func initClientTraffic() error {
|
||||||
return db.AutoMigrate(&xray.ClientTraffic{})
|
return db.AutoMigrate(&xray.ClientTraffic{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ plain='\033[0m'
|
||||||
cur_dir=$(pwd)
|
cur_dir=$(pwd)
|
||||||
|
|
||||||
# check root
|
# check root
|
||||||
[[ $EUID -ne 0 ]] && echo -e "${red}Fatal error:${plain} Please run this script with root privilege \n " && exit 1
|
[[ $EUID -ne 0 ]] && echo -e "${red}Fatal error: ${plain} Please run this script with root privilege \n " && exit 1
|
||||||
|
|
||||||
# Check OS and set release variable
|
# Check OS and set release variable
|
||||||
if [[ -f /etc/os-release ]]; then
|
if [[ -f /etc/os-release ]]; then
|
||||||
|
@ -41,12 +41,12 @@ if [[ "${release}" == "centos" ]]; then
|
||||||
fi
|
fi
|
||||||
elif [[ "${release}" == "ubuntu" ]]; then
|
elif [[ "${release}" == "ubuntu" ]]; then
|
||||||
if [[ ${os_version} -lt 20 ]]; then
|
if [[ ${os_version} -lt 20 ]]; then
|
||||||
echo -e "${red}please use Ubuntu 20 or higher version!${plain}\n" && exit 1
|
echo -e "${red}please use Ubuntu 20 or higher version!${plain}\n" && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
elif [[ "${release}" == "fedora" ]]; then
|
elif [[ "${release}" == "fedora" ]]; then
|
||||||
if [[ ${os_version} -lt 36 ]]; then
|
if [[ ${os_version} -lt 36 ]]; then
|
||||||
echo -e "${red}please use Fedora 36 or higher version!${plain}\n" && exit 1
|
echo -e "${red}please use Fedora 36 or higher version!${plain}\n" && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
elif [[ "${release}" == "debian" ]]; then
|
elif [[ "${release}" == "debian" ]]; then
|
||||||
|
@ -68,7 +68,7 @@ install_base() {
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
#This function will be called when user installed x-ui out of sercurity
|
# This function will be called when user installed x-ui out of sercurity
|
||||||
config_after_install() {
|
config_after_install() {
|
||||||
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
|
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
|
||||||
read -p "Do you want to continue with the modification [y/n]? ": config_confirm
|
read -p "Do you want to continue with the modification [y/n]? ": config_confirm
|
||||||
|
|
|
@ -3,6 +3,7 @@ package controller
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"x-ui/database/model"
|
"x-ui/database/model"
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
"x-ui/web/global"
|
"x-ui/web/global"
|
||||||
|
@ -40,7 +41,6 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
|
||||||
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
||||||
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
|
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
|
||||||
g.POST("/delDepletedClients/:id", a.delDepletedClients)
|
g.POST("/delDepletedClients/:id", a.delDepletedClients)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *InboundController) startTask() {
|
func (a *InboundController) startTask() {
|
||||||
|
@ -79,6 +79,7 @@ func (a *InboundController) getInbound(c *gin.Context) {
|
||||||
}
|
}
|
||||||
jsonObj(c, inbound, nil)
|
jsonObj(c, inbound, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *InboundController) getClientTraffics(c *gin.Context) {
|
func (a *InboundController) getClientTraffics(c *gin.Context) {
|
||||||
email := c.Param("email")
|
email := c.Param("email")
|
||||||
clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email)
|
clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email)
|
||||||
|
|
|
@ -45,7 +45,9 @@
|
||||||
<a-tag :color="statsColor(record, client.email)">
|
<a-tag :color="statsColor(record, client.email)">
|
||||||
[[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] /
|
[[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] /
|
||||||
<template v-if="client.totalGB > 0">[[client._totalGB]]GB</template>
|
<template v-if="client.totalGB > 0">[[client._totalGB]]GB</template>
|
||||||
<template v-else>♾</template>
|
<template v-else>
|
||||||
|
<svg style="fill: currentColor; height: 16px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"/></svg>
|
||||||
|
</template>
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -224,7 +224,9 @@
|
||||||
<template v-if="dbInbound.total > 0">
|
<template v-if="dbInbound.total > 0">
|
||||||
[[ sizeFormat(dbInbound.total) ]]
|
[[ sizeFormat(dbInbound.total) ]]
|
||||||
</template>
|
</template>
|
||||||
<template v-else>♾</template>
|
<template v-else>
|
||||||
|
<svg style="fill: currentColor; height: 16px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"/></svg>
|
||||||
|
</template>
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -5,23 +5,26 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"x-ui/database"
|
|
||||||
"x-ui/database/model"
|
|
||||||
"x-ui/logger"
|
|
||||||
"x-ui/web/service"
|
|
||||||
"x-ui/xray"
|
|
||||||
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"x-ui/database"
|
||||||
|
"x-ui/database/model"
|
||||||
|
"x-ui/logger"
|
||||||
|
"x-ui/xray"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CheckClientIpJob struct {
|
type CheckClientIpJob struct {}
|
||||||
xrayService service.XrayService
|
|
||||||
}
|
|
||||||
|
|
||||||
var job *CheckClientIpJob
|
var job *CheckClientIpJob
|
||||||
var disAllowedIps []string
|
var disAllowedIps []string
|
||||||
|
var ipFiles = []string{
|
||||||
|
xray.GetBlockedIPsPath(),
|
||||||
|
xray.GetIPLimitLogPath(),
|
||||||
|
xray.GetIPLimitBannedLogPath(),
|
||||||
|
xray.GetAccessPersistentLogPath(),
|
||||||
|
}
|
||||||
|
|
||||||
func NewCheckClientIpJob() *CheckClientIpJob {
|
func NewCheckClientIpJob() *CheckClientIpJob {
|
||||||
job = new(CheckClientIpJob)
|
job = new(CheckClientIpJob)
|
||||||
|
@ -31,37 +34,28 @@ func NewCheckClientIpJob() *CheckClientIpJob {
|
||||||
func (j *CheckClientIpJob) Run() {
|
func (j *CheckClientIpJob) Run() {
|
||||||
logger.Debug("Check Client IP Job...")
|
logger.Debug("Check Client IP Job...")
|
||||||
|
|
||||||
if hasLimitIp() {
|
// create files required for iplimit if not exists
|
||||||
//create log file for Fail2ban IP Limit
|
for i := 0; i < len(ipFiles); i++ {
|
||||||
logIpFile, err := os.OpenFile("/var/log/3xipl.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
file, err := os.OpenFile(ipFiles[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
||||||
checkError(err)
|
j.checkError(err)
|
||||||
defer logIpFile.Close()
|
defer file.Close()
|
||||||
log.SetOutput(logIpFile)
|
|
||||||
log.SetFlags(log.LstdFlags)
|
|
||||||
|
|
||||||
//create file to collect access.log to another file accessp.log (p=persistent)
|
|
||||||
logAccessP, err := os.OpenFile("/usr/local/x-ui/accessp.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
|
||||||
checkError(err)
|
|
||||||
defer logAccessP.Close()
|
|
||||||
|
|
||||||
processLogFile()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for limit ip
|
||||||
|
if j.hasLimitIp() {
|
||||||
|
j.processLogFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
// write to blocked ips
|
||||||
blockedIps := []byte(strings.Join(disAllowedIps, ","))
|
blockedIps := []byte(strings.Join(disAllowedIps, ","))
|
||||||
|
err := os.WriteFile(xray.GetBlockedIPsPath(), blockedIps, 0644)
|
||||||
// check if file exists, if not create one
|
j.checkError(err)
|
||||||
_, err := os.Stat(xray.GetBlockedIPsPath())
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
_, err = os.OpenFile(xray.GetBlockedIPsPath(), os.O_RDWR|os.O_CREATE, 0755)
|
|
||||||
checkError(err)
|
|
||||||
}
|
|
||||||
err = os.WriteFile(xray.GetBlockedIPsPath(), blockedIps, 0755)
|
|
||||||
checkError(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasLimitIp() bool {
|
func (j *CheckClientIpJob) hasLimitIp() bool {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
|
|
||||||
err := db.Model(model.Inbound{}).Find(&inbounds).Error
|
err := db.Model(model.Inbound{}).Find(&inbounds).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
@ -83,11 +77,12 @@ func hasLimitIp() bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func processLogFile() {
|
func (j *CheckClientIpJob) processLogFile() {
|
||||||
accessLogPath := GetAccessLogPath()
|
accessLogPath := xray.GetAccessLogPath()
|
||||||
if accessLogPath == "" {
|
if accessLogPath == "" {
|
||||||
logger.Warning("access.log doesn't exist in your config.json")
|
logger.Warning("access.log doesn't exist in your config.json")
|
||||||
return
|
return
|
||||||
|
@ -95,7 +90,7 @@ func processLogFile() {
|
||||||
|
|
||||||
data, err := os.ReadFile(accessLogPath)
|
data, err := os.ReadFile(accessLogPath)
|
||||||
InboundClientIps := make(map[string][]string)
|
InboundClientIps := make(map[string][]string)
|
||||||
checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
lines := strings.Split(string(data), "\n")
|
lines := strings.Split(string(data), "\n")
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
|
@ -116,7 +111,7 @@ func processLogFile() {
|
||||||
matchesEmail = strings.TrimSpace(strings.Split(matchesEmail, "email: ")[1])
|
matchesEmail = strings.TrimSpace(strings.Split(matchesEmail, "email: ")[1])
|
||||||
|
|
||||||
if InboundClientIps[matchesEmail] != nil {
|
if InboundClientIps[matchesEmail] != nil {
|
||||||
if contains(InboundClientIps[matchesEmail], ip) {
|
if j.contains(InboundClientIps[matchesEmail], ip) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
||||||
|
@ -125,68 +120,50 @@ func processLogFile() {
|
||||||
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disAllowedIps = []string{}
|
disAllowedIps = []string{}
|
||||||
shouldCleanLog := false
|
shouldCleanLog := false
|
||||||
|
|
||||||
for clientEmail, ips := range InboundClientIps {
|
for clientEmail, ips := range InboundClientIps {
|
||||||
inboundClientIps, err := GetInboundClientIps(clientEmail)
|
inboundClientIps, err := j.getInboundClientIps(clientEmail)
|
||||||
sort.Strings(ips)
|
sort.Strings(ips)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
addInboundClientIps(clientEmail, ips)
|
j.addInboundClientIps(clientEmail, ips)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
shouldCleanLog = updateInboundClientIps(inboundClientIps, clientEmail, ips)
|
shouldCleanLog = j.updateInboundClientIps(inboundClientIps, clientEmail, ips)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// added 3 seconds delay before cleaning logs to reduce chance of logging IP that already has been banned
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
//added 3 seconds delay before cleaning logs to reduce chance of logging IP that already has been banned
|
|
||||||
if shouldCleanLog {
|
if shouldCleanLog {
|
||||||
//copy log
|
// copy access log to persistent file
|
||||||
logAccessP, err := os.OpenFile("/usr/local/x-ui/accessp.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
||||||
checkError(err)
|
j.checkError(err)
|
||||||
input, err := os.ReadFile(accessLogPath)
|
input, err := os.ReadFile(accessLogPath)
|
||||||
checkError(err)
|
j.checkError(err)
|
||||||
if _, err := logAccessP.Write(input); err != nil {
|
if _, err := logAccessP.Write(input); err != nil {
|
||||||
checkError(err)
|
j.checkError(err)
|
||||||
}
|
}
|
||||||
defer logAccessP.Close()
|
defer logAccessP.Close()
|
||||||
// clean log
|
|
||||||
if err := os.Truncate(GetAccessLogPath(), 0); err != nil {
|
|
||||||
checkError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// clean access log
|
||||||
|
if err := os.Truncate(xray.GetAccessLogPath(), 0); err != nil {
|
||||||
|
j.checkError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func GetAccessLogPath() string {
|
|
||||||
|
|
||||||
config, err := os.ReadFile(xray.GetConfigPath())
|
func (j *CheckClientIpJob) checkError(e error) {
|
||||||
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 {
|
if e != nil {
|
||||||
logger.Warning("client ip job err:", e)
|
logger.Warning("client ip job err:", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func contains(s []string, str string) bool {
|
|
||||||
|
func (j *CheckClientIpJob) contains(s []string, str string) bool {
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
if v == str {
|
if v == str {
|
||||||
return true
|
return true
|
||||||
|
@ -195,7 +172,8 @@ func contains(s []string, str string) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
func GetInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
|
|
||||||
|
func (j *CheckClientIpJob) getInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
InboundClientIps := &model.InboundClientIps{}
|
InboundClientIps := &model.InboundClientIps{}
|
||||||
err := db.Model(model.InboundClientIps{}).Where("client_email = ?", clientEmail).First(InboundClientIps).Error
|
err := db.Model(model.InboundClientIps{}).Where("client_email = ?", clientEmail).First(InboundClientIps).Error
|
||||||
|
@ -204,10 +182,11 @@ func GetInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
|
||||||
}
|
}
|
||||||
return InboundClientIps, nil
|
return InboundClientIps, nil
|
||||||
}
|
}
|
||||||
func addInboundClientIps(clientEmail string, ips []string) error {
|
|
||||||
|
func (j *CheckClientIpJob) addInboundClientIps(clientEmail string, ips []string) error {
|
||||||
inboundClientIps := &model.InboundClientIps{}
|
inboundClientIps := &model.InboundClientIps{}
|
||||||
jsonIps, err := json.Marshal(ips)
|
jsonIps, err := json.Marshal(ips)
|
||||||
checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
inboundClientIps.ClientEmail = clientEmail
|
inboundClientIps.ClientEmail = clientEmail
|
||||||
inboundClientIps.Ips = string(jsonIps)
|
inboundClientIps.Ips = string(jsonIps)
|
||||||
|
@ -229,17 +208,17 @@ func addInboundClientIps(clientEmail string, ips []string) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmail string, ips []string) bool {
|
|
||||||
|
|
||||||
|
func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmail string, ips []string) bool {
|
||||||
jsonIps, err := json.Marshal(ips)
|
jsonIps, err := json.Marshal(ips)
|
||||||
checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
inboundClientIps.ClientEmail = clientEmail
|
inboundClientIps.ClientEmail = clientEmail
|
||||||
inboundClientIps.Ips = string(jsonIps)
|
inboundClientIps.Ips = string(jsonIps)
|
||||||
|
|
||||||
// check inbound limitation
|
// check inbound limitation
|
||||||
inbound, err := GetInboundByEmail(clientEmail)
|
inbound, err := j.getInboundByEmail(clientEmail)
|
||||||
checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
if inbound.Settings == "" {
|
if inbound.Settings == "" {
|
||||||
logger.Debug("wrong data ", inbound)
|
logger.Debug("wrong data ", inbound)
|
||||||
|
@ -251,13 +230,20 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
|
||||||
clients := settings["clients"]
|
clients := settings["clients"]
|
||||||
shouldCleanLog := false
|
shouldCleanLog := false
|
||||||
|
|
||||||
|
// create iplimit log file channel
|
||||||
|
logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("failed to create or open ip limit log file: %s", err)
|
||||||
|
}
|
||||||
|
defer logIpFile.Close()
|
||||||
|
log.SetOutput(logIpFile)
|
||||||
|
log.SetFlags(log.LstdFlags)
|
||||||
|
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.Email == clientEmail {
|
if client.Email == clientEmail {
|
||||||
|
|
||||||
limitIp := client.LimitIP
|
limitIp := client.LimitIP
|
||||||
|
|
||||||
if limitIp != 0 {
|
if limitIp != 0 {
|
||||||
|
|
||||||
shouldCleanLog = true
|
shouldCleanLog = true
|
||||||
|
|
||||||
if limitIp < len(ips) && inbound.Enable {
|
if limitIp < len(ips) && inbound.Enable {
|
||||||
|
@ -280,27 +266,14 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
|
||||||
return shouldCleanLog
|
return shouldCleanLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func DisableInbound(id int) error {
|
func (j *CheckClientIpJob) getInboundByEmail(clientEmail string) (*model.Inbound, 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()
|
db := database.GetDB()
|
||||||
var inbounds *model.Inbound
|
var inbounds *model.Inbound
|
||||||
|
|
||||||
err := db.Model(model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").Find(&inbounds).Error
|
err := db.Model(model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").Find(&inbounds).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return inbounds, nil
|
return inbounds, nil
|
||||||
}
|
}
|
||||||
|
|
25
web/job/clear_logs_job.go
Normal file
25
web/job/clear_logs_job.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package job
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"x-ui/logger"
|
||||||
|
"x-ui/xray"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClearLogsJob struct{}
|
||||||
|
|
||||||
|
func NewClearLogsJob() *ClearLogsJob {
|
||||||
|
return new(ClearLogsJob)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here Run is an interface method of the Job interface
|
||||||
|
func (j *ClearLogsJob) Run() {
|
||||||
|
logFiles := []string{xray.GetIPLimitLogPath(), xray.GetIPLimitBannedLogPath(), xray.GetAccessPersistentLogPath()}
|
||||||
|
|
||||||
|
// clear log files
|
||||||
|
for i := 0; i < len(logFiles); i++ {
|
||||||
|
if err := os.Truncate(logFiles[i], 0); err != nil {
|
||||||
|
logger.Warning("clear logs job err:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"x-ui/database"
|
"x-ui/database"
|
||||||
"x-ui/database/model"
|
"x-ui/database/model"
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
|
@ -74,7 +75,6 @@ func (s *InboundService) getAllEmails() ([]string, error) {
|
||||||
FROM inbounds,
|
FROM inbounds,
|
||||||
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
||||||
`).Scan(&emails).Error
|
`).Scan(&emails).Error
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -816,7 +816,8 @@ func (s *InboundService) UpdateClientStat(email string, client *model.Client) er
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"email": client.Email,
|
"email": client.Email,
|
||||||
"total": client.TotalGB,
|
"total": client.TotalGB,
|
||||||
"expiry_time": client.ExpiryTime})
|
"expiry_time": client.ExpiryTime,
|
||||||
|
})
|
||||||
err := result.Error
|
err := result.Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1068,8 +1069,8 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry_time int64) error {
|
func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry_time int64) error {
|
||||||
_, inbound, err := s.GetClientInboundByEmail(clientEmail)
|
_, inbound, err := s.GetClientInboundByEmail(clientEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1126,7 +1127,6 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
|
func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
|
||||||
|
@ -1137,7 +1137,6 @@ func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
|
||||||
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
|
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
|
||||||
|
|
||||||
err := result.Error
|
err := result.Error
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1209,7 +1208,6 @@ func (s *InboundService) ResetAllClientTraffics(id int) error {
|
||||||
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
|
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
|
||||||
|
|
||||||
err := result.Error
|
err := result.Error
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1224,7 +1222,6 @@ func (s *InboundService) ResetAllTraffics() error {
|
||||||
Updates(map[string]interface{}{"up": 0, "down": 0})
|
Updates(map[string]interface{}{"up": 0, "down": 0})
|
||||||
|
|
||||||
err := result.Error
|
err := result.Error
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1411,7 +1408,6 @@ func (s *InboundService) ClearClientIps(clientEmail string) error {
|
||||||
Where("client_email = ?", clientEmail).
|
Where("client_email = ?", clientEmail).
|
||||||
Update("ips", "")
|
Update("ips", "")
|
||||||
err := result.Error
|
err := result.Error
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
"x-ui/database"
|
"x-ui/database"
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
|
@ -250,7 +251,6 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) StopXrayService() (string error) {
|
func (s *ServerService) StopXrayService() (string error) {
|
||||||
|
|
||||||
err := s.xrayService.StopXray()
|
err := s.xrayService.StopXray()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("stop xray failed:", err)
|
logger.Error("stop xray failed:", err)
|
||||||
|
@ -261,7 +261,6 @@ func (s *ServerService) StopXrayService() (string error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) RestartXrayService() (string error) {
|
func (s *ServerService) RestartXrayService() (string error) {
|
||||||
|
|
||||||
s.xrayService.StopXray()
|
s.xrayService.StopXray()
|
||||||
defer func() {
|
defer func() {
|
||||||
err := s.xrayService.RestartXray(true)
|
err := s.xrayService.RestartXray(true)
|
||||||
|
@ -377,7 +376,6 @@ func (s *ServerService) UpdateXray(version string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetLogs(count string, logLevel string) ([]string, error) {
|
func (s *ServerService) GetLogs(count string, logLevel string) ([]string, error) {
|
||||||
|
|
|
@ -253,6 +253,9 @@ func (s *Server) startTask() {
|
||||||
// check client ips from log file every 20 sec
|
// check client ips from log file every 20 sec
|
||||||
s.cron.AddJob("@every 20s", job.NewCheckClientIpJob())
|
s.cron.AddJob("@every 20s", job.NewCheckClientIpJob())
|
||||||
|
|
||||||
|
// check client ips from log file every 3 day
|
||||||
|
s.cron.AddJob("@every 3d", job.NewClearLogsJob())
|
||||||
|
|
||||||
// Make a traffic condition every day, 8:30
|
// Make a traffic condition every day, 8:30
|
||||||
var entry cron.EntryID
|
var entry cron.EntryID
|
||||||
isTgbotenabled, err := s.settingService.GetTgbotenabled()
|
isTgbotenabled, err := s.settingService.GetTgbotenabled()
|
||||||
|
|
267
x-ui.sh
267
x-ui.sh
|
@ -56,6 +56,13 @@ elif [[ "${release}" == "debian" ]]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Declare Variables
|
||||||
|
log_folder="${XUI_LOG_FOLDER:=/var/log}"
|
||||||
|
iplimit_log_path="${log_folder}/3xipl.log"
|
||||||
|
iplimit_banned_log_path="${log_folder}/3xipl-banned.log"
|
||||||
|
|
||||||
|
|
||||||
confirm() {
|
confirm() {
|
||||||
if [[ $# > 1 ]]; then
|
if [[ $# > 1 ]]; then
|
||||||
echo && read -p "$1 [Default $2]: " temp
|
echo && read -p "$1 [Default $2]: " temp
|
||||||
|
@ -296,25 +303,28 @@ enable_bbr() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check the OS and install necessary packages
|
# Check the OS and install necessary packages
|
||||||
if [[ "$(cat /etc/os-release | grep -E '^ID=' | awk -F '=' '{print $2}')" == "ubuntu" ]]; then
|
case "${release}" in
|
||||||
sudo apt-get update && sudo apt-get install -yqq --no-install-recommends ca-certificates
|
ubuntu|debian)
|
||||||
elif [[ "$(cat /etc/os-release | grep -E '^ID=' | awk -F '=' '{print $2}')" == "debian" ]]; then
|
apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
|
||||||
sudo apt-get update && sudo apt-get install -yqq --no-install-recommends ca-certificates
|
;;
|
||||||
elif [[ "$(cat /etc/os-release | grep -E '^ID=' | awk -F '=' '{print $2}')" == "fedora" ]]; then
|
centos)
|
||||||
sudo dnf -y update && sudo dnf -y install ca-certificates
|
yum -y update && yum -y install ca-certificates
|
||||||
elif [[ "$(cat /etc/os-release | grep -E '^ID=' | awk -F '=' '{print $2}')" == "centos" ]]; then
|
;;
|
||||||
sudo yum -y update && sudo yum -y install ca-certificates
|
fedora)
|
||||||
else
|
dnf -y update && dnf -y install ca-certificates
|
||||||
echo "Unsupported operating system. Please check the script and install the necessary packages manually."
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
# Enable BBR
|
# Enable BBR
|
||||||
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
|
echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf
|
||||||
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
|
echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf
|
||||||
|
|
||||||
# Apply changes
|
# Apply changes
|
||||||
sudo sysctl -p
|
sysctl -p
|
||||||
|
|
||||||
# Verify that BBR is enabled
|
# Verify that BBR is enabled
|
||||||
if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then
|
if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then
|
||||||
|
@ -434,24 +444,24 @@ show_xray_status() {
|
||||||
open_ports() {
|
open_ports() {
|
||||||
if ! command -v ufw &>/dev/null; then
|
if ! command -v ufw &>/dev/null; then
|
||||||
echo "ufw firewall is not installed. Installing now..."
|
echo "ufw firewall is not installed. Installing now..."
|
||||||
sudo apt-get update
|
apt-get update
|
||||||
sudo apt-get install -y ufw
|
apt-get install -y ufw
|
||||||
else
|
else
|
||||||
echo "ufw firewall is already installed"
|
echo "ufw firewall is already installed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if the firewall is inactive
|
# Check if the firewall is inactive
|
||||||
if sudo ufw status | grep -q "Status: active"; then
|
if ufw status | grep -q "Status: active"; then
|
||||||
echo "firewall is already active"
|
echo "firewall is already active"
|
||||||
else
|
else
|
||||||
# Open the necessary ports
|
# Open the necessary ports
|
||||||
sudo ufw allow ssh
|
ufw allow ssh
|
||||||
sudo ufw allow http
|
ufw allow http
|
||||||
sudo ufw allow https
|
ufw allow https
|
||||||
sudo ufw allow 2053/tcp
|
ufw allow 2053/tcp
|
||||||
|
|
||||||
# Enable the firewall
|
# Enable the firewall
|
||||||
sudo ufw --force enable
|
ufw --force enable
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Prompt the user to enter a list of ports
|
# Prompt the user to enter a list of ports
|
||||||
|
@ -472,15 +482,15 @@ open_ports() {
|
||||||
end_port=$(echo $port | cut -d'-' -f2)
|
end_port=$(echo $port | cut -d'-' -f2)
|
||||||
# Loop through the range and open each port
|
# Loop through the range and open each port
|
||||||
for ((i = start_port; i <= end_port; i++)); do
|
for ((i = start_port; i <= end_port; i++)); do
|
||||||
sudo ufw allow $i
|
ufw allow $i
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
sudo ufw allow "$port"
|
ufw allow "$port"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Confirm that the ports are open
|
# Confirm that the ports are open
|
||||||
sudo ufw status | grep $ports
|
ufw status | grep $ports
|
||||||
}
|
}
|
||||||
|
|
||||||
update_geo() {
|
update_geo() {
|
||||||
|
@ -539,7 +549,7 @@ ssl_cert_issue_main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ssl_cert_issue() {
|
ssl_cert_issue() {
|
||||||
#check for acme.sh first
|
# check for acme.sh first
|
||||||
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
||||||
echo "acme.sh could not be found. we will install it"
|
echo "acme.sh could not be found. we will install it"
|
||||||
install_acme
|
install_acme
|
||||||
|
@ -548,24 +558,30 @@ ssl_cert_issue() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
#install socat second
|
# install socat second
|
||||||
if [[ "${release}" == "centos" ]] || [[ "${release}" == "fedora" ]]; then
|
case "${release}" in
|
||||||
yum install socat -y
|
ubuntu|debian)
|
||||||
else
|
apt update && apt install socat -y ;;
|
||||||
apt install socat -y
|
centos)
|
||||||
fi
|
yum -y update && yum -y install socat ;;
|
||||||
|
fedora)
|
||||||
|
dnf -y update && dnf -y install socat ;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "install socat failed,please check logs"
|
LOGE "install socat failed, please check logs"
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
LOGI "install socat succeed..."
|
LOGI "install socat succeed..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#get the domain here,and we need verify it
|
# get the domain here,and we need verify it
|
||||||
local domain=""
|
local domain=""
|
||||||
read -p "Please enter your domain name:" domain
|
read -p "Please enter your domain name:" domain
|
||||||
LOGD "your domain is:${domain},check it..."
|
LOGD "your domain is:${domain},check it..."
|
||||||
#here we need to judge whether there exists cert already
|
# here we need to judge whether there exists cert already
|
||||||
local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
|
local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
|
||||||
|
|
||||||
if [ ${currentCert} == ${domain} ]; then
|
if [ ${currentCert} == ${domain} ]; then
|
||||||
|
@ -577,7 +593,7 @@ ssl_cert_issue() {
|
||||||
LOGI "your domain is ready for issuing cert now..."
|
LOGI "your domain is ready for issuing cert now..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#create a directory for install cert
|
# create a directory for install cert
|
||||||
certPath="/root/cert/${domain}"
|
certPath="/root/cert/${domain}"
|
||||||
if [ ! -d "$certPath" ]; then
|
if [ ! -d "$certPath" ]; then
|
||||||
mkdir -p "$certPath"
|
mkdir -p "$certPath"
|
||||||
|
@ -586,15 +602,15 @@ ssl_cert_issue() {
|
||||||
mkdir -p "$certPath"
|
mkdir -p "$certPath"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#get needed port here
|
# get needed port here
|
||||||
local WebPort=80
|
local WebPort=80
|
||||||
read -p "please choose which port do you use,default will be 80 port:" WebPort
|
read -p "please choose which port do you use,default will be 80 port:" WebPort
|
||||||
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
|
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
|
||||||
LOGE "your input ${WebPort} is invalid,will use default port"
|
LOGE "your input ${WebPort} is invalid,will use default port"
|
||||||
fi
|
fi
|
||||||
LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..."
|
LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..."
|
||||||
#NOTE:This should be handled by user
|
# NOTE:This should be handled by user
|
||||||
#open the port and kill the occupied progress
|
# open the port and kill the occupied progress
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort}
|
~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort}
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
@ -604,7 +620,7 @@ ssl_cert_issue() {
|
||||||
else
|
else
|
||||||
LOGE "issue certs succeed,installing certs..."
|
LOGE "issue certs succeed,installing certs..."
|
||||||
fi
|
fi
|
||||||
#install cert
|
# install cert
|
||||||
~/.acme.sh/acme.sh --installcert -d ${domain} \
|
~/.acme.sh/acme.sh --installcert -d ${domain} \
|
||||||
--key-file /root/cert/${domain}/privkey.pem \
|
--key-file /root/cert/${domain}/privkey.pem \
|
||||||
--fullchain-file /root/cert/${domain}/fullchain.pem
|
--fullchain-file /root/cert/${domain}/fullchain.pem
|
||||||
|
@ -628,18 +644,17 @@ ssl_cert_issue() {
|
||||||
ls -lah cert/*
|
ls -lah cert/*
|
||||||
chmod 755 $certPath/*
|
chmod 755 $certPath/*
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
warp_cloudflare() {
|
warp_cloudflare() {
|
||||||
echo -e "${green}\t1.${plain} install WARP"
|
echo -e "${green}\t1.${plain} Install WARP socks5 proxy"
|
||||||
echo -e "${green}\t2.${plain} Account Type (free, plus, team)"
|
echo -e "${green}\t2.${plain} Account Type (free, plus, team)"
|
||||||
echo -e "${green}\t3.${plain} Turn on/off WireProxy"
|
echo -e "${green}\t3.${plain} Turn on/off WireProxy"
|
||||||
echo -e "${green}\t4.${plain} Uninstall WARP"
|
echo -e "${green}\t4.${plain} Uninstall WARP"
|
||||||
read -p "Choose an option: " choice
|
read -p "Choose an option: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1)
|
1)
|
||||||
bash <(curl -sSL https://gist.githubusercontent.com/hamid-gh98/dc5dd9b0cc5b0412af927b1ccdb294c7/raw/install_warp_proxy.sh)
|
bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
warp a
|
warp a
|
||||||
|
@ -679,8 +694,8 @@ run_speedtest() {
|
||||||
echo "Error: Package manager not found. You may need to install Speedtest manually."
|
echo "Error: Package manager not found. You may need to install Speedtest manually."
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
curl -s $speedtest_install_script | sudo bash
|
curl -s $speedtest_install_script | bash
|
||||||
sudo $pkg_manager install -y speedtest
|
$pkg_manager install -y speedtest
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -688,6 +703,70 @@ run_speedtest() {
|
||||||
speedtest
|
speedtest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create_iplimit_jails() {
|
||||||
|
# Use default bantime if not passed => 5 minutes
|
||||||
|
local bantime="${1:-5}"
|
||||||
|
|
||||||
|
cat << EOF > /etc/fail2ban/jail.d/3x-ipl.conf
|
||||||
|
[3x-ipl]
|
||||||
|
enabled=true
|
||||||
|
filter=3x-ipl
|
||||||
|
action=3x-ipl
|
||||||
|
logpath=${iplimit_log_path}
|
||||||
|
maxretry=3
|
||||||
|
findtime=100
|
||||||
|
bantime=${bantime}m
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat << EOF > /etc/fail2ban/filter.d/3x-ipl.conf
|
||||||
|
[Definition]
|
||||||
|
datepattern = ^%%Y/%%m/%%d %%H:%%M:%%S
|
||||||
|
failregex = \[LIMIT_IP\]\s*Email\s*=\s*<F-USER>.+</F-USER>\s*\|\|\s*SRC\s*=\s*<ADDR>
|
||||||
|
ignoreregex =
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
|
||||||
|
[INCLUDES]
|
||||||
|
before = iptables-common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
actionstart = <iptables> -N f2b-<name>
|
||||||
|
<iptables> -A f2b-<name> -j <returntype>
|
||||||
|
<iptables> -I <chain> -p <protocol> -j f2b-<name>
|
||||||
|
|
||||||
|
actionstop = <iptables> -D <chain> -p <protocol> -j f2b-<name>
|
||||||
|
<actionflush>
|
||||||
|
<iptables> -X f2b-<name>
|
||||||
|
|
||||||
|
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
||||||
|
|
||||||
|
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
|
||||||
|
echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") BAN [Email] = <F-USER> [IP] = <ip> banned for <bantime> seconds." >> ${iplimit_banned_log_path}
|
||||||
|
|
||||||
|
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
|
||||||
|
echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = <F-USER> [IP] = <ip> unbanned." >> ${iplimit_banned_log_path}
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo -e "${green}Created Ip Limit jail files with a bantime of ${bantime} minutes.${plain}"
|
||||||
|
}
|
||||||
|
|
||||||
|
iplimit_remove_conflicts() {
|
||||||
|
local jail_files=(
|
||||||
|
/etc/fail2ban/jail.conf
|
||||||
|
/etc/fail2ban/jail.local
|
||||||
|
)
|
||||||
|
|
||||||
|
for file in "${jail_files[@]}"; do
|
||||||
|
# Check for [3x-ipl] config in jail file then remove it
|
||||||
|
if test -f "${file}" && grep -qw '3x-ipl' ${file}; then
|
||||||
|
sed -i "/\[3x-ipl\]/,/^$/d" ${file}
|
||||||
|
echo -e "${yellow}Removing conflicts of [3x-ipl] in jail (${file})!${plain}\n"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
iplimit_main() {
|
iplimit_main() {
|
||||||
echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
|
echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
|
||||||
echo -e "${green}\t2.${plain} Change Ban Duration"
|
echo -e "${green}\t2.${plain} Change Ban Duration"
|
||||||
|
@ -709,9 +788,8 @@ iplimit_main() {
|
||||||
2)
|
2)
|
||||||
read -rp "Please enter new Ban Duration in Minutes [default 5]: " NUM
|
read -rp "Please enter new Ban Duration in Minutes [default 5]: " NUM
|
||||||
if [[ $NUM =~ ^[0-9]+$ ]]; then
|
if [[ $NUM =~ ^[0-9]+$ ]]; then
|
||||||
echo -e "\n[3x-ipl]\nenabled=true\nfilter=3x-ipl\naction=3x-ipl\nlogpath=/var/log/3xipl.log\nmaxretry=3\nfindtime=100\nbantime=${NUM}m" > /etc/fail2ban/jail.d/3x-ipl.conf
|
create_iplimit_jail ${NUM}
|
||||||
sudo systemctl restart fail2ban
|
systemctl restart fail2ban
|
||||||
echo -e "${green}Bantime set to ${NUM} minutes successfully.${plain}"
|
|
||||||
else
|
else
|
||||||
echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
|
echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
|
||||||
fi
|
fi
|
||||||
|
@ -727,9 +805,9 @@ iplimit_main() {
|
||||||
fi
|
fi
|
||||||
iplimit_main ;;
|
iplimit_main ;;
|
||||||
4)
|
4)
|
||||||
if test -f "/var/log/3xipl-banned.log"; then
|
if test -f "${iplimit_banned_log_path}"; then
|
||||||
if [[ -s "/var/log/3xipl-banned.log" ]]; then
|
if [[ -s "${iplimit_banned_log_path}" ]]; then
|
||||||
cat /var/log/3xipl-banned.log
|
cat ${iplimit_banned_log_path}
|
||||||
else
|
else
|
||||||
echo -e "${red}Log file is empty.${plain}\n"
|
echo -e "${red}Log file is empty.${plain}\n"
|
||||||
fi
|
fi
|
||||||
|
@ -749,11 +827,11 @@ install_iplimit() {
|
||||||
# Check the OS and install necessary packages
|
# Check the OS and install necessary packages
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
ubuntu|debian)
|
ubuntu|debian)
|
||||||
sudo apt-get update && sudo apt-get install fail2ban -y ;;
|
apt update && apt install fail2ban -y ;;
|
||||||
centos)
|
centos)
|
||||||
sudo yum -y update && sudo yum -y install fail2ban ;;
|
yum -y update && yum -y install fail2ban ;;
|
||||||
fedora)
|
fedora)
|
||||||
sudo dnf -y update && sudo dnf -y install fail2ban ;;
|
dnf -y update && dnf -y install fail2ban ;;
|
||||||
*)
|
*)
|
||||||
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
||||||
exit 1 ;;
|
exit 1 ;;
|
||||||
|
@ -765,63 +843,30 @@ install_iplimit() {
|
||||||
|
|
||||||
echo -e "${green}Configuring IP Limit...${plain}\n"
|
echo -e "${green}Configuring IP Limit...${plain}\n"
|
||||||
|
|
||||||
#Check if [3x-ipl] exists in jail.local (just making sure there's no double config for jail)
|
# make sure there's no conflict for jail files
|
||||||
if grep -qw '3x-ipl' /etc/fail2ban/jail.local || grep -qw '3x-ipl' /etc/fail2ban/jail.conf; then
|
iplimit_remove_conflicts
|
||||||
echo -e "${red}Found conflicts in /etc/fail2ban/jail.conf or jail.local file!\nPlease manually remove anything related 3x-ipl in that files and try again.\nInstallation of IP Limit failed.${plain}\n"
|
|
||||||
exit 1
|
# Check if log file exists
|
||||||
|
if ! test -f "${iplimit_banned_log_path}"; then
|
||||||
|
touch ${iplimit_banned_log_path}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#Check if log file exists
|
# Check if service log file exists so fail2ban won't return error
|
||||||
if ! test -f "/var/log/3xipl-banned.log"; then
|
if ! test -f "${iplimit_log_path}"; then
|
||||||
touch /var/log/3xipl-banned.log
|
touch ${iplimit_log_path}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#Check if service log file exists so fail2ban won't return error
|
# Create the iplimit jail files
|
||||||
if ! test -f "/var/log/3xipl.log"; then
|
# we didn't pass the bantime here to use the default value
|
||||||
touch /var/log/3xipl.log
|
create_iplimit_jails
|
||||||
fi
|
|
||||||
|
|
||||||
|
# Launching fail2ban
|
||||||
echo -e "\n[3x-ipl]\nenabled=true\nfilter=3x-ipl\naction=3x-ipl\nlogpath=/var/log/3xipl.log\nmaxretry=3\nfindtime=100\nbantime=5m" > /etc/fail2ban/jail.d/3x-ipl.conf
|
if ! systemctl is-active --quiet fail2ban; then
|
||||||
|
systemctl start fail2ban
|
||||||
sudo cat > /etc/fail2ban/filter.d/3x-ipl.conf << EOF
|
|
||||||
[Definition]
|
|
||||||
datepattern = ^%%Y/%%m/%%d %%H:%%M:%%S
|
|
||||||
failregex = \[LIMIT_IP\]\s*Email\s*=\s*<F-USER>.+</F-USER>\s*\|\|\s*SRC\s*=\s*<ADDR>
|
|
||||||
ignoreregex =
|
|
||||||
EOF
|
|
||||||
|
|
||||||
sudo cat > /etc/fail2ban/action.d/3x-ipl.conf << 'EOF'
|
|
||||||
[INCLUDES]
|
|
||||||
before = iptables-common.conf
|
|
||||||
|
|
||||||
[Definition]
|
|
||||||
actionstart = <iptables> -N f2b-<name>
|
|
||||||
<iptables> -A f2b-<name> -j <returntype>
|
|
||||||
<iptables> -I <chain> -p <protocol> -j f2b-<name>
|
|
||||||
|
|
||||||
actionstop = <iptables> -D <chain> -p <protocol> -j f2b-<name>
|
|
||||||
<actionflush>
|
|
||||||
<iptables> -X f2b-<name>
|
|
||||||
|
|
||||||
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
|
|
||||||
|
|
||||||
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
|
|
||||||
echo "$(date +"%%Y/%%m/%%d %%H:%%M:%%S") BAN [Email] = <F-USER> [IP] = <ip> banned for <bantime> seconds." >> /var/log/3xipl-banned.log
|
|
||||||
|
|
||||||
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
|
|
||||||
echo "$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = <F-USER> [IP] = <ip> unbanned." >> /var/log/3xipl-banned.log
|
|
||||||
|
|
||||||
[Init]
|
|
||||||
EOF
|
|
||||||
|
|
||||||
#Launching fail2ban
|
|
||||||
if ! sudo systemctl is-active --quiet fail2ban; then
|
|
||||||
sudo systemctl start fail2ban
|
|
||||||
else
|
else
|
||||||
systemctl restart fail2ban
|
systemctl restart fail2ban
|
||||||
fi
|
fi
|
||||||
sudo systemctl enable fail2ban
|
systemctl enable fail2ban
|
||||||
|
|
||||||
echo -e "${green}IP Limit installed and configured successfully!${plain}\n"
|
echo -e "${green}IP Limit installed and configured successfully!${plain}\n"
|
||||||
before_show_menu
|
before_show_menu
|
||||||
|
@ -837,27 +882,27 @@ remove_iplimit(){
|
||||||
rm -f /etc/fail2ban/filter.d/3x-ipl.conf
|
rm -f /etc/fail2ban/filter.d/3x-ipl.conf
|
||||||
rm -f /etc/fail2ban/action.d/3x-ipl.conf
|
rm -f /etc/fail2ban/action.d/3x-ipl.conf
|
||||||
rm -f /etc/fail2ban/jail.d/3x-ipl.conf
|
rm -f /etc/fail2ban/jail.d/3x-ipl.conf
|
||||||
sudo systemctl restart fail2ban
|
systemctl restart fail2ban
|
||||||
echo -e "${green}IP Limit removed successfully!${plain}\n"
|
echo -e "${green}IP Limit removed successfully!${plain}\n"
|
||||||
before_show_menu ;;
|
before_show_menu ;;
|
||||||
2)
|
2)
|
||||||
rm -f /etc/fail2ban/filter.d/3x-ipl.conf
|
rm -f /etc/fail2ban/filter.d/3x-ipl.conf
|
||||||
rm -f /etc/fail2ban/action.d/3x-ipl.conf
|
rm -f /etc/fail2ban/action.d/3x-ipl.conf
|
||||||
rm -f /etc/fail2ban/jail.d/3x-ipl.conf
|
rm -f /etc/fail2ban/jail.d/3x-ipl.conf
|
||||||
sudo systemctl stop fail2ban
|
systemctl stop fail2ban
|
||||||
sudo systemctl disable fail2ban
|
systemctl disable fail2ban
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
ubuntu|debian)
|
ubuntu|debian)
|
||||||
sudo apt-get remove fail2ban -y ;;
|
apt remove fail2ban -y ;;
|
||||||
centos)
|
centos)
|
||||||
sudo yum -y remove fail2ban ;;
|
yum -y remove fail2ban ;;
|
||||||
fedora)
|
fedora)
|
||||||
sudo dnf -y remove fail2ban ;;
|
dnf -y remove fail2ban ;;
|
||||||
*)
|
*)
|
||||||
echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
|
echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
|
||||||
exit 1 ;;
|
exit 1 ;;
|
||||||
esac
|
esac
|
||||||
rm -rf /etc/fail2ban/*
|
rm -rf /etc/fail2ban
|
||||||
echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
|
echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
|
||||||
before_show_menu ;;
|
before_show_menu ;;
|
||||||
0)
|
0)
|
||||||
|
@ -917,7 +962,7 @@ show_menu() {
|
||||||
${green}19.${plain} Update Geo Files
|
${green}19.${plain} Update Geo Files
|
||||||
${green}20.${plain} Active Firewall and open ports
|
${green}20.${plain} Active Firewall and open ports
|
||||||
${green}21.${plain} Speedtest by Ookla
|
${green}21.${plain} Speedtest by Ookla
|
||||||
"
|
"
|
||||||
show_status
|
show_status
|
||||||
echo && read -p "Please enter your selection [0-21]: " num
|
echo && read -p "Please enter your selection [0-21]: " num
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
|
"x-ui/logger"
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
|
|
||||||
"github.com/Workiva/go-datastructures/queue"
|
"github.com/Workiva/go-datastructures/queue"
|
||||||
|
@ -47,10 +48,47 @@ func GetBlockedIPsPath() string {
|
||||||
return config.GetBinFolderPath() + "/BlockedIps"
|
return config.GetBinFolderPath() + "/BlockedIps"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetIPLimitLogPath() string {
|
||||||
|
return config.GetLogFolder() + "/3xipl.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetIPLimitBannedLogPath() string {
|
||||||
|
return config.GetLogFolder() + "/3xipl-banned.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAccessPersistentLogPath() string {
|
||||||
|
return config.GetLogFolder() + "/3xipl-access-persistent.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAccessLogPath() string {
|
||||||
|
config, err := os.ReadFile(GetConfigPath())
|
||||||
|
if err != nil {
|
||||||
|
logger.Warningf("Something went wrong: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonConfig := map[string]interface{}{}
|
||||||
|
err = json.Unmarshal([]byte(config), &jsonConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warningf("Something went wrong: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if jsonConfig["log"] != nil {
|
||||||
|
jsonLog := jsonConfig["log"].(map[string]interface{})
|
||||||
|
if jsonLog["access"] != nil {
|
||||||
|
|
||||||
|
accessLogPath := jsonLog["access"].(string)
|
||||||
|
|
||||||
|
return accessLogPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func stopProcess(p *Process) {
|
func stopProcess(p *Process) {
|
||||||
p.Stop()
|
p.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type Process struct {
|
type Process struct {
|
||||||
*process
|
*process
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue