mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-03-01 01:20:49 +03:00
🚀 Some improvements for x-ui.sh and ip job (#665)
This commit is contained in:
parent
f726474a5d
commit
1028319386
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
|
||||
if [ $1 == "amd64" ]; then
|
||||
ARCH="64";
|
||||
FNAME="amd64";
|
||||
elif [ $1 == "arm64" ]; then
|
||||
ARCH="arm64-v8a"
|
||||
FNAME="arm64";
|
||||
else
|
||||
ARCH="64";
|
||||
FNAME="amd64";
|
||||
fi
|
||||
|
||||
case $1 in
|
||||
amd64)
|
||||
ARCH="64"
|
||||
FNAME="amd64"
|
||||
;;
|
||||
arm64)
|
||||
ARCH="arm64-v8a"
|
||||
FNAME="arm64"
|
||||
;;
|
||||
*)
|
||||
ARCH="64"
|
||||
FNAME="amd64"
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p build/bin
|
||||
cd build/bin
|
||||
|
||||
wget "https://github.com/mhsanaei/xray-core/releases/latest/download/Xray-linux-${ARCH}.zip"
|
||||
unzip "Xray-linux-${ARCH}.zip"
|
||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat iran.dat
|
||||
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/geosite.dat"
|
||||
wget "https://github.com/bootmortis/iran-hosted-domains/releases/latest/download/iran.dat"
|
||||
|
||||
cd ../../
|
43
Dockerfile
43
Dockerfile
@ -1,20 +1,47 @@
|
||||
#Build latest x-ui from source
|
||||
# ========================================================
|
||||
# Stage: Builder
|
||||
# ========================================================
|
||||
FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine AS builder
|
||||
WORKDIR /app
|
||||
ARG TARGETARCH
|
||||
RUN apk --no-cache --update add build-base gcc wget unzip
|
||||
ARG TARGETARCH
|
||||
ENV CGO_ENABLED=1
|
||||
|
||||
RUN apk --no-cache --update add \
|
||||
build-base \
|
||||
gcc \
|
||||
wget \
|
||||
unzip
|
||||
|
||||
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"
|
||||
|
||||
|
||||
#Build app image using latest x-ui
|
||||
# ========================================================
|
||||
# Stage: Final Image of 3x-ui
|
||||
# ========================================================
|
||||
FROM alpine
|
||||
ENV TZ=Asia/Tehran
|
||||
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/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" ]
|
||||
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**:
|
||||
|
||||
```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)
|
||||
@ -280,6 +280,7 @@ Reference syntax:
|
||||
| XUI_DEBUG | `boolean` | `false` |
|
||||
| XUI_BIN_FOLDER | `string` | `"bin"` |
|
||||
| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` |
|
||||
| XUI_LOG_FOLDER | `string` | `"/var/log"` |
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -65,3 +65,11 @@ func GetDBFolderPath() string {
|
||||
func GetDBPath() string {
|
||||
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"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"x-ui/config"
|
||||
"x-ui/database/model"
|
||||
"x-ui/xray"
|
||||
@ -26,7 +27,6 @@ var initializers = []func() error{
|
||||
}
|
||||
|
||||
func initUser() error {
|
||||
|
||||
err := db.AutoMigrate(&model.User{})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -54,9 +54,11 @@ func initInbound() error {
|
||||
func initSetting() error {
|
||||
return db.AutoMigrate(&model.Setting{})
|
||||
}
|
||||
|
||||
func initInboundClientIps() error {
|
||||
return db.AutoMigrate(&model.InboundClientIps{})
|
||||
}
|
||||
|
||||
func initClientTraffic() error {
|
||||
return db.AutoMigrate(&xray.ClientTraffic{})
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ plain='\033[0m'
|
||||
cur_dir=$(pwd)
|
||||
|
||||
# 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
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
@ -41,12 +41,12 @@ if [[ "${release}" == "centos" ]]; then
|
||||
fi
|
||||
elif [[ "${release}" == "ubuntu" ]]; 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
|
||||
|
||||
elif [[ "${release}" == "fedora" ]]; 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
|
||||
|
||||
elif [[ "${release}" == "debian" ]]; then
|
||||
@ -68,7 +68,7 @@ install_base() {
|
||||
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() {
|
||||
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
|
||||
|
@ -3,6 +3,7 @@ package controller
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
"x-ui/web/global"
|
||||
@ -40,7 +41,6 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
|
||||
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
||||
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
|
||||
g.POST("/delDepletedClients/:id", a.delDepletedClients)
|
||||
|
||||
}
|
||||
|
||||
func (a *InboundController) startTask() {
|
||||
@ -79,6 +79,7 @@ func (a *InboundController) getInbound(c *gin.Context) {
|
||||
}
|
||||
jsonObj(c, inbound, nil)
|
||||
}
|
||||
|
||||
func (a *InboundController) getClientTraffics(c *gin.Context) {
|
||||
email := c.Param("email")
|
||||
clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email)
|
||||
|
@ -45,7 +45,9 @@
|
||||
<a-tag :color="statsColor(record, client.email)">
|
||||
[[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] /
|
||||
<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-popover>
|
||||
</template>
|
||||
|
@ -224,7 +224,9 @@
|
||||
<template v-if="dbInbound.total > 0">
|
||||
[[ sizeFormat(dbInbound.total) ]]
|
||||
</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-popover>
|
||||
</template>
|
||||
|
@ -5,23 +5,26 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"x-ui/database"
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
"x-ui/web/service"
|
||||
"x-ui/xray"
|
||||
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"x-ui/database"
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
"x-ui/xray"
|
||||
)
|
||||
|
||||
type CheckClientIpJob struct {
|
||||
xrayService service.XrayService
|
||||
}
|
||||
type CheckClientIpJob struct {}
|
||||
|
||||
var job *CheckClientIpJob
|
||||
var disAllowedIps []string
|
||||
var ipFiles = []string{
|
||||
xray.GetBlockedIPsPath(),
|
||||
xray.GetIPLimitLogPath(),
|
||||
xray.GetIPLimitBannedLogPath(),
|
||||
xray.GetAccessPersistentLogPath(),
|
||||
}
|
||||
|
||||
func NewCheckClientIpJob() *CheckClientIpJob {
|
||||
job = new(CheckClientIpJob)
|
||||
@ -31,37 +34,28 @@ func NewCheckClientIpJob() *CheckClientIpJob {
|
||||
func (j *CheckClientIpJob) Run() {
|
||||
logger.Debug("Check Client IP Job...")
|
||||
|
||||
if hasLimitIp() {
|
||||
//create log file for Fail2ban IP Limit
|
||||
logIpFile, err := os.OpenFile("/var/log/3xipl.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
||||
checkError(err)
|
||||
defer logIpFile.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()
|
||||
// create files required for iplimit if not exists
|
||||
for i := 0; i < len(ipFiles); i++ {
|
||||
file, err := os.OpenFile(ipFiles[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
||||
j.checkError(err)
|
||||
defer file.Close()
|
||||
}
|
||||
|
||||
// check for limit ip
|
||||
if j.hasLimitIp() {
|
||||
j.processLogFile()
|
||||
}
|
||||
|
||||
// write to blocked ips
|
||||
blockedIps := []byte(strings.Join(disAllowedIps, ","))
|
||||
|
||||
// check if file exists, if not create one
|
||||
_, 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)
|
||||
err := os.WriteFile(xray.GetBlockedIPsPath(), blockedIps, 0644)
|
||||
j.checkError(err)
|
||||
}
|
||||
|
||||
func hasLimitIp() bool {
|
||||
func (j *CheckClientIpJob) hasLimitIp() bool {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
|
||||
err := db.Model(model.Inbound{}).Find(&inbounds).Error
|
||||
if err != nil {
|
||||
return false
|
||||
@ -83,11 +77,12 @@ func hasLimitIp() bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func processLogFile() {
|
||||
accessLogPath := GetAccessLogPath()
|
||||
func (j *CheckClientIpJob) processLogFile() {
|
||||
accessLogPath := xray.GetAccessLogPath()
|
||||
if accessLogPath == "" {
|
||||
logger.Warning("access.log doesn't exist in your config.json")
|
||||
return
|
||||
@ -95,7 +90,7 @@ func processLogFile() {
|
||||
|
||||
data, err := os.ReadFile(accessLogPath)
|
||||
InboundClientIps := make(map[string][]string)
|
||||
checkError(err)
|
||||
j.checkError(err)
|
||||
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, line := range lines {
|
||||
@ -116,7 +111,7 @@ func processLogFile() {
|
||||
matchesEmail = strings.TrimSpace(strings.Split(matchesEmail, "email: ")[1])
|
||||
|
||||
if InboundClientIps[matchesEmail] != nil {
|
||||
if contains(InboundClientIps[matchesEmail], ip) {
|
||||
if j.contains(InboundClientIps[matchesEmail], ip) {
|
||||
continue
|
||||
}
|
||||
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
||||
@ -125,68 +120,50 @@ func processLogFile() {
|
||||
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
disAllowedIps = []string{}
|
||||
shouldCleanLog := false
|
||||
|
||||
for clientEmail, ips := range InboundClientIps {
|
||||
inboundClientIps, err := GetInboundClientIps(clientEmail)
|
||||
inboundClientIps, err := j.getInboundClientIps(clientEmail)
|
||||
sort.Strings(ips)
|
||||
if err != nil {
|
||||
addInboundClientIps(clientEmail, ips)
|
||||
|
||||
j.addInboundClientIps(clientEmail, ips)
|
||||
} 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)
|
||||
//added 3 seconds delay before cleaning logs to reduce chance of logging IP that already has been banned
|
||||
|
||||
if shouldCleanLog {
|
||||
//copy log
|
||||
logAccessP, err := os.OpenFile("/usr/local/x-ui/accessp.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
||||
checkError(err)
|
||||
// copy access log to persistent file
|
||||
logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
||||
j.checkError(err)
|
||||
input, err := os.ReadFile(accessLogPath)
|
||||
checkError(err)
|
||||
j.checkError(err)
|
||||
if _, err := logAccessP.Write(input); err != nil {
|
||||
checkError(err)
|
||||
j.checkError(err)
|
||||
}
|
||||
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())
|
||||
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) {
|
||||
func (j *CheckClientIpJob) checkError(e error) {
|
||||
if e != nil {
|
||||
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 {
|
||||
if v == str {
|
||||
return true
|
||||
@ -195,7 +172,8 @@ func contains(s []string, str string) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
func GetInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
|
||||
|
||||
func (j *CheckClientIpJob) getInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
|
||||
db := database.GetDB()
|
||||
InboundClientIps := &model.InboundClientIps{}
|
||||
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
|
||||
}
|
||||
func addInboundClientIps(clientEmail string, ips []string) error {
|
||||
|
||||
func (j *CheckClientIpJob) addInboundClientIps(clientEmail string, ips []string) error {
|
||||
inboundClientIps := &model.InboundClientIps{}
|
||||
jsonIps, err := json.Marshal(ips)
|
||||
checkError(err)
|
||||
j.checkError(err)
|
||||
|
||||
inboundClientIps.ClientEmail = clientEmail
|
||||
inboundClientIps.Ips = string(jsonIps)
|
||||
@ -229,17 +208,17 @@ func addInboundClientIps(clientEmail string, ips []string) error {
|
||||
}
|
||||
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)
|
||||
checkError(err)
|
||||
j.checkError(err)
|
||||
|
||||
inboundClientIps.ClientEmail = clientEmail
|
||||
inboundClientIps.Ips = string(jsonIps)
|
||||
|
||||
// check inbound limitation
|
||||
inbound, err := GetInboundByEmail(clientEmail)
|
||||
checkError(err)
|
||||
inbound, err := j.getInboundByEmail(clientEmail)
|
||||
j.checkError(err)
|
||||
|
||||
if inbound.Settings == "" {
|
||||
logger.Debug("wrong data ", inbound)
|
||||
@ -251,13 +230,20 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
|
||||
clients := settings["clients"]
|
||||
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 {
|
||||
if client.Email == clientEmail {
|
||||
|
||||
limitIp := client.LimitIP
|
||||
|
||||
if limitIp != 0 {
|
||||
|
||||
shouldCleanLog = true
|
||||
|
||||
if limitIp < len(ips) && inbound.Enable {
|
||||
@ -280,27 +266,14 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
|
||||
return shouldCleanLog
|
||||
}
|
||||
|
||||
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) {
|
||||
func (j *CheckClientIpJob) 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
|
||||
}
|
||||
|
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"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"x-ui/database"
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
@ -74,7 +75,6 @@ func (s *InboundService) getAllEmails() ([]string, error) {
|
||||
FROM inbounds,
|
||||
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
||||
`).Scan(&emails).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -816,7 +816,8 @@ func (s *InboundService) UpdateClientStat(email string, client *model.Client) er
|
||||
"enable": true,
|
||||
"email": client.Email,
|
||||
"total": client.TotalGB,
|
||||
"expiry_time": client.ExpiryTime})
|
||||
"expiry_time": client.ExpiryTime,
|
||||
})
|
||||
err := result.Error
|
||||
if err != nil {
|
||||
return err
|
||||
@ -1068,8 +1069,8 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry_time int64) error {
|
||||
_, inbound, err := s.GetClientInboundByEmail(clientEmail)
|
||||
if err != nil {
|
||||
@ -1126,7 +1127,6 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
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})
|
||||
|
||||
err := result.Error
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1209,7 +1208,6 @@ func (s *InboundService) ResetAllClientTraffics(id int) error {
|
||||
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
|
||||
|
||||
err := result.Error
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1224,7 +1222,6 @@ func (s *InboundService) ResetAllTraffics() error {
|
||||
Updates(map[string]interface{}{"up": 0, "down": 0})
|
||||
|
||||
err := result.Error
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1411,7 +1408,6 @@ func (s *InboundService) ClearClientIps(clientEmail string) error {
|
||||
Where("client_email = ?", clientEmail).
|
||||
Update("ips", "")
|
||||
err := result.Error
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"x-ui/config"
|
||||
"x-ui/database"
|
||||
"x-ui/logger"
|
||||
@ -250,7 +251,6 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||
}
|
||||
|
||||
func (s *ServerService) StopXrayService() (string error) {
|
||||
|
||||
err := s.xrayService.StopXray()
|
||||
if err != nil {
|
||||
logger.Error("stop xray failed:", err)
|
||||
@ -261,7 +261,6 @@ func (s *ServerService) StopXrayService() (string error) {
|
||||
}
|
||||
|
||||
func (s *ServerService) RestartXrayService() (string error) {
|
||||
|
||||
s.xrayService.StopXray()
|
||||
defer func() {
|
||||
err := s.xrayService.RestartXray(true)
|
||||
@ -377,7 +376,6 @@ func (s *ServerService) UpdateXray(version string) error {
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
var entry cron.EntryID
|
||||
isTgbotenabled, err := s.settingService.GetTgbotenabled()
|
||||
|
357
x-ui.sh
357
x-ui.sh
@ -56,6 +56,13 @@ elif [[ "${release}" == "debian" ]]; then
|
||||
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() {
|
||||
if [[ $# > 1 ]]; then
|
||||
echo && read -p "$1 [Default $2]: " temp
|
||||
@ -296,25 +303,28 @@ enable_bbr() {
|
||||
fi
|
||||
|
||||
# Check the OS and install necessary packages
|
||||
if [[ "$(cat /etc/os-release | grep -E '^ID=' | awk -F '=' '{print $2}')" == "ubuntu" ]]; then
|
||||
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}')" == "debian" ]]; then
|
||||
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
|
||||
sudo dnf -y update && sudo dnf -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
|
||||
else
|
||||
echo "Unsupported operating system. Please check the script and install the necessary packages manually."
|
||||
exit 1
|
||||
fi
|
||||
case "${release}" in
|
||||
ubuntu|debian)
|
||||
apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
|
||||
;;
|
||||
centos)
|
||||
yum -y update && yum -y install ca-certificates
|
||||
;;
|
||||
fedora)
|
||||
dnf -y update && dnf -y install ca-certificates
|
||||
;;
|
||||
*)
|
||||
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Enable BBR
|
||||
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
|
||||
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
|
||||
echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf
|
||||
echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf
|
||||
|
||||
# Apply changes
|
||||
sudo sysctl -p
|
||||
sysctl -p
|
||||
|
||||
# Verify that BBR is enabled
|
||||
if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then
|
||||
@ -434,24 +444,24 @@ show_xray_status() {
|
||||
open_ports() {
|
||||
if ! command -v ufw &>/dev/null; then
|
||||
echo "ufw firewall is not installed. Installing now..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ufw
|
||||
apt-get update
|
||||
apt-get install -y ufw
|
||||
else
|
||||
echo "ufw firewall is already installed"
|
||||
fi
|
||||
|
||||
# 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"
|
||||
else
|
||||
# Open the necessary ports
|
||||
sudo ufw allow ssh
|
||||
sudo ufw allow http
|
||||
sudo ufw allow https
|
||||
sudo ufw allow 2053/tcp
|
||||
ufw allow ssh
|
||||
ufw allow http
|
||||
ufw allow https
|
||||
ufw allow 2053/tcp
|
||||
|
||||
# Enable the firewall
|
||||
sudo ufw --force enable
|
||||
ufw --force enable
|
||||
fi
|
||||
|
||||
# Prompt the user to enter a list of ports
|
||||
@ -472,15 +482,15 @@ open_ports() {
|
||||
end_port=$(echo $port | cut -d'-' -f2)
|
||||
# Loop through the range and open each port
|
||||
for ((i = start_port; i <= end_port; i++)); do
|
||||
sudo ufw allow $i
|
||||
ufw allow $i
|
||||
done
|
||||
else
|
||||
sudo ufw allow "$port"
|
||||
ufw allow "$port"
|
||||
fi
|
||||
done
|
||||
|
||||
# Confirm that the ports are open
|
||||
sudo ufw status | grep $ports
|
||||
ufw status | grep $ports
|
||||
}
|
||||
|
||||
update_geo() {
|
||||
@ -539,7 +549,7 @@ ssl_cert_issue_main() {
|
||||
}
|
||||
|
||||
ssl_cert_issue() {
|
||||
#check for acme.sh first
|
||||
# check for acme.sh first
|
||||
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
||||
echo "acme.sh could not be found. we will install it"
|
||||
install_acme
|
||||
@ -548,24 +558,30 @@ ssl_cert_issue() {
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
#install socat second
|
||||
if [[ "${release}" == "centos" ]] || [[ "${release}" == "fedora" ]]; then
|
||||
yum install socat -y
|
||||
else
|
||||
apt install socat -y
|
||||
fi
|
||||
# install socat second
|
||||
case "${release}" in
|
||||
ubuntu|debian)
|
||||
apt update && apt install socat -y ;;
|
||||
centos)
|
||||
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
|
||||
LOGE "install socat failed,please check logs"
|
||||
LOGE "install socat failed, please check logs"
|
||||
exit 1
|
||||
else
|
||||
LOGI "install socat succeed..."
|
||||
fi
|
||||
|
||||
#get the domain here,and we need verify it
|
||||
# get the domain here,and we need verify it
|
||||
local domain=""
|
||||
read -p "Please enter your domain name:" domain
|
||||
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}')
|
||||
|
||||
if [ ${currentCert} == ${domain} ]; then
|
||||
@ -577,7 +593,7 @@ ssl_cert_issue() {
|
||||
LOGI "your domain is ready for issuing cert now..."
|
||||
fi
|
||||
|
||||
#create a directory for install cert
|
||||
# create a directory for install cert
|
||||
certPath="/root/cert/${domain}"
|
||||
if [ ! -d "$certPath" ]; then
|
||||
mkdir -p "$certPath"
|
||||
@ -586,15 +602,15 @@ ssl_cert_issue() {
|
||||
mkdir -p "$certPath"
|
||||
fi
|
||||
|
||||
#get needed port here
|
||||
# get needed port here
|
||||
local WebPort=80
|
||||
read -p "please choose which port do you use,default will be 80 port:" WebPort
|
||||
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
|
||||
LOGE "your input ${WebPort} is invalid,will use default port"
|
||||
fi
|
||||
LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..."
|
||||
#NOTE:This should be handled by user
|
||||
#open the port and kill the occupied progress
|
||||
# NOTE:This should be handled by user
|
||||
# open the port and kill the occupied progress
|
||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort}
|
||||
if [ $? -ne 0 ]; then
|
||||
@ -604,7 +620,7 @@ ssl_cert_issue() {
|
||||
else
|
||||
LOGE "issue certs succeed,installing certs..."
|
||||
fi
|
||||
#install cert
|
||||
# install cert
|
||||
~/.acme.sh/acme.sh --installcert -d ${domain} \
|
||||
--key-file /root/cert/${domain}/privkey.pem \
|
||||
--fullchain-file /root/cert/${domain}/fullchain.pem
|
||||
@ -628,18 +644,17 @@ ssl_cert_issue() {
|
||||
ls -lah cert/*
|
||||
chmod 755 $certPath/*
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
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}\t3.${plain} Turn on/off WireProxy"
|
||||
echo -e "${green}\t4.${plain} Uninstall WARP"
|
||||
read -p "Choose an option: " choice
|
||||
case "$choice" in
|
||||
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)
|
||||
warp a
|
||||
@ -679,8 +694,8 @@ run_speedtest() {
|
||||
echo "Error: Package manager not found. You may need to install Speedtest manually."
|
||||
return 1
|
||||
else
|
||||
curl -s $speedtest_install_script | sudo bash
|
||||
sudo $pkg_manager install -y speedtest
|
||||
curl -s $speedtest_install_script | bash
|
||||
$pkg_manager install -y speedtest
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -688,110 +703,29 @@ run_speedtest() {
|
||||
speedtest
|
||||
}
|
||||
|
||||
iplimit_main() {
|
||||
echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
|
||||
echo -e "${green}\t2.${plain} Change Ban Duration"
|
||||
echo -e "${green}\t3.${plain} Unban Everyone"
|
||||
echo -e "${green}\t4.${plain} Check Logs"
|
||||
echo -e "${green}\t5.${plain} Uninstall IP Limit"
|
||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||
read -p "Choose an option: " choice
|
||||
case "$choice" in
|
||||
0)
|
||||
show_menu ;;
|
||||
1)
|
||||
confirm "Proceed with installation of Fail2ban & IP Limit?" "y"
|
||||
if [[ $? == 0 ]]; then
|
||||
install_iplimit
|
||||
else
|
||||
iplimit_main
|
||||
fi ;;
|
||||
2)
|
||||
read -rp "Please enter new Ban Duration in Minutes [default 5]: " NUM
|
||||
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
|
||||
sudo systemctl restart fail2ban
|
||||
echo -e "${green}Bantime set to ${NUM} minutes successfully.${plain}"
|
||||
else
|
||||
echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
|
||||
fi
|
||||
iplimit_main ;;
|
||||
3)
|
||||
confirm "Proceed with Unbanning everyone from IP Limit jail?" "y"
|
||||
if [[ $? == 0 ]]; then
|
||||
fail2ban-client reload --restart --unban 3x-ipl
|
||||
echo -e "${green}All users Unbanned successfully.${plain}"
|
||||
iplimit_main
|
||||
else
|
||||
echo -e "${yellow}Cancelled.${plain}"
|
||||
fi
|
||||
iplimit_main ;;
|
||||
4)
|
||||
if test -f "/var/log/3xipl-banned.log"; then
|
||||
if [[ -s "/var/log/3xipl-banned.log" ]]; then
|
||||
cat /var/log/3xipl-banned.log
|
||||
else
|
||||
echo -e "${red}Log file is empty.${plain}\n"
|
||||
fi
|
||||
else
|
||||
echo -e "${red}Log file not found. Please Install Fail2ban and IP Limit first.${plain}\n"
|
||||
iplimit_main
|
||||
fi ;;
|
||||
5)
|
||||
remove_iplimit ;;
|
||||
*) echo "Invalid choice" ;;
|
||||
esac
|
||||
}
|
||||
create_iplimit_jails() {
|
||||
# Use default bantime if not passed => 5 minutes
|
||||
local bantime="${1:-5}"
|
||||
|
||||
install_iplimit() {
|
||||
if ! command -v fail2ban-client &>/dev/null; then
|
||||
echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n"
|
||||
# Check the OS and install necessary packages
|
||||
case "${release}" in
|
||||
ubuntu|debian)
|
||||
sudo apt-get update && sudo apt-get install fail2ban -y ;;
|
||||
centos)
|
||||
sudo yum -y update && sudo yum -y install fail2ban ;;
|
||||
fedora)
|
||||
sudo dnf -y update && sudo dnf -y install fail2ban ;;
|
||||
*)
|
||||
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
||||
exit 1 ;;
|
||||
esac
|
||||
echo -e "${green}Fail2ban installed successfully!${plain}\n"
|
||||
else
|
||||
echo -e "${yellow}Fail2ban is already installed.${plain}\n"
|
||||
fi
|
||||
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
|
||||
|
||||
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)
|
||||
if grep -qw '3x-ipl' /etc/fail2ban/jail.local || grep -qw '3x-ipl' /etc/fail2ban/jail.conf; then
|
||||
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
|
||||
fi
|
||||
|
||||
#Check if log file exists
|
||||
if ! test -f "/var/log/3xipl-banned.log"; then
|
||||
touch /var/log/3xipl-banned.log
|
||||
fi
|
||||
|
||||
#Check if service log file exists so fail2ban won't return error
|
||||
if ! test -f "/var/log/3xipl.log"; then
|
||||
touch /var/log/3xipl.log
|
||||
fi
|
||||
|
||||
|
||||
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
|
||||
|
||||
sudo cat > /etc/fail2ban/filter.d/3x-ipl.conf << 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
|
||||
|
||||
sudo cat > /etc/fail2ban/action.d/3x-ipl.conf << 'EOF'
|
||||
cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
|
||||
[INCLUDES]
|
||||
before = iptables-common.conf
|
||||
|
||||
@ -807,21 +741,132 @@ actionstop = <iptables> -D <chain> -p <protocol> -j 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
|
||||
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." >> /var/log/3xipl-banned.log
|
||||
echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = <F-USER> [IP] = <ip> unbanned." >> ${iplimit_banned_log_path}
|
||||
|
||||
[Init]
|
||||
EOF
|
||||
|
||||
#Launching fail2ban
|
||||
if ! sudo systemctl is-active --quiet fail2ban; then
|
||||
sudo systemctl start fail2ban
|
||||
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() {
|
||||
echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
|
||||
echo -e "${green}\t2.${plain} Change Ban Duration"
|
||||
echo -e "${green}\t3.${plain} Unban Everyone"
|
||||
echo -e "${green}\t4.${plain} Check Logs"
|
||||
echo -e "${green}\t5.${plain} Uninstall IP Limit"
|
||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||
read -p "Choose an option: " choice
|
||||
case "$choice" in
|
||||
0)
|
||||
show_menu ;;
|
||||
1)
|
||||
confirm "Proceed with installation of Fail2ban & IP Limit?" "y"
|
||||
if [[ $? == 0 ]]; then
|
||||
install_iplimit
|
||||
else
|
||||
iplimit_main
|
||||
fi ;;
|
||||
2)
|
||||
read -rp "Please enter new Ban Duration in Minutes [default 5]: " NUM
|
||||
if [[ $NUM =~ ^[0-9]+$ ]]; then
|
||||
create_iplimit_jail ${NUM}
|
||||
systemctl restart fail2ban
|
||||
else
|
||||
echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
|
||||
fi
|
||||
iplimit_main ;;
|
||||
3)
|
||||
confirm "Proceed with Unbanning everyone from IP Limit jail?" "y"
|
||||
if [[ $? == 0 ]]; then
|
||||
fail2ban-client reload --restart --unban 3x-ipl
|
||||
echo -e "${green}All users Unbanned successfully.${plain}"
|
||||
iplimit_main
|
||||
else
|
||||
echo -e "${yellow}Cancelled.${plain}"
|
||||
fi
|
||||
iplimit_main ;;
|
||||
4)
|
||||
if test -f "${iplimit_banned_log_path}"; then
|
||||
if [[ -s "${iplimit_banned_log_path}" ]]; then
|
||||
cat ${iplimit_banned_log_path}
|
||||
else
|
||||
echo -e "${red}Log file is empty.${plain}\n"
|
||||
fi
|
||||
else
|
||||
echo -e "${red}Log file not found. Please Install Fail2ban and IP Limit first.${plain}\n"
|
||||
iplimit_main
|
||||
fi ;;
|
||||
5)
|
||||
remove_iplimit ;;
|
||||
*) echo "Invalid choice" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
install_iplimit() {
|
||||
if ! command -v fail2ban-client &>/dev/null; then
|
||||
echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n"
|
||||
# Check the OS and install necessary packages
|
||||
case "${release}" in
|
||||
ubuntu|debian)
|
||||
apt update && apt install fail2ban -y ;;
|
||||
centos)
|
||||
yum -y update && yum -y install fail2ban ;;
|
||||
fedora)
|
||||
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"
|
||||
exit 1 ;;
|
||||
esac
|
||||
echo -e "${green}Fail2ban installed successfully!${plain}\n"
|
||||
else
|
||||
echo -e "${yellow}Fail2ban is already installed.${plain}\n"
|
||||
fi
|
||||
|
||||
echo -e "${green}Configuring IP Limit...${plain}\n"
|
||||
|
||||
# make sure there's no conflict for jail files
|
||||
iplimit_remove_conflicts
|
||||
|
||||
# Check if log file exists
|
||||
if ! test -f "${iplimit_banned_log_path}"; then
|
||||
touch ${iplimit_banned_log_path}
|
||||
fi
|
||||
|
||||
# Check if service log file exists so fail2ban won't return error
|
||||
if ! test -f "${iplimit_log_path}"; then
|
||||
touch ${iplimit_log_path}
|
||||
fi
|
||||
|
||||
# Create the iplimit jail files
|
||||
# we didn't pass the bantime here to use the default value
|
||||
create_iplimit_jails
|
||||
|
||||
# Launching fail2ban
|
||||
if ! systemctl is-active --quiet fail2ban; then
|
||||
systemctl start fail2ban
|
||||
else
|
||||
systemctl restart fail2ban
|
||||
fi
|
||||
sudo systemctl enable fail2ban
|
||||
systemctl enable fail2ban
|
||||
|
||||
echo -e "${green}IP Limit installed and configured successfully!${plain}\n"
|
||||
before_show_menu
|
||||
@ -837,27 +882,27 @@ remove_iplimit(){
|
||||
rm -f /etc/fail2ban/filter.d/3x-ipl.conf
|
||||
rm -f /etc/fail2ban/action.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"
|
||||
before_show_menu ;;
|
||||
2)
|
||||
rm -f /etc/fail2ban/filter.d/3x-ipl.conf
|
||||
rm -f /etc/fail2ban/action.d/3x-ipl.conf
|
||||
rm -f /etc/fail2ban/jail.d/3x-ipl.conf
|
||||
sudo systemctl stop fail2ban
|
||||
sudo systemctl disable fail2ban
|
||||
systemctl stop fail2ban
|
||||
systemctl disable fail2ban
|
||||
case "${release}" in
|
||||
ubuntu|debian)
|
||||
sudo apt-get remove fail2ban -y ;;
|
||||
apt remove fail2ban -y ;;
|
||||
centos)
|
||||
sudo yum -y remove fail2ban ;;
|
||||
yum -y remove fail2ban ;;
|
||||
fedora)
|
||||
sudo dnf -y remove fail2ban ;;
|
||||
dnf -y remove fail2ban ;;
|
||||
*)
|
||||
echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
|
||||
exit 1 ;;
|
||||
esac
|
||||
rm -rf /etc/fail2ban/*
|
||||
rm -rf /etc/fail2ban
|
||||
echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
|
||||
before_show_menu ;;
|
||||
0)
|
||||
@ -917,7 +962,7 @@ show_menu() {
|
||||
${green}19.${plain} Update Geo Files
|
||||
${green}20.${plain} Active Firewall and open ports
|
||||
${green}21.${plain} Speedtest by Ookla
|
||||
"
|
||||
"
|
||||
show_status
|
||||
echo && read -p "Please enter your selection [0-21]: " num
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"x-ui/config"
|
||||
"x-ui/logger"
|
||||
"x-ui/util/common"
|
||||
|
||||
"github.com/Workiva/go-datastructures/queue"
|
||||
@ -47,10 +48,47 @@ func GetBlockedIPsPath() string {
|
||||
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) {
|
||||
p.Stop()
|
||||
}
|
||||
|
||||
|
||||
type Process struct {
|
||||
*process
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user