Compare commits

..

25 Commits

Author SHA1 Message Date
mhsanaei
7abb092211 v2.4.6 2024-10-31 09:54:53 +01:00
Sanaei
937bfb4c78 SSH Port Forwarding (#2590)
* getListen & getCert

* SSH Port Forwarding

* fix
2024-10-31 09:53:47 +01:00
mhsanaei
1bcdc54b68 Xray Core v24.10.31 2024-10-31 09:47:39 +01:00
mhsanaei
eb58314c53 bash - remove version limit 2024-10-31 01:18:37 +01:00
mhsanaei
c158e6ec73 getListen & getCert 2024-10-31 01:10:17 +01:00
mhsanaei
5ae587ee81 bash - set or reset listenIP
we need this if we want to add SSH port forwarding
2024-10-30 16:35:36 +01:00
mhsanaei
d40fa46851 fix access url 2024-10-30 15:24:18 +01:00
mhsanaei
19a31686da REALITY: SplitHTTP transport 2024-10-30 14:46:27 +01:00
mhsanaei
13f7e07128 bash - Access URL
I will add https with domain later
2024-10-29 15:20:38 +01:00
mhsanaei
0e3691fdbd Xray core buggy version removed
only v24.10.16 or newer and v1.8.24
2024-10-29 14:56:30 +01:00
mhsanaei
e359b5c75e removed - XTLS Security
because its too old and no one use it anymore
2024-10-29 12:50:25 +01:00
pr3ci0u5
3b3bd3dea4 Update translate.ru_RU.toml (#2588) 2024-10-29 11:03:15 +01:00
mhsanaei
8f36b7ea84 update dependencies 2024-10-29 11:00:07 +01:00
mhsanaei
569d99512c iplimit - accept all email format 2024-10-28 20:13:42 +01:00
mhsanaei
ac84553a68 Fail2ban - Real-Time logs 2024-10-28 19:24:44 +01:00
mhsanaei
610db7827d Update install.sh 2024-10-25 11:30:44 +02:00
mhsanaei
088b55c9ed readme 2024-10-24 21:23:21 +02:00
mhsanaei
c800e29900 Update docker.yml 2024-10-24 21:22:40 +02:00
mhsanaei
14435db0d8 bash - Default credentials detected. Security update required 2024-10-24 21:17:00 +02:00
mhsanaei
bd6402562e OS - Alpine Linux
fix bash menu for docker
2024-10-24 20:36:12 +02:00
mhsanaei
d16ad11136 fix outbound noises
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-10-21 10:40:29 +02:00
MHSanaei
6c27e4177d readme: install old version 2024-10-20 14:18:50 +02:00
MHSanaei
bebf83f06c wireguard - noKernelTun 2024-10-20 14:07:21 +02:00
Amazing Watermelon
07bf741b15 Fixed the traditional Chinese name 2024-10-20 11:33:50 +02:00
mhsanaei
5e5851029d update README: SSL Certificate Management 2024-10-17 12:58:18 +02:00
41 changed files with 466 additions and 679 deletions

View File

@@ -1,5 +1,4 @@
name: Release 3X-UI for Docker name: Release 3X-UI for Docker
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
@@ -7,36 +6,50 @@ on:
- "v*.*.*" - "v*.*.*"
jobs: jobs:
build_and_push: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out the code - uses: actions/checkout@v4
uses: actions/checkout@v4 with:
- name: Set up QEMU submodules: true
uses: docker/setup-qemu-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
hsanaeii/3x-ui
ghcr.io/mhsanaei/3x-ui
tags: |
type=ref,event=branch
type=ref,event=tag
type=pep440,pattern={{version}}
- name: Set up Docker Buildx - name: Set up QEMU
uses: docker/setup-buildx-action@v3 uses: docker/setup-qemu-action@v3
- name: Login to GHCR - name: Set up Docker Buildx
uses: docker/login-action@v3 uses: docker/setup-buildx-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta - name: Login to Docker Hub
id: meta uses: docker/login-action@v3
uses: docker/metadata-action@v5 with:
with: username: ${{ secrets.DOCKER_HUB_USERNAME }}
images: ghcr.io/${{ github.repository }} password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build and push Docker image - name: Login to GHCR
uses: docker/build-push-action@v6 uses: docker/login-action@v3
with: with:
context: . registry: ghcr.io
push: ${{ github.event_name != 'pull_request' }} username: ${{ github.repository_owner }}
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386 password: ${{ secrets.GITHUB_TOKEN }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} - name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@@ -83,7 +83,7 @@ jobs:
cd x-ui/bin cd x-ui/bin
# Download dependencies # Download dependencies
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.10.16/" Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.10.31/"
if [ "${{ matrix.platform }}" == "amd64" ]; then if [ "${{ matrix.platform }}" == "amd64" ]; then
wget ${Xray_URL}Xray-linux-64.zip wget ${Xray_URL}Xray-linux-64.zip
unzip Xray-linux-64.zip unzip Xray-linux-64.zip

View File

@@ -27,7 +27,7 @@ case $1 in
esac esac
mkdir -p build/bin mkdir -p build/bin
cd build/bin cd build/bin
wget "https://github.com/XTLS/Xray-core/releases/download/v24.10.16/Xray-linux-${ARCH}.zip" wget "https://github.com/XTLS/Xray-core/releases/download/v24.10.31/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip" unzip "Xray-linux-${ARCH}.zip"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
mv xray "xray-linux-${FNAME}" mv xray "xray-linux-${FNAME}"

View File

@@ -30,38 +30,62 @@
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
``` ```
## Instalar una Versión Personalizada (no recomendamos) ## Instalar versión antigua (no recomendamos)
Para instalar la versión deseada, utiliza el siguiente comando de instalación. Por ejemplo, ver `v1.7.9`: Para instalar la versión deseada, utiliza el siguiente comando de instalación. Por ejemplo, ver `v1.7.9`:
``` ```
VERSION=v1.7.9 bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/refs/tags/$VERSION/install.sh") $VERSION VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
``` ```
## Certificado SSL ## Certificado SSL
<details> <details>
<summary>Haz clic para el Certificado SSL</summary> <summary>Haga clic para ver los detalles del certificado SSL</summary>
### Cloudflare ### ACME
El script de gestión tiene una aplicación de certificado SSL incorporada para Cloudflare. Para usar este script para colocar un certificado, necesitas lo siguiente: Para gestionar certificados SSL utilizando ACME:
- Correo electrónico registrado en Cloudflare 1. Asegúrate de que tu dominio esté correctamente resuelto al servidor.
- Clave Global de API de Cloudflare 2. Ejecuta el comando `x-ui` en la terminal y elige `Gestión de Certificados SSL`.
- El nombre de dominio se ha resuelto en el servidor actual a través de Cloudflare 3. Se te presentarán las siguientes opciones:
**1:** Ejecuta el comando`x-ui`en la terminal, luego elige `Certificado SSL de Cloudflare`.
- **Get SSL:** Obtener certificados SSL.
- **Revoke:** Revocar certificados SSL existentes.
- **Force Renew:** Forzar la renovación de certificados SSL.
- **Show Existing Domains:** Mostrar todos los certificados de dominio disponibles en el servidor.
- **Set Certificate Paths for the Panel:** Especificar el certificado para tu dominio que será utilizado por el panel.
### Certbot ### Certbot
```
Para instalar y usar Certbot:
```sh
apt-get install certbot -y apt-get install certbot -y
certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
certbot renew --dry-run certbot renew --dry-run
``` ```
***Consejo:*** *Certbot también está integrado en el script de gestión. Puedes ejecutar el comando `x-ui` , luego elegir `Gestión de Certificados SSL`.* ### Cloudflare
El script de gestión incluye una aplicación de certificado SSL integrada para Cloudflare. Para usar este script para solicitar un certificado, necesitas lo siguiente:
- Correo electrónico registrado en Cloudflare
- Clave API Global de Cloudflare
- El nombre de dominio debe estar resuelto al servidor actual a través de Cloudflare
**Cómo obtener la Clave API Global de Cloudflare:**
1. Ejecuta el comando `x-ui` en la terminal y elige `Certificado SSL de Cloudflare`.
2. Visita el enlace: [Tokens de API de Cloudflare](https://dash.cloudflare.com/profile/api-tokens).
3. Haz clic en "Ver Clave API Global" (consulta la captura de pantalla a continuación):
![](media/APIKey1.PNG)
4. Es posible que necesites volver a autenticar tu cuenta. Después de eso, se mostrará la Clave API (consulta la captura de pantalla a continuación):
![](media/APIKey2.png)
Al utilizarlo, simplemente ingresa tu `nombre de dominio`, `correo electrónico` y `CLAVE API`. El diagrama es el siguiente:
![](media/DetailEnter.png)
</details> </details>

View File

@@ -30,12 +30,12 @@
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
``` ```
## Install Custom Version (we don't recommend) ## Install legacy Version (we don't recommend)
To install your desired version, use following installation command. e.g., ver `v1.7.9`: To install your desired version, use following installation command. e.g., ver `v1.7.9`:
``` ```
VERSION=v1.7.9 bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/refs/tags/$VERSION/install.sh") $VERSION VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
``` ```
## SSL Certificate ## SSL Certificate
@@ -54,6 +54,8 @@ To manage SSL certificates using ACME:
- **Get SSL:** Obtain SSL certificates. - **Get SSL:** Obtain SSL certificates.
- **Revoke:** Revoke existing SSL certificates. - **Revoke:** Revoke existing SSL certificates.
- **Force Renew:** Force renewal of SSL certificates. - **Force Renew:** Force renewal of SSL certificates.
- **Show Existing Domains:** Display all domain certificates available on the server.
- **Set Certificate Paths for the Panel:** Specify the certificate for your domain to be used by the panel.
### Certbot ### Certbot

View File

@@ -30,12 +30,12 @@
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
``` ```
## Установка определённой версии (мы не рекомендуем) ## Установить старую версию (мы не рекомендуем)
Чтобы установить желаемую версию, используйте следующую команду установки. Например, ver `v1.7.9`: Чтобы установить желаемую версию, используйте следующую команду установки. Например, ver `v1.7.9`:
``` ```
VERSION=v1.7.9 bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/refs/tags/$VERSION/install.sh") $VERSION VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
``` ```
## SSL Сертификат ## SSL Сертификат
@@ -54,6 +54,8 @@ VERSION=v1.7.9 bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui
- **Get SSL:** Получить SSL сертификаты. - **Get SSL:** Получить SSL сертификаты.
- **Revoke:** Отозвать существующие SSL сертификаты. - **Revoke:** Отозвать существующие SSL сертификаты.
- **Force Renew:** Принудительно перевыпустить SSL сертификаты. - **Force Renew:** Принудительно перевыпустить SSL сертификаты.
- **Show Existing Domains:** Отобразить все сертификаты доменов, доступные на сервере.
- **Set Certificate Paths for the Panel:** Укажите сертификат для вашего домена, который будет использоваться панелью.
### Certbot ### Certbot

View File

@@ -30,12 +30,12 @@
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
``` ```
## 安装指定版本 (我们不建议) ## 安装版本 (我们不建议)
要安装您想要的版本请使用以下安装命令。例如ver `v1.7.9`: 要安装您想要的版本请使用以下安装命令。例如ver `v1.7.9`:
``` ```
VERSION=v1.7.9 bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/refs/tags/$VERSION/install.sh") $VERSION VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
``` ```
### SSL证书 ### SSL证书
@@ -51,9 +51,11 @@ VERSION=v1.7.9 bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui
2. 在终端中运行 `x-ui` 命令,然后选择 `SSL证书管理` 2. 在终端中运行 `x-ui` 命令,然后选择 `SSL证书管理`
3. 您将看到以下选项: 3. 您将看到以下选项:
- **获取SSL证书:** 获取SSL证书。 - **Get SSL:** 获取SSL证书。
- **吊销:** 吊销现有的SSL证书。 - **Revoke:** 吊销现有的SSL证书。
- **强制更新:** 强制更新SSL证书。 - **Force Renew:** 强制更新SSL证书。
- **Show Existing Domains:** 显示服务器上所有可用的域证书。
- **Set Certificate Paths for the Panel:** 指定用于面板的域证书。
### Certbot ### Certbot

View File

@@ -1 +1 @@
2.4.5 2.4.6

16
go.mod
View File

@@ -13,8 +13,8 @@ require (
github.com/pelletier/go-toml/v2 v2.2.3 github.com/pelletier/go-toml/v2 v2.2.3
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v4 v4.24.9 github.com/shirou/gopsutil/v4 v4.24.9
github.com/valyala/fasthttp v1.56.0 github.com/valyala/fasthttp v1.57.0
github.com/xtls/xray-core v1.8.25-0.20241005021528-c30f5d47964b github.com/xtls/xray-core v1.8.25-0.20241031075831-4ec5c78c3453
go.uber.org/atomic v1.11.0 go.uber.org/atomic v1.11.0
golang.org/x/text v0.19.0 golang.org/x/text v0.19.0
google.golang.org/grpc v1.67.1 google.golang.org/grpc v1.67.1
@@ -25,12 +25,12 @@ require (
require ( require (
github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect
github.com/bytedance/sonic v1.12.3 // indirect github.com/bytedance/sonic v1.12.3 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/bytedance/sonic/loader v0.2.1 // indirect
github.com/cloudflare/circl v1.5.0 // indirect github.com/cloudflare/circl v1.5.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect github.com/cloudwego/iasm v0.2.0 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/ebitengine/purego v0.8.0 // indirect github.com/ebitengine/purego v0.8.1 // indirect
github.com/fasthttp/router v1.5.2 // indirect github.com/fasthttp/router v1.5.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
@@ -40,7 +40,7 @@ require (
github.com/go-playground/validator/v10 v10.22.1 // indirect github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/btree v1.1.3 // indirect github.com/google/btree v1.1.3 // indirect
github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect github.com/google/pprof v0.0.0-20241023014458-598669927662 // indirect
github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.4.0 // indirect github.com/gorilla/sessions v1.4.0 // indirect
@@ -62,7 +62,7 @@ require (
github.com/pires/go-proxyproto v0.8.0 // indirect github.com/pires/go-proxyproto v0.8.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.48.0 // indirect github.com/quic-go/quic-go v0.48.1 // indirect
github.com/refraction-networking/utls v1.6.7 // indirect github.com/refraction-networking/utls v1.6.7 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect
@@ -81,7 +81,7 @@ require (
github.com/vishvananda/netns v0.0.4 // indirect github.com/vishvananda/netns v0.0.4 // indirect
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/mock v0.4.0 // indirect go.uber.org/mock v0.5.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.11.0 // indirect golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.28.0 // indirect golang.org/x/crypto v0.28.0 // indirect
@@ -94,7 +94,7 @@ require (
golang.org/x/tools v0.26.0 // indirect golang.org/x/tools v0.26.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/protobuf v1.35.1 // indirect google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect

32
go.sum
View File

@@ -7,8 +7,8 @@ github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOL
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
@@ -22,8 +22,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/fasthttp/router v1.5.2 h1:ckJCCdV7hWkkrMeId3WfEhz+4Gyyf6QPwxi/RHIMZ6I= github.com/fasthttp/router v1.5.2 h1:ckJCCdV7hWkkrMeId3WfEhz+4Gyyf6QPwxi/RHIMZ6I=
github.com/fasthttp/router v1.5.2/go.mod h1:C8EY53ozOwpONyevc/V7Gr8pqnEjwnkFFqPo1alAGs0= github.com/fasthttp/router v1.5.2/go.mod h1:C8EY53ozOwpONyevc/V7Gr8pqnEjwnkFFqPo1alAGs0=
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
@@ -64,8 +64,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241009165004-a3522334989c h1:NDovD0SMpBYXlE1zJmS1q55vWB/fUQBcPAqAboZSccA= github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA=
github.com/google/pprof v0.0.0-20241009165004-a3522334989c/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
@@ -129,8 +129,8 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.48.0 h1:2TCyvBrMu1Z25rvIAlnp2dPT4lgh/uTqLqiXVpp5AeU= github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
github.com/quic-go/quic-go v0.48.0/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM= github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
@@ -172,8 +172,8 @@ github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW8U= github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg=
github.com/valyala/fasthttp v1.56.0/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI= github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE=
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk= github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
@@ -182,16 +182,16 @@ github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1Y
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM= github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg= github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
github.com/xtls/xray-core v1.8.25-0.20241005021528-c30f5d47964b h1:bWuePNnzV4ptnSYJkY96dAg3WgjYbfVgGnasLe3++9w= github.com/xtls/xray-core v1.8.25-0.20241031075831-4ec5c78c3453 h1:2piT7IYX0SKhYjD+XiJMDZAbY01MkP1HYO54njmisaQ=
github.com/xtls/xray-core v1.8.25-0.20241005021528-c30f5d47964b/go.mod h1:YSvBScSqyzAocGDvzHBbEeoHNrFy8nV6gityRVDvHaM= github.com/xtls/xray-core v1.8.25-0.20241031075831-4ec5c78c3453/go.mod h1:OlJhs59caMUabGbOamwTc2khBSOfd34qtVJVXFhpfWM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
@@ -225,8 +225,8 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=

View File

@@ -49,6 +49,8 @@ elif [[ "${release}" == "manjaro" ]]; then
echo "Your OS is Manjaro" echo "Your OS is Manjaro"
elif [[ "${release}" == "armbian" ]]; then elif [[ "${release}" == "armbian" ]]; then
echo "Your OS is Armbian" echo "Your OS is Armbian"
elif [[ "${release}" == "alpine" ]]; then
echo "Your OS is Alpine Linux"
elif [[ "${release}" == "opensuse-tumbleweed" ]]; then elif [[ "${release}" == "opensuse-tumbleweed" ]]; then
echo "Your OS is OpenSUSE Tumbleweed" echo "Your OS is OpenSUSE Tumbleweed"
elif [[ "${release}" == "openEuler" ]]; then elif [[ "${release}" == "openEuler" ]]; then
@@ -137,69 +139,59 @@ gen_random_string() {
} }
config_after_install() { config_after_install() {
local existing_username=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'username: .+' | awk '{print $2}') local existing_username=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'username: .+' | awk '{print $2}')
local existing_password=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'password: .+' | awk '{print $2}') local existing_password=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'password: .+' | awk '{print $2}')
local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}') local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
local server_ip=$(curl -s https://api.ipify.org)
# Check if username and password exist if [[ ${#existing_webBasePath} -lt 4 ]]; then
if [[ -n "$existing_username" && -n "$existing_password" ]]; then if [[ "$existing_username" == "admin" && "$existing_password" == "admin" ]]; then
# If webBasePath is missing, generate a new one
if [[ ${#existing_webBasePath} -lt 4 ]]; then
local config_webBasePath=$(gen_random_string 15) local config_webBasePath=$(gen_random_string 15)
echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}" local config_username=$(gen_random_string 10)
/usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}" local config_password=$(gen_random_string 10)
echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
else
echo -e "${green}Username, Password, and WebBasePath are already set. Exiting...${plain}"
fi
/usr/local/x-ui/x-ui migrate
return 0
fi
read -p "Would you like to customize the Panel Port settings? (If not, random settings will be applied) [y/n]: " config_confirm read -p "Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: " config_confirm
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
read -p "Please set up the panel port: " config_port
echo -e "${yellow}Your Panel Port is: ${config_port}${plain}"
else
local config_port=$(shuf -i 1024-62000 -n 1)
echo -e "${yellow}Generated random port: ${config_port}${plain}"
fi
local config_webBasePath=$(gen_random_string 15) /usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
local config_username=$(gen_random_string 10)
local config_password=$(gen_random_string 10)
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
read -p "Please set up the panel port: " config_port
echo -e "${yellow}Your Panel Port is: ${config_port}${plain}"
echo -e "${yellow}Your Username will be generated randomly: ${config_username}${plain}"
echo -e "${yellow}Your Password will be generated randomly: ${config_password}${plain}"
echo -e "${yellow}Your Web Base Path will be generated randomly: ${config_webBasePath}${plain}"
echo -e "${yellow}Initializing, please wait...${plain}"
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
echo -e "${green}Settings applied successfully!${plain}"
echo -e "###############################################"
echo -e "${green}Username: ${config_username}${plain}"
echo -e "${green}Password: ${config_password}${plain}"
echo -e "${green}Port: ${config_port}${plain}"
echo -e "${green}WebBasePath: ${config_webBasePath}${plain}"
echo -e "###############################################"
else
echo -e "${red}Cancel...${plain}"
if [[ ! -f "/etc/x-ui/x-ui.db" ]]; then
local portTemp=$(shuf -i 1024-62000 -n 1)
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${portTemp}" -webBasePath "${config_webBasePath}"
echo -e "This is a fresh installation, generating random login info for security concerns:" echo -e "This is a fresh installation, generating random login info for security concerns:"
echo -e "###############################################" echo -e "###############################################"
echo -e "${green}Username: ${config_username}${plain}" echo -e "${green}Username: ${config_username}${plain}"
echo -e "${green}Password: ${config_password}${plain}" echo -e "${green}Password: ${config_password}${plain}"
echo -e "${green}Port: ${portTemp}${plain}" echo -e "${green}Port: ${config_port}${plain}"
echo -e "${green}WebBasePath: ${config_webBasePath}${plain}" echo -e "${green}WebBasePath: ${config_webBasePath}${plain}"
echo -e "${green}Access URL: http://${server_ip}:${config_port}/${config_webBasePath}${plain}"
echo -e "###############################################" echo -e "###############################################"
echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check after installation${plain}" echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check${plain}"
else else
echo -e "${yellow}This is your upgrade, keeping old settings. If you forgot your login info, you can type 'x-ui settings' to check${plain}" local config_webBasePath=$(gen_random_string 15)
echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}"
/usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}"
echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
echo -e "${green}Access URL: http://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
fi
else
if [[ "$existing_username" == "admin" && "$existing_password" == "admin" ]]; then
local config_username=$(gen_random_string 10)
local config_password=$(gen_random_string 10)
echo -e "${yellow}Default credentials detected. Security update required...${plain}"
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}"
echo -e "Generated new random login credentials:"
echo -e "###############################################"
echo -e "${green}Username: ${config_username}${plain}"
echo -e "${green}Password: ${config_password}${plain}"
echo -e "###############################################"
echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check${plain}"
else
echo -e "${green}Username, Password, and WebBasePath are properly set. Exiting...${plain}"
fi fi
fi fi
@@ -282,7 +274,7 @@ install_x-ui() {
echo -e "x-ui log - Check logs" echo -e "x-ui log - Check logs"
echo -e "x-ui banlog - Check Fail2ban ban logs" echo -e "x-ui banlog - Check Fail2ban ban logs"
echo -e "x-ui update - Update" echo -e "x-ui update - Update"
echo -e "x-ui custom - custom version" echo -e "x-ui legacy - legacy version"
echo -e "x-ui install - Install" echo -e "x-ui install - Install"
echo -e "x-ui uninstall - Uninstall" echo -e "x-ui uninstall - Uninstall"
echo -e "----------------------------------------------" echo -e "----------------------------------------------"

77
main.go
View File

@@ -136,6 +136,15 @@ func showSetting(show bool) {
fmt.Println("get webBasePath failed, error info:", err) fmt.Println("get webBasePath failed, error info:", err)
} }
certFile, err := settingService.GetCertFile()
if err != nil {
fmt.Println("get cert file failed, error info:", err)
}
keyFile, err := settingService.GetKeyFile()
if err != nil {
fmt.Println("get key file failed, error info:", err)
}
userService := service.UserService{} userService := service.UserService{}
userModel, err := userService.GetFirstUser() userModel, err := userService.GetFirstUser()
if err != nil { if err != nil {
@@ -149,14 +158,15 @@ func showSetting(show bool) {
} }
fmt.Println("current panel settings as follows:") fmt.Println("current panel settings as follows:")
if certFile == "" || keyFile == "" {
fmt.Println("Warning: Panel is not secure with SSL")
} else {
fmt.Println("Panel is secure with SSL")
}
fmt.Println("username:", username) fmt.Println("username:", username)
fmt.Println("password:", userpasswd) fmt.Println("password:", userpasswd)
fmt.Println("port:", port) fmt.Println("port:", port)
if webBasePath != "" { fmt.Println("webBasePath:", webBasePath)
fmt.Println("webBasePath:", webBasePath)
} else {
fmt.Println("webBasePath is not set")
}
} }
} }
@@ -216,7 +226,7 @@ func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime stri
} }
} }
func updateSetting(port int, username string, password string, webBasePath string) { func updateSetting(port int, username string, password string, webBasePath string, listenIP string) {
err := database.InitDB(config.GetDBPath()) err := database.InitDB(config.GetDBPath())
if err != nil { if err != nil {
fmt.Println("Database initialization failed:", err) fmt.Println("Database initialization failed:", err)
@@ -252,6 +262,15 @@ func updateSetting(port int, username string, password string, webBasePath strin
fmt.Println("Base URI path set successfully") fmt.Println("Base URI path set successfully")
} }
} }
if listenIP != "" {
err := settingService.SetListen(listenIP)
if err != nil {
fmt.Println("Failed to set listen IP:", err)
} else {
fmt.Printf("listen %v set successfully", listenIP)
}
}
} }
func updateCert(publicKey string, privateKey string) { func updateCert(publicKey string, privateKey string) {
@@ -281,6 +300,37 @@ func updateCert(publicKey string, privateKey string) {
} }
} }
func GetCertificate(getCert bool) {
if getCert {
settingService := service.SettingService{}
certFile, err := settingService.GetCertFile()
if err != nil {
fmt.Println("get cert file failed, error info:", err)
}
keyFile, err := settingService.GetKeyFile()
if err != nil {
fmt.Println("get key file failed, error info:", err)
}
fmt.Println("cert:", certFile)
fmt.Println("key:", keyFile)
}
}
func GetListenIP(getListen bool) {
if getListen {
settingService := service.SettingService{}
ListenIP, err := settingService.GetListen()
if err != nil {
log.Printf("Failed to retrieve listen IP: %v", err)
return
}
fmt.Println("listenIP:", ListenIP)
}
}
func migrateDb() { func migrateDb() {
inboundService := service.InboundService{} inboundService := service.InboundService{}
@@ -339,6 +389,8 @@ func main() {
var username string var username string
var password string var password string
var webBasePath string var webBasePath string
var listenIP string
var getListen bool
var webCertFile string var webCertFile string
var webKeyFile string var webKeyFile string
var tgbottoken string var tgbottoken string
@@ -347,6 +399,7 @@ func main() {
var tgbotRuntime string var tgbotRuntime string
var reset bool var reset bool
var show bool var show bool
var getCert bool
var remove_secret bool var remove_secret bool
settingCmd.BoolVar(&reset, "reset", false, "Reset all settings") settingCmd.BoolVar(&reset, "reset", false, "Reset all settings")
settingCmd.BoolVar(&show, "show", false, "Display current settings") settingCmd.BoolVar(&show, "show", false, "Display current settings")
@@ -355,6 +408,9 @@ func main() {
settingCmd.StringVar(&username, "username", "", "Set login username") settingCmd.StringVar(&username, "username", "", "Set login username")
settingCmd.StringVar(&password, "password", "", "Set login password") settingCmd.StringVar(&password, "password", "", "Set login password")
settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel") settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel")
settingCmd.StringVar(&listenIP, "listenIP", "", "set panel listenIP IP")
settingCmd.BoolVar(&getListen, "getListen", false, "Display current panel listenIP IP")
settingCmd.BoolVar(&getCert, "getCert", false, "Display current certificate settings")
settingCmd.StringVar(&webCertFile, "webCert", "", "Set path to public key file for panel") settingCmd.StringVar(&webCertFile, "webCert", "", "Set path to public key file for panel")
settingCmd.StringVar(&webKeyFile, "webCertKey", "", "Set path to private key file for panel") settingCmd.StringVar(&webKeyFile, "webCertKey", "", "Set path to private key file for panel")
settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "Set token for Telegram bot") settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "Set token for Telegram bot")
@@ -397,11 +453,17 @@ func main() {
if reset { if reset {
resetSetting() resetSetting()
} else { } else {
updateSetting(port, username, password, webBasePath) updateSetting(port, username, password, webBasePath, listenIP)
} }
if show { if show {
showSetting(show) showSetting(show)
} }
if getListen {
GetListenIP(getListen)
}
if getCert {
GetCertificate(getCert)
}
if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") { if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") {
updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime) updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
} }
@@ -422,7 +484,6 @@ func main() {
} else { } else {
updateCert(webCertFile, webKeyFile) updateCert(webCertFile, webKeyFile)
} }
default: default:
fmt.Println("Invalid subcommands") fmt.Println("Invalid subcommands")
fmt.Println() fmt.Println()

View File

@@ -452,38 +452,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
} }
} }
if security == "xtls" { if security != "tls" && security != "reality" {
params["security"] = "xtls"
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
alpns, _ := xtlsSetting["alpn"].([]interface{})
var alpn []string
for _, a := range alpns {
alpn = append(alpn, a.(string))
}
if len(alpn) > 0 {
params["alpn"] = strings.Join(alpn, ",")
}
if sniValue, ok := searchKey(xtlsSetting, "serverName"); ok {
params["sni"], _ = sniValue.(string)
}
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
if xtlsSetting != nil {
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
params["fp"], _ = fpValue.(string)
}
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
if insecure.(bool) {
params["allowInsecure"] = "1"
}
}
}
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
params["flow"] = clients[clientIndex].Flow
}
}
if security != "tls" && security != "reality" && security != "xtls" {
params["security"] = "none" params["security"] = "none"
} }
@@ -676,39 +645,7 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
} }
} }
if security == "xtls" { if security != "tls" && security != "reality" {
params["security"] = "xtls"
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
alpns, _ := xtlsSetting["alpn"].([]interface{})
var alpn []string
for _, a := range alpns {
alpn = append(alpn, a.(string))
}
if len(alpn) > 0 {
params["alpn"] = strings.Join(alpn, ",")
}
if sniValue, ok := searchKey(xtlsSetting, "serverName"); ok {
params["sni"], _ = sniValue.(string)
}
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
if xtlsSetting != nil {
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
params["fp"], _ = fpValue.(string)
}
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
if insecure.(bool) {
params["allowInsecure"] = "1"
}
}
}
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
params["flow"] = clients[clientIndex].Flow
}
}
if security != "tls" && security != "reality" && security != "xtls" {
params["security"] = "none" params["security"] = "none"
} }

View File

@@ -10,7 +10,7 @@ const supportLangs = [
icon: "🇮🇷", icon: "🇮🇷",
}, },
{ {
name: "簡體中文", name: "简体中文",
value: "zh-CN", value: "zh-CN",
icon: "🇨🇳", icon: "🇨🇳",
}, },

View File

@@ -21,11 +21,6 @@ const SSMethods = {
BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305', BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
}; };
const XTLS_FLOW_CONTROL = {
ORIGIN: "xtls-rprx-origin",
DIRECT: "xtls-rprx-direct",
};
const TLS_FLOW_CONTROL = { const TLS_FLOW_CONTROL = {
VISION: "xtls-rprx-vision", VISION: "xtls-rprx-vision",
VISION_UDP443: "xtls-rprx-vision-udp443", VISION_UDP443: "xtls-rprx-vision-udp443",
@@ -120,7 +115,6 @@ const USERS_SECURITY = {
Object.freeze(Protocols); Object.freeze(Protocols);
Object.freeze(SSMethods); Object.freeze(SSMethods);
Object.freeze(XTLS_FLOW_CONTROL);
Object.freeze(TLS_FLOW_CONTROL); Object.freeze(TLS_FLOW_CONTROL);
Object.freeze(TLS_VERSION_OPTION); Object.freeze(TLS_VERSION_OPTION);
Object.freeze(TLS_CIPHER_OPTION); Object.freeze(TLS_CIPHER_OPTION);
@@ -755,137 +749,6 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
} }
}; };
class XtlsStreamSettings extends XrayCommonClass {
constructor(
serverName = '',
certificates = [new XtlsStreamSettings.Cert()],
alpn = [ALPN_OPTION.H3, ALPN_OPTION.H2, ALPN_OPTION.HTTP1],
settings = new XtlsStreamSettings.Settings()
) {
super();
this.sni = serverName;
this.certs = certificates;
this.alpn = alpn;
this.settings = settings;
}
addCert() {
this.certs.push(new XtlsStreamSettings.Cert());
}
removeCert(index) {
this.certs.splice(index, 1);
}
static fromJson(json = {}) {
let certs;
let settings;
if (!ObjectUtil.isEmpty(json.certificates)) {
certs = json.certificates.map(cert => XtlsStreamSettings.Cert.fromJson(cert));
}
if (!ObjectUtil.isEmpty(json.settings)) {
settings = new XtlsStreamSettings.Settings(json.settings.allowInsecure, json.settings.serverName);
}
return new XtlsStreamSettings(
json.serverName,
certs,
json.alpn,
settings,
);
}
toJson() {
return {
serverName: this.sni,
certificates: XtlsStreamSettings.toJsonArray(this.certs),
alpn: this.alpn,
settings: this.settings,
};
}
}
XtlsStreamSettings.Cert = class extends XrayCommonClass {
constructor(
useFile = true,
certificateFile = '',
keyFile = '',
certificate = '',
key = '',
ocspStapling = 3600,
oneTimeLoading = false,
usage = USAGE_OPTION.ENCIPHERMENT
) {
super();
this.useFile = useFile;
this.certFile = certificateFile;
this.keyFile = keyFile;
this.cert = Array.isArray(certificate) ? certificate.join('\n') : certificate;
this.key = Array.isArray(key) ? key.join('\n') : key;
this.ocspStapling = ocspStapling;
this.oneTimeLoading = oneTimeLoading;
this.usage = usage;
}
static fromJson(json = {}) {
if ('certificateFile' in json && 'keyFile' in json) {
return new XtlsStreamSettings.Cert(
true,
json.certificateFile,
json.keyFile, '', '',
json.ocspStapling,
json.oneTimeLoading,
json.usage,
);
} else {
return new XtlsStreamSettings.Cert(
false, '', '',
json.certificate.join('\n'),
json.key.join('\n'),
json.ocspStapling,
json.oneTimeLoading,
json.usage,
);
}
}
toJson() {
if (this.useFile) {
return {
certificateFile: this.certFile,
keyFile: this.keyFile,
ocspStapling: this.ocspStapling,
oneTimeLoading: this.oneTimeLoading,
usage: this.usage,
};
} else {
return {
certificate: this.cert.split('\n'),
key: this.key.split('\n'),
ocspStapling: this.ocspStapling,
oneTimeLoading: this.oneTimeLoading,
usage: this.usage,
};
}
}
};
XtlsStreamSettings.Settings = class extends XrayCommonClass {
constructor(allowInsecure = false) {
super();
this.allowInsecure = allowInsecure;
}
static fromJson(json = {}) {
return new XtlsStreamSettings.Settings(
json.allowInsecure,
);
}
toJson() {
return {
allowInsecure: this.allowInsecure,
};
}
};
class RealityStreamSettings extends XrayCommonClass { class RealityStreamSettings extends XrayCommonClass {
constructor( constructor(
@@ -1071,7 +934,6 @@ class StreamSettings extends XrayCommonClass {
security = 'none', security = 'none',
externalProxy = [], externalProxy = [],
tlsSettings = new TlsStreamSettings(), tlsSettings = new TlsStreamSettings(),
xtlsSettings = new XtlsStreamSettings(),
realitySettings = new RealityStreamSettings(), realitySettings = new RealityStreamSettings(),
tcpSettings = new TcpStreamSettings(), tcpSettings = new TcpStreamSettings(),
kcpSettings = new KcpStreamSettings(), kcpSettings = new KcpStreamSettings(),
@@ -1087,7 +949,6 @@ class StreamSettings extends XrayCommonClass {
this.security = security; this.security = security;
this.externalProxy = externalProxy; this.externalProxy = externalProxy;
this.tls = tlsSettings; this.tls = tlsSettings;
this.xtls = xtlsSettings;
this.reality = realitySettings; this.reality = realitySettings;
this.tcp = tcpSettings; this.tcp = tcpSettings;
this.kcp = kcpSettings; this.kcp = kcpSettings;
@@ -1111,18 +972,6 @@ class StreamSettings extends XrayCommonClass {
} }
} }
get isXtls() {
return this.security === "xtls";
}
set isXtls(isXtls) {
if (isXtls) {
this.security = 'xtls';
} else {
this.security = 'none';
}
}
//for Reality //for Reality
get isReality() { get isReality() {
return this.security === "reality"; return this.security === "reality";
@@ -1150,7 +999,6 @@ class StreamSettings extends XrayCommonClass {
json.security, json.security,
json.externalProxy, json.externalProxy,
TlsStreamSettings.fromJson(json.tlsSettings), TlsStreamSettings.fromJson(json.tlsSettings),
XtlsStreamSettings.fromJson(json.xtlsSettings),
RealityStreamSettings.fromJson(json.realitySettings), RealityStreamSettings.fromJson(json.realitySettings),
TcpStreamSettings.fromJson(json.tcpSettings), TcpStreamSettings.fromJson(json.tcpSettings),
KcpStreamSettings.fromJson(json.kcpSettings), KcpStreamSettings.fromJson(json.kcpSettings),
@@ -1170,7 +1018,6 @@ class StreamSettings extends XrayCommonClass {
security: this.security, security: this.security,
externalProxy: this.externalProxy, externalProxy: this.externalProxy,
tlsSettings: this.isTls ? this.tls.toJson() : undefined, tlsSettings: this.isTls ? this.tls.toJson() : undefined,
xtlsSettings: this.isXtls ? this.xtls.toJson() : undefined,
realitySettings: this.isReality ? this.reality.toJson() : undefined, realitySettings: this.isReality ? this.reality.toJson() : undefined,
tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined, tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined, kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
@@ -1283,18 +1130,6 @@ class Inbound extends XrayCommonClass {
} }
} }
get xtls() {
return this.stream.security === 'xtls';
}
set xtls(isXtls) {
if (isXtls) {
this.stream.security = 'xtls';
} else {
this.stream.security = 'none';
}
}
get network() { get network() {
return this.stream.network; return this.stream.network;
} }
@@ -1349,7 +1184,6 @@ class Inbound extends XrayCommonClass {
get serverName() { get serverName() {
if (this.stream.isTls) return this.stream.tls.sni; if (this.stream.isTls) return this.stream.tls.sni;
if (this.stream.isXtls) return this.stream.xtls.sni;
if (this.stream.isReality) return this.stream.reality.serverNames; if (this.stream.isReality) return this.stream.reality.serverNames;
return ""; return "";
} }
@@ -1425,12 +1259,7 @@ class Inbound extends XrayCommonClass {
canEnableReality() { canEnableReality() {
if (![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false; if (![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
return ["tcp", "http", "grpc"].includes(this.network); return ["tcp", "http", "grpc", "splithttp"].includes(this.network);
}
canEnableXtls() {
if (![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
return this.network === "tcp";
} }
canEnableStream() { canEnableStream() {
@@ -1592,18 +1421,6 @@ class Inbound extends XrayCommonClass {
} }
} }
else if (security === 'xtls') {
params.set("security", "xtls");
params.set("alpn", this.stream.xtls.alpn);
if (this.stream.xtls.settings.allowInsecure) {
params.set("allowInsecure", "1");
}
if (!ObjectUtil.isEmpty(this.stream.xtls.sni)) {
params.set("sni", this.stream.xtls.sni);
}
params.set("flow", flow);
}
else if (security === 'reality') { else if (security === 'reality') {
params.set("security", "reality"); params.set("security", "reality");
params.set("pbk", this.stream.reality.settings.publicKey); params.set("pbk", this.stream.reality.settings.publicKey);
@@ -1801,18 +1618,6 @@ class Inbound extends XrayCommonClass {
} }
} }
else if (security === 'xtls') {
params.set("security", "xtls");
params.set("alpn", this.stream.xtls.alpn);
if (this.stream.xtls.settings.allowInsecure) {
params.set("allowInsecure", "1");
}
if (!ObjectUtil.isEmpty(this.stream.xtls.sni)) {
params.set("sni", this.stream.xtls.sni);
}
params.set("flow", flow);
}
else { else {
params.set("security", "none"); params.set("security", "none");
} }
@@ -2273,7 +2078,6 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
Inbound.TrojanSettings.Trojan = class extends XrayCommonClass { Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
constructor( constructor(
password = RandomUtil.randomSeq(10), password = RandomUtil.randomSeq(10),
flow = '',
email = RandomUtil.randomLowerAndNum(8), email = RandomUtil.randomLowerAndNum(8),
limitIp = 0, limitIp = 0,
totalGB = 0, totalGB = 0,
@@ -2285,7 +2089,6 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
) { ) {
super(); super();
this.password = password; this.password = password;
this.flow = flow;
this.email = email; this.email = email;
this.limitIp = limitIp; this.limitIp = limitIp;
this.totalGB = totalGB; this.totalGB = totalGB;
@@ -2299,7 +2102,6 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
toJson() { toJson() {
return { return {
password: this.password, password: this.password,
flow: this.flow,
email: this.email, email: this.email,
limitIp: this.limitIp, limitIp: this.limitIp,
totalGB: this.totalGB, totalGB: this.totalGB,
@@ -2314,7 +2116,6 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
static fromJson(json = {}) { static fromJson(json = {}) {
return new Inbound.TrojanSettings.Trojan( return new Inbound.TrojanSettings.Trojan(
json.password, json.password,
json.flow,
json.email, json.email,
json.limitIp, json.limitIp,
json.totalGB, json.totalGB,
@@ -2651,16 +2452,14 @@ Inbound.WireguardSettings = class extends XrayCommonClass {
mtu = 1420, mtu = 1420,
secretKey = Wireguard.generateKeypair().privateKey, secretKey = Wireguard.generateKeypair().privateKey,
peers = [new Inbound.WireguardSettings.Peer()], peers = [new Inbound.WireguardSettings.Peer()],
kernelMode = false, noKernelTun = false
kernelTun = false,
) { ) {
super(protocol); super(protocol);
this.mtu = mtu; this.mtu = mtu;
this.secretKey = secretKey; this.secretKey = secretKey;
this.pubKey = secretKey.length > 0 ? Wireguard.generateKeypair(secretKey).publicKey : ''; this.pubKey = secretKey.length > 0 ? Wireguard.generateKeypair(secretKey).publicKey : '';
this.peers = peers; this.peers = peers;
this.kernelMode = kernelMode; this.noKernelTun = noKernelTun;
this.kernelTun = kernelTun;
} }
addPeer() { addPeer() {
@@ -2677,8 +2476,7 @@ Inbound.WireguardSettings = class extends XrayCommonClass {
json.mtu, json.mtu,
json.secretKey, json.secretKey,
json.peers.map(peer => Inbound.WireguardSettings.Peer.fromJson(peer)), json.peers.map(peer => Inbound.WireguardSettings.Peer.fromJson(peer)),
json.kernelMode, json.noKernelTun,
json.kernelTun,
); );
} }
@@ -2687,8 +2485,7 @@ Inbound.WireguardSettings = class extends XrayCommonClass {
mtu: this.mtu ?? undefined, mtu: this.mtu ?? undefined,
secretKey: this.secretKey, secretKey: this.secretKey,
peers: Inbound.WireguardSettings.Peer.toJsonArray(this.peers), peers: Inbound.WireguardSettings.Peer.toJsonArray(this.peers),
kernelMode: this.kernelMode, noKernelTun: this.noKernelTun,
kernelTun: this.kernelTun,
}; };
} }
}; };

View File

@@ -602,7 +602,7 @@ class Outbound extends CommonClass {
canEnableReality() { canEnableReality() {
if (![Protocols.VLESS, Protocols.Trojan].includes(this.protocol)) return false; if (![Protocols.VLESS, Protocols.Trojan].includes(this.protocol)) return false;
return ["tcp", "http", "grpc"].includes(this.stream.network); return ["tcp", "http", "grpc", "splithttp"].includes(this.stream.network);
} }
canEnableStream() { canEnableStream() {
@@ -875,16 +875,16 @@ Outbound.FreedomSettings = class extends CommonClass {
json.domainStrategy, json.domainStrategy,
json.redirect, json.redirect,
json.fragment ? Outbound.FreedomSettings.Fragment.fromJson(json.fragment) : undefined, json.fragment ? Outbound.FreedomSettings.Fragment.fromJson(json.fragment) : undefined,
json.noises ? json.noises.map(noise => Outbound.FreedomSettings.Noise.fromJson(noise)) : [new Outbound.FreedomSettings.Noise()], json.noises ? json.noises.map(noise => Outbound.FreedomSettings.Noise.fromJson(noise)) : undefined,
); );
} }
toJson() { toJson() {
return { return {
domainStrategy: ObjectUtil.isEmpty(this.domainStrategy) ? undefined : this.domainStrategy, domainStrategy: ObjectUtil.isEmpty(this.domainStrategy) ? undefined : this.domainStrategy,
redirect: this.redirect, redirect: ObjectUtil.isEmpty(this.redirect) ? undefined: this.redirect,
fragment: Object.keys(this.fragment).length === 0 ? undefined : this.fragment, fragment: Object.keys(this.fragment).length === 0 ? undefined : this.fragment,
noises: Outbound.FreedomSettings.Noise.toJsonArray(this.noises), noises: this.noises.length === 0 ? undefined : Outbound.FreedomSettings.Noise.toJsonArray(this.noises),
}; };
} }
}; };
@@ -937,10 +937,6 @@ Outbound.FreedomSettings.Noise = class extends CommonClass {
delay: this.delay, delay: this.delay,
}; };
} }
static toJsonArray(noises) {
return noises.map(noise => noise.toJson());
}
}; };
Outbound.BlackholeSettings = class extends CommonClass { Outbound.BlackholeSettings = class extends CommonClass {
@@ -1182,8 +1178,7 @@ Outbound.WireguardSettings = class extends CommonClass {
domainStrategy = '', domainStrategy = '',
reserved = '', reserved = '',
peers = [new Outbound.WireguardSettings.Peer()], peers = [new Outbound.WireguardSettings.Peer()],
kernelMode = false, noKernelTun = false,
kernelTun = false
) { ) {
super(); super();
this.mtu = mtu; this.mtu = mtu;
@@ -1194,8 +1189,7 @@ Outbound.WireguardSettings = class extends CommonClass {
this.domainStrategy = domainStrategy; this.domainStrategy = domainStrategy;
this.reserved = Array.isArray(reserved) ? reserved.join(',') : reserved; this.reserved = Array.isArray(reserved) ? reserved.join(',') : reserved;
this.peers = peers; this.peers = peers;
this.kernelMode = kernelMode; this.noKernelTun = noKernelTun;
this.kernelTun = kernelTun;
} }
addPeer() { addPeer() {
@@ -1215,8 +1209,7 @@ Outbound.WireguardSettings = class extends CommonClass {
json.domainStrategy, json.domainStrategy,
json.reserved, json.reserved,
json.peers.map(peer => Outbound.WireguardSettings.Peer.fromJson(peer)), json.peers.map(peer => Outbound.WireguardSettings.Peer.fromJson(peer)),
json.kernelMode, json.noKernelTun,
json.kernelTun,
); );
} }
@@ -1229,8 +1222,7 @@ Outbound.WireguardSettings = class extends CommonClass {
domainStrategy: WireguardDomainStrategy.includes(this.domainStrategy) ? this.domainStrategy : undefined, domainStrategy: WireguardDomainStrategy.includes(this.domainStrategy) ? this.domainStrategy : undefined,
reserved: this.reserved ? this.reserved.split(",").map(Number) : undefined, reserved: this.reserved ? this.reserved.split(",").map(Number) : undefined,
peers: Outbound.WireguardSettings.Peer.toJsonArray(this.peers), peers: Outbound.WireguardSettings.Peer.toJsonArray(this.peers),
kernelMode: this.kernelMode, noKernelTun: this.noKernelTun,
kernelTun: this.kernelTun,
}; };
} }
}; };

View File

@@ -39,12 +39,6 @@
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option> <a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='Flow' v-if="clientsBulkModal.inbound.xtls">
<a-select v-model="clientsBulkModal.flow" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item v-if="app.subSettings.enable"> <a-form-item v-if="app.subSettings.enable">
<template slot="label"> <template slot="label">
<a-tooltip> <a-tooltip>
@@ -181,9 +175,6 @@
if (clientsBulkModal.inbound.canEnableTlsFlow()) { if (clientsBulkModal.inbound.canEnableTlsFlow()) {
newClient.flow = clientsBulkModal.flow; newClient.flow = clientsBulkModal.flow;
} }
if (clientsBulkModal.inbound.xtls) {
newClient.flow = clientsBulkModal.flow;
}
newClient.reset = clientsBulkModal.reset; newClient.reset = clientsBulkModal.reset;
clients.push(newClient); clients.push(newClient);
} }

View File

@@ -104,12 +104,6 @@
</a-textarea> </a-textarea>
</a-form> </a-form>
</a-form-item> </a-form-item>
<a-form-item v-if="inbound.stream.isXtls" label='Flow'>
<a-select v-model="client.flow" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item v-if="inbound.canEnableTlsFlow()" label='Flow'> <a-form-item v-if="inbound.canEnableTlsFlow()" label='Flow'>
<a-select v-model="client.flow" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="client.flow" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option> <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>

View File

@@ -147,11 +147,8 @@
<a-form-item label='Workers'> <a-form-item label='Workers'>
<a-input-number v-model.number="outbound.settings.workers" min="0"></a-input-number> <a-input-number v-model.number="outbound.settings.workers" min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Kernel Mode'> <a-form-item label='No Kernel Tun'>
<a-switch v-model="outbound.settings.kernelMode"></a-switch> <a-switch v-model="outbound.settings.noKernelTun"></a-switch>
</a-form-item>
<a-form-item label='Kernel Tun'>
<a-switch v-model="outbound.settings.kernelTun"></a-switch>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<template slot="label"> <template slot="label">

View File

@@ -18,11 +18,8 @@
<a-form-item label='MTU'> <a-form-item label='MTU'>
<a-input-number v-model.number="inbound.settings.mtu"></a-input-number> <a-input-number v-model.number="inbound.settings.mtu"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Kernel Mode'> <a-form-item label='No Kernel Tun'>
<a-switch v-model="inbound.settings.kernelMode"></a-switch> <a-switch v-model="inbound.settings.noKernelTun"></a-switch>
</a-form-item>
<a-form-item label='Kernel Tun'>
<a-switch v-model="inbound.settings.kernelTun"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label="Peers"> <a-form-item label="Peers">
<a-button icon="plus" type="primary" size="small" @click="inbound.settings.addPeer()"></a-button> <a-button icon="plus" type="primary" size="small" @click="inbound.settings.addPeer()"></a-button>

View File

@@ -5,18 +5,7 @@
<a-form-item label='{{ i18n "security" }}'> <a-form-item label='{{ i18n "security" }}'>
<a-radio-group v-model="inbound.stream.security" button-style="solid"> <a-radio-group v-model="inbound.stream.security" button-style="solid">
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button> <a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
<a-tooltip> <a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
<template slot="title">
<span>{{ i18n "pages.inbounds.xtlsDesc" }}</span>
</template>
<a-radio-button v-if="inbound.canEnableXtls()" value="xtls">XTLS</a-radio-button>
</a-tooltip>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.realityDesc" }}</span>
</template>
<a-radio-button v-if="inbound.canEnableReality()" value="reality">REALITY</a-radio-button>
</a-tooltip>
<a-radio-button value="tls">TLS</a-radio-button> <a-radio-button value="tls">TLS</a-radio-button>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
@@ -116,11 +105,6 @@
</template> </template>
</template> </template>
<!-- xtls settings -->
<template v-else-if="inbound.stream.isXtls">
{{template "form/xtlsSettings"}}
</template>
<!-- reality settings --> <!-- reality settings -->
<template v-if="inbound.stream.isReality"> <template v-if="inbound.stream.isReality">
{{template "form/realitySettings"}} {{template "form/realitySettings"}}

View File

@@ -1,58 +0,0 @@
{{define "form/xtlsSettings"}}
<template>
<a-form-item label="SNI" placeholder="Server Name Indication">
<a-input v-model.trim="inbound.stream.xtls.sni"></a-input>
</a-form-item>
<a-form-item label="ALPN">
<a-select mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme" v-model="inbound.stream.xtls.alpn">
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="Allow Insecure">
<a-switch v-model="inbound.stream.xtls.settings.allowInsecure"></a-switch>
</a-form-item>
<template v-for="cert,index in inbound.stream.xtls.certs">
<a-form-item label='{{ i18n "certificate" }}'>
<a-radio-group v-model="cert.useFile" button-style="solid">
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
</a-radio-group>
<a-button icon="plus" v-if="index === 0" type="primary" size="small" @click="inbound.stream.xtls.addCert()"
style="margin-left: 10px"></a-button>
<a-button icon="minus" v-if="inbound.stream.xtls.certs.length>1" type="primary" size="small"
@click="inbound.stream.xtls.removeCert(index)" style="margin-left: 10px"></a-button>
</a-form-item>
<template v-if="cert.useFile">
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
<a-input v-model.trim="cert.certFile"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
<a-input v-model.trim="cert.keyFile"></a-input>
</a-form-item>
<a-form-item label=" ">
<a-button type="primary" icon="import" @click="setDefaultCertXtls(index)">
{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
</a-form-item>
</template>
<template v-else>
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
<a-input type="textarea" :rows="3" v-model="cert.cert"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
<a-input type="textarea" :rows="3" v-model="cert.key"></a-input>
</a-form-item>
</template>
<a-form-item label='OCSP stapling'>
<a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
</a-form-item>
<a-form-item label="One Time Loading">
<a-switch v-model="cert.oneTimeLoading"></a-switch>
</a-form-item>
<a-form-item label='Usage Option'>
<a-select v-model="cert.usage" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in USAGE_OPTION" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
</template>
</template>
{{end}}

View File

@@ -154,15 +154,6 @@
<a-tag color="orange">{{ i18n "none" }}</a-tag> <a-tag color="orange">{{ i18n "none" }}</a-tag>
</td> </td>
</tr> </tr>
<tr v-if="infoModal.inbound.xtls">
<td>Flow</td>
<td v-if="infoModal.clientSettings.flow">
<a-tag>[[ infoModal.clientSettings.flow ]]</a-tag>
</td>
<td v-else>
<a-tag color="orange">{{ i18n "none" }}</a-tag>
</td>
</tr>
<tr v-if="infoModal.clientSettings.password"> <tr v-if="infoModal.clientSettings.password">
<td>{{ i18n "password" }}</td> <td>{{ i18n "password" }}</td>
<td> <td>
@@ -370,12 +361,8 @@
<td>[[ inbound.settings.mtu ]]</td> <td>[[ inbound.settings.mtu ]]</td>
</tr> </tr>
<tr> <tr>
<td>Kernel Mode</td> <td>No Kernel Tun</td>
<td>[[ inbound.settings.kernelMode ]]</td> <td>[[ inbound.settings.noKernelTun ]]</td>
</tr>
<tr>
<td>Kernel Tun</td>
<td>[[ inbound.settings.kernelTun ]]</td>
</tr> </tr>
<template v-for="(peer, index) in inbound.settings.peers"> <template v-for="(peer, index) in inbound.settings.peers">
<tr> <tr>

View File

@@ -102,11 +102,6 @@
client.flow = ""; client.flow = "";
}); });
} }
if ((this.inModal.inbound.protocol == Protocols.VLESS || this.inModal.inbound.protocol == Protocols.TROJAN) && !inModal.inbound.xtls) {
this.inModal.inbound.settings.vlesses.forEach(client => {
client.flow = "";
});
}
}, },
SSMethodChange() { SSMethodChange() {
if (this.inModal.inbound.isSSMultiUser) { if (this.inModal.inbound.isSSMultiUser) {
@@ -132,10 +127,6 @@
inModal.inbound.stream.tls.certs[index].certFile = app.defaultCert; inModal.inbound.stream.tls.certs[index].certFile = app.defaultCert;
inModal.inbound.stream.tls.certs[index].keyFile = app.defaultKey; inModal.inbound.stream.tls.certs[index].keyFile = app.defaultKey;
}, },
setDefaultCertXtls(index) {
inModal.inbound.stream.xtls.certs[index].certFile = app.defaultCert;
inModal.inbound.stream.xtls.certs[index].keyFile = app.defaultKey;
},
async getNewX25519Cert() { async getNewX25519Cert() {
inModal.loading(true); inModal.loading(true);
const msg = await HttpUtil.post('/server/getNewX25519Cert'); const msg = await HttpUtil.post('/server/getNewX25519Cert');

View File

@@ -338,7 +338,6 @@
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS"> <template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
<a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag> <a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag> <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag>
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isXtls" color="blue">XTLS</a-tag>
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag> <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag>
</template> </template>
</template> </template>
@@ -549,7 +548,7 @@
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/clipboard/clipboard.min.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/clipboard/clipboard.min.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
{{template "component/themeSwitcher" .}} {{template "component/themeSwitcher" .}}
{{template "component/persianDatepicker" .}} {{template "component/persianDatepicker" .}}

View File

@@ -147,8 +147,7 @@
publicKey: peer.public_key, publicKey: peer.public_key,
endpoint: peer.endpoint.host, endpoint: peer.endpoint.host,
}], }],
kernelMode: false, noKernelTun: false,
kernelTun: false,
} }
}); });
} }

View File

@@ -9,7 +9,6 @@ import (
"os/exec" "os/exec"
"regexp" "regexp"
"sort" "sort"
"strings"
"time" "time"
"x-ui/database" "x-ui/database"
@@ -37,11 +36,17 @@ func (j *CheckClientIpJob) Run() {
shouldClearAccessLog := false shouldClearAccessLog := false
iplimitActive := j.hasLimitIp() iplimitActive := j.hasLimitIp()
f2bInstalled := j.checkFail2BanInstalled(iplimitActive) f2bInstalled := j.checkFail2BanInstalled()
isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive) isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive)
if iplimitActive && f2bInstalled && isAccessLogAvailable { if iplimitActive {
shouldClearAccessLog = j.processLogFile() if f2bInstalled && isAccessLogAvailable {
shouldClearAccessLog = j.processLogFile()
} else {
if !f2bInstalled {
logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.")
}
}
} }
if shouldClearAccessLog || (isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600) { if shouldClearAccessLog || (isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600) {
@@ -53,23 +58,18 @@ func (j *CheckClientIpJob) clearAccessLog() {
logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644) logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
j.checkError(err) j.checkError(err)
// get access log path to open it
accessLogPath, err := xray.GetAccessLogPath() accessLogPath, err := xray.GetAccessLogPath()
j.checkError(err) j.checkError(err)
// reopen the access log file for reading
file, err := os.Open(accessLogPath) file, err := os.Open(accessLogPath)
j.checkError(err) j.checkError(err)
// copy access log content to persistent file
_, err = io.Copy(logAccessP, file) _, err = io.Copy(logAccessP, file)
j.checkError(err) j.checkError(err)
// close the file after copying content
logAccessP.Close() logAccessP.Close()
file.Close() file.Close()
// clean access log
err = os.Truncate(accessLogPath, 0) err = os.Truncate(accessLogPath, 0)
j.checkError(err) j.checkError(err)
j.lastClear = time.Now().Unix() j.lastClear = time.Now().Unix()
@@ -105,74 +105,69 @@ func (j *CheckClientIpJob) hasLimitIp() bool {
} }
func (j *CheckClientIpJob) processLogFile() bool { func (j *CheckClientIpJob) processLogFile() bool {
accessLogPath, err := xray.GetAccessLogPath()
j.checkError(err)
file, err := os.Open(accessLogPath) ipRegex := regexp.MustCompile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
j.checkError(err) emailRegex := regexp.MustCompile(`email: (.+)$`)
InboundClientIps := make(map[string][]string) accessLogPath, _ := xray.GetAccessLogPath()
file, _ := os.Open(accessLogPath)
defer file.Close()
inboundClientIps := make(map[string]map[string]struct{}, 100)
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
ipRegx, _ := regexp.Compile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`) ipMatches := ipRegex.FindStringSubmatch(line)
emailRegx, _ := regexp.Compile(`email: (\S+)$`) if len(ipMatches) < 2 {
continue
matches := ipRegx.FindStringSubmatch(line)
if len(matches) > 1 {
ip := matches[1]
if ip == "127.0.0.1" || ip == "::1" {
continue
}
matchesEmail := emailRegx.FindString(line)
if matchesEmail == "" {
continue
}
matchesEmail = strings.Split(matchesEmail, "email: ")[1]
if InboundClientIps[matchesEmail] != nil {
if j.contains(InboundClientIps[matchesEmail], ip) {
continue
}
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
} else {
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
}
} }
ip := ipMatches[1]
if ip == "127.0.0.1" || ip == "::1" {
continue
}
emailMatches := emailRegex.FindStringSubmatch(line)
if len(emailMatches) < 2 {
continue
}
email := emailMatches[1]
if _, exists := inboundClientIps[email]; !exists {
inboundClientIps[email] = make(map[string]struct{})
}
inboundClientIps[email][ip] = struct{}{}
} }
j.checkError(scanner.Err())
file.Close()
shouldCleanLog := false shouldCleanLog := false
for email, uniqueIps := range inboundClientIps {
for clientEmail, ips := range InboundClientIps { ips := make([]string, 0, len(uniqueIps))
inboundClientIps, err := j.getInboundClientIps(clientEmail) for ip := range uniqueIps {
sort.Strings(ips) ips = append(ips, ip)
if err != nil {
j.addInboundClientIps(clientEmail, ips)
} else {
shouldCleanLog = j.updateInboundClientIps(inboundClientIps, clientEmail, ips)
} }
sort.Strings(ips)
inboundClientIps, err := j.getInboundClientIps(email)
if err != nil {
j.addInboundClientIps(email, ips)
continue
}
shouldCleanLog = j.updateInboundClientIps(inboundClientIps, email, ips) || shouldCleanLog
} }
return shouldCleanLog return shouldCleanLog
} }
func (j *CheckClientIpJob) checkFail2BanInstalled(iplimitActive bool) bool { func (j *CheckClientIpJob) checkFail2BanInstalled() bool {
cmd := "fail2ban-client" cmd := "fail2ban-client"
args := []string{"-h"} args := []string{"-h"}
err := exec.Command(cmd, args...).Run() err := exec.Command(cmd, args...).Run()
return err == nil
if iplimitActive && err != nil {
logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.")
return false
}
return true
} }
func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool { func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool {
@@ -253,7 +248,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
inboundClientIps.ClientEmail = clientEmail inboundClientIps.ClientEmail = clientEmail
inboundClientIps.Ips = string(jsonIps) inboundClientIps.Ips = string(jsonIps)
// Fetch inbound settings by client email
inbound, err := j.getInboundByEmail(clientEmail) inbound, err := j.getInboundByEmail(clientEmail)
if err != nil { if err != nil {
logger.Errorf("failed to fetch inbound settings for email %s: %s", clientEmail, err) logger.Errorf("failed to fetch inbound settings for email %s: %s", clientEmail, err)
@@ -265,14 +259,12 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
return false return false
} }
// Unmarshal settings to get client limits
settings := map[string][]model.Client{} settings := map[string][]model.Client{}
json.Unmarshal([]byte(inbound.Settings), &settings) json.Unmarshal([]byte(inbound.Settings), &settings)
clients := settings["clients"] clients := settings["clients"]
shouldCleanLog := false shouldCleanLog := false
j.disAllowedIps = []string{} j.disAllowedIps = []string{}
// Open log file for IP limits
logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil { if err != nil {
logger.Errorf("failed to open IP limit log file: %s", err) logger.Errorf("failed to open IP limit log file: %s", err)
@@ -282,7 +274,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
log.SetOutput(logIpFile) log.SetOutput(logIpFile)
log.SetFlags(log.LstdFlags) log.SetFlags(log.LstdFlags)
// Check client IP limits
for _, client := range clients { for _, client := range clients {
if client.Email == clientEmail { if client.Email == clientEmail {
limitIp := client.LimitIP limitIp := client.LimitIP

View File

@@ -248,28 +248,46 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
} }
func (s *ServerService) GetXrayVersions() ([]string, error) { func (s *ServerService) GetXrayVersions() ([]string, error) {
url := "https://api.github.com/repos/XTLS/Xray-core/releases" const (
resp, err := http.Get(url) XrayURL = "https://api.github.com/repos/XTLS/Xray-core/releases"
bufferSize = 8192
)
resp, err := http.Get(XrayURL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
buffer := bytes.NewBuffer(make([]byte, 8192))
buffer := bytes.NewBuffer(make([]byte, bufferSize))
buffer.Reset() buffer.Reset()
_, err = buffer.ReadFrom(resp.Body) if _, err := buffer.ReadFrom(resp.Body); err != nil {
if err != nil {
return nil, err return nil, err
} }
releases := make([]Release, 0) var releases []Release
err = json.Unmarshal(buffer.Bytes(), &releases) if err := json.Unmarshal(buffer.Bytes(), &releases); err != nil {
if err != nil {
return nil, err return nil, err
} }
var versions []string var versions []string
for _, release := range releases { for _, release := range releases {
if release.TagName >= "v1.7.5" { tagVersion := strings.TrimPrefix(release.TagName, "v")
tagParts := strings.Split(tagVersion, ".")
if len(tagParts) != 3 {
continue
}
major, err1 := strconv.Atoi(tagParts[0])
minor, err2 := strconv.Atoi(tagParts[1])
patch, err3 := strconv.Atoi(tagParts[2])
if err1 != nil || err2 != nil || err3 != nil {
continue
}
if (major == 1 && minor == 8 && patch == 24) ||
(major == 24 && ((minor > 10) || (minor == 10 && patch >= 16))) ||
(major > 24) {
versions = append(versions, release.TagName) versions = append(versions, release.TagName)
} }
} }

View File

@@ -243,6 +243,10 @@ func (s *SettingService) GetListen() (string, error) {
return s.getString("webListen") return s.getString("webListen")
} }
func (s *SettingService) SetListen(ip string) error {
return s.setString("webListen", ip)
}
func (s *SettingService) GetWebDomain() (string, error) { func (s *SettingService) GetWebDomain() (string, error) {
return s.getString("webDomain") return s.getString("webDomain")
} }

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "The IPs history log. (to enable inbound after disabling, clear the log)" "IPLimitlogDesc" = "The IPs history log. (to enable inbound after disabling, clear the log)"
"IPLimitlogclear" = "Clear The Log" "IPLimitlogclear" = "Clear The Log"
"setDefaultCert" = "Set Cert from Panel" "setDefaultCert" = "Set Cert from Panel"
"xtlsDesc" = "Xray must be v1.7.5"
"realityDesc" = "Xray must be v1.8.0+"
"telegramDesc" = "Please provide Telegram Chat ID. (use '/id' command in the bot) or (@userinfobot)" "telegramDesc" = "Please provide Telegram Chat ID. (use '/id' command in the bot) or (@userinfobot)"
"subscriptionDesc" = "To find your subscription URL, navigate to the 'Details'. Additionally, you can use the same name for several clients." "subscriptionDesc" = "To find your subscription URL, navigate to the 'Details'. Additionally, you can use the same name for several clients."
"info" = "Info" "info" = "Info"

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "Registro de historial de IPs (antes de habilitar la entrada después de que haya sido desactivada por el límite de IP, debes borrar el registro)." "IPLimitlogDesc" = "Registro de historial de IPs (antes de habilitar la entrada después de que haya sido desactivada por el límite de IP, debes borrar el registro)."
"IPLimitlogclear" = "Limpiar el Registro" "IPLimitlogclear" = "Limpiar el Registro"
"setDefaultCert" = "Establecer certificado desde el panel" "setDefaultCert" = "Establecer certificado desde el panel"
"xtlsDesc" = "La versión del núcleo de Xray debe ser 1.7.5"
"realityDesc" = "La versión del núcleo de Xray debe ser 1.8.0 o superior."
"telegramDesc" = "Por favor, proporciona el ID de Chat de Telegram. (usa el comando '/id' en el bot) o (@userinfobot)" "telegramDesc" = "Por favor, proporciona el ID de Chat de Telegram. (usa el comando '/id' en el bot) o (@userinfobot)"
"subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones." "subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones."
"info" = "Info" "info" = "Info"

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "گزارش تاریخچه آی‌پی. برای فعال کردن ورودی پس از غیرفعال شدن، گزارش را پاک کنید" "IPLimitlogDesc" = "گزارش تاریخچه آی‌پی. برای فعال کردن ورودی پس از غیرفعال شدن، گزارش را پاک کنید"
"IPLimitlogclear" = "پاک کردن گزارش‌ها" "IPLimitlogclear" = "پاک کردن گزارش‌ها"
"setDefaultCert" = "استفاده از گواهی پنل" "setDefaultCert" = "استفاده از گواهی پنل"
"xtlsDesc" = "ایکس‌ری باید 1.7.5 باشد"
"realityDesc" = "ایکس‌ری باید +1.8.0 باشد"
"telegramDesc" = "لطفا شناسه گفتگوی تلگرام را وارد کنید. (از دستور '/id' در ربات استفاده کنید) یا (@userinfobot)" "telegramDesc" = "لطفا شناسه گفتگوی تلگرام را وارد کنید. (از دستور '/id' در ربات استفاده کنید) یا (@userinfobot)"
"subscriptionDesc" = "شما می‌توانید لینک سابسکربپشن خودرا در 'جزئیات' پیدا کنید، همچنین می‌توانید از همین نام برای چندین کاربر استفاده‌کنید" "subscriptionDesc" = "شما می‌توانید لینک سابسکربپشن خودرا در 'جزئیات' پیدا کنید، همچنین می‌توانید از همین نام برای چندین کاربر استفاده‌کنید"
"info" = "اطلاعات" "info" = "اطلاعات"

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "Log histori IP. (untuk mengaktifkan masuk setelah menonaktifkan, hapus log)" "IPLimitlogDesc" = "Log histori IP. (untuk mengaktifkan masuk setelah menonaktifkan, hapus log)"
"IPLimitlogclear" = "Hapus Log" "IPLimitlogclear" = "Hapus Log"
"setDefaultCert" = "Atur Sertifikat dari Panel" "setDefaultCert" = "Atur Sertifikat dari Panel"
"xtlsDesc" = "Xray harus versi 1.7.5"
"realityDesc" = "Xray harus versi 1.8.0+"
"telegramDesc" = "Harap berikan ID Obrolan Telegram. (gunakan perintah '/id' di bot) atau (@userinfobot)" "telegramDesc" = "Harap berikan ID Obrolan Telegram. (gunakan perintah '/id' di bot) atau (@userinfobot)"
"subscriptionDesc" = "Untuk menemukan URL langganan Anda, buka 'Rincian'. Selain itu, Anda dapat menggunakan nama yang sama untuk beberapa klien." "subscriptionDesc" = "Untuk menemukan URL langganan Anda, buka 'Rincian'. Selain itu, Anda dapat menggunakan nama yang sama untuk beberapa klien."
"info" = "Info" "info" = "Info"

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "O histórico de IPs. (para ativar o inbound após a desativação, limpe o log)" "IPLimitlogDesc" = "O histórico de IPs. (para ativar o inbound após a desativação, limpe o log)"
"IPLimitlogclear" = "Limpar o Log" "IPLimitlogclear" = "Limpar o Log"
"setDefaultCert" = "Definir Certificado pelo Painel" "setDefaultCert" = "Definir Certificado pelo Painel"
"xtlsDesc" = "O Xray deve ser v1.7.5"
"realityDesc" = "O Xray deve ser v1.8.0+"
"telegramDesc" = "Por favor, forneça o ID do Chat do Telegram. (use o comando '/id' no bot) ou (@userinfobot)" "telegramDesc" = "Por favor, forneça o ID do Chat do Telegram. (use o comando '/id' no bot) ou (@userinfobot)"
"subscriptionDesc" = "Para encontrar seu URL de assinatura, navegue até 'Detalhes'. Além disso, você pode usar o mesmo nome para vários clientes." "subscriptionDesc" = "Para encontrar seu URL de assinatura, navegue até 'Detalhes'. Além disso, você pode usar o mesmo nome para vários clientes."
"info" = "Informações" "info" = "Informações"

View File

@@ -146,7 +146,7 @@
"meansNoLimit" = "= Без ограничений (значение: ГБ)" "meansNoLimit" = "= Без ограничений (значение: ГБ)"
"totalFlow" = "Общий расход" "totalFlow" = "Общий расход"
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало" "leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
"noRecommendKeepDefault" = "Рекомендуется оставить настройки по умолчанию" "noRecommendKeepDefault" = "Не рекомендуется оставлять настройки по умолчанию"
"certificatePath" = "Путь к файлу" "certificatePath" = "Путь к файлу"
"certificateContent" = "Содержимое файла" "certificateContent" = "Содержимое файла"
"publicKey" = "Публичный ключ" "publicKey" = "Публичный ключ"
@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "Лог IP-адресов (перед включением лога IP-адресов, вы должны очистить список)" "IPLimitlogDesc" = "Лог IP-адресов (перед включением лога IP-адресов, вы должны очистить список)"
"IPLimitlogclear" = "Очистить лог" "IPLimitlogclear" = "Очистить лог"
"setDefaultCert" = "Установить сертификат с панели" "setDefaultCert" = "Установить сертификат с панели"
"xtlsDesc" = "Версия Xray должна быть не ниже 1.7.5"
"realityDesc" = "Версия Xray должна быть не ниже 1.8.0"
"telegramDesc" = "Пожалуйста, укажите ID чата Telegram. (используйте команду '/id' в боте) или (@userinfobot)" "telegramDesc" = "Пожалуйста, укажите ID чата Telegram. (используйте команду '/id' в боте) или (@userinfobot)"
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций" "subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
"info" = "Информация" "info" = "Информация"
@@ -187,7 +185,7 @@
"inboundData" = "Входящие данные" "inboundData" = "Входящие данные"
"exportInbound" = "Экспорт входящих" "exportInbound" = "Экспорт входящих"
"import" = "Импортировать" "import" = "Импортировать"
"importInbound" = "Импортировать входящее сообщение" "importInbound" = "Импортировать подключение"
[pages.client] [pages.client]
"add" = "Добавить пользователя" "add" = "Добавить пользователя"
@@ -214,7 +212,7 @@
"request" = "Запрос" "request" = "Запрос"
"response" = "Ответ" "response" = "Ответ"
"name" = "Имя" "name" = "Имя"
"value" = "Ценить" "value" = "Значение"
[pages.inbounds.stream.tcp] [pages.inbounds.stream.tcp]
"version" = "Версия" "version" = "Версия"
@@ -230,7 +228,7 @@
"save" = "Сохранить" "save" = "Сохранить"
"infoDesc" = "Каждое выполненное изменение необходимо сохранить. Пожалуйста, перезапустите панель, чтобы изменения вступили в силу" "infoDesc" = "Каждое выполненное изменение необходимо сохранить. Пожалуйста, перезапустите панель, чтобы изменения вступили в силу"
"restartPanel" = "Перезапуск панели" "restartPanel" = "Перезапуск панели"
"restartPanelDesc" = "Вы уверены, что хотите перезапустить панель?Нажмите ОК для перезапуска панели через 3 сек. Если вы не можете пользоваться панелью после перезапуска, пожалуйста, посмотрите лог панели на сервере" "restartPanelDesc" = "Вы уверены, что хотите перезапустить панель? Нажмите ОК для перезапуска панели через 3 сек. Если вы не можете пользоваться панелью после перезапуска, пожалуйста, посмотрите лог панели на сервере"
"actions" = "Действия" "actions" = "Действия"
"resetDefaultConfig" = "Сбросить на конфигурацию по умолчанию" "resetDefaultConfig" = "Сбросить на конфигурацию по умолчанию"
"panelSettings" = "Настройки панели" "panelSettings" = "Настройки панели"
@@ -402,7 +400,7 @@
"portal" = "Портал" "portal" = "Портал"
"intercon" = "Соединение" "intercon" = "Соединение"
"settings" = "Настройки" "settings" = "Настройки"
"accountInfo" = "Информация Об учетной записи" "accountInfo" = "Информация об учетной записи"
"outboundStatus" = "Исходящий статус" "outboundStatus" = "Исходящий статус"
"sendThrough" = "Отправить через" "sendThrough" = "Отправить через"

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "IP geçmiş günlüğü. (devre dışı bırakıldıktan sonra gelini etkinleştirmek için günlüğü temizleyin)" "IPLimitlogDesc" = "IP geçmiş günlüğü. (devre dışı bırakıldıktan sonra gelini etkinleştirmek için günlüğü temizleyin)"
"IPLimitlogclear" = "Günlüğü Temizle" "IPLimitlogclear" = "Günlüğü Temizle"
"setDefaultCert" = "Panelden Sertifikayı Ayarla" "setDefaultCert" = "Panelden Sertifikayı Ayarla"
"xtlsDesc" = "Xray v1.7.5 olmalıdır"
"realityDesc" = "Xray v1.8.0+ olmalıdır"
"telegramDesc" = "Lütfen Telegram Sohbet Kimliği sağlayın. (botta '/id' komutunu kullanın) veya (@userinfobot)" "telegramDesc" = "Lütfen Telegram Sohbet Kimliği sağlayın. (botta '/id' komutunu kullanın) veya (@userinfobot)"
"subscriptionDesc" = "Abonelik URL'inizi bulmak için 'Detaylar'a gidin. Ayrıca, aynı adı birden fazla müşteri için kullanabilirsiniz." "subscriptionDesc" = "Abonelik URL'inizi bulmak için 'Detaylar'a gidin. Ayrıca, aynı adı birden fazla müşteri için kullanabilirsiniz."
"info" = "Bilgi" "info" = "Bilgi"

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "Журнал історії IP-адрес. (щоб увімкнути вхідну після вимкнення, очистіть журнал)" "IPLimitlogDesc" = "Журнал історії IP-адрес. (щоб увімкнути вхідну після вимкнення, очистіть журнал)"
"IPLimitlogclear" = "Очистити журнал" "IPLimitlogclear" = "Очистити журнал"
"setDefaultCert" = "Установити сертифікат з панелі" "setDefaultCert" = "Установити сертифікат з панелі"
"xtlsDesc" = "Xray має бути v1.7.5"
"realityDesc" = "Xray має бути v1.8.0+"
"telegramDesc" = "Будь ласка, вкажіть ID чату Telegram. (використовуйте команду '/id' у боті) або (@userinfobot)" "telegramDesc" = "Будь ласка, вкажіть ID чату Telegram. (використовуйте команду '/id' у боті) або (@userinfobot)"
"subscriptionDesc" = "Щоб знайти URL-адресу вашої підписки, перейдіть до «Деталі». Крім того, ви можете використовувати одне ім'я для кількох клієнтів." "subscriptionDesc" = "Щоб знайти URL-адресу вашої підписки, перейдіть до «Деталі». Крім того, ви можете використовувати одне ім'я для кількох клієнтів."
"info" = "Інформація" "info" = "Інформація"

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "Lịch sử đăng nhập IP (trước khi kích hoạt điểm vào sau khi bị vô hiệu hóa bởi giới hạn IP, bạn nên xóa lịch sử)." "IPLimitlogDesc" = "Lịch sử đăng nhập IP (trước khi kích hoạt điểm vào sau khi bị vô hiệu hóa bởi giới hạn IP, bạn nên xóa lịch sử)."
"IPLimitlogclear" = "Xóa Lịch sử" "IPLimitlogclear" = "Xóa Lịch sử"
"setDefaultCert" = "Đặt chứng chỉ từ bảng điều khiển" "setDefaultCert" = "Đặt chứng chỉ từ bảng điều khiển"
"xtlsDesc" = "Xray core cần phiên bản 1.7.5"
"realityDesc" = "Xray core cần phiên bản 1.8.0 hoặc cao hơn."
"telegramDesc" = "Vui lòng cung cấp ID Trò chuyện Telegram. (sử dụng lệnh '/id' trong bot) hoặc (@userinfobot)" "telegramDesc" = "Vui lòng cung cấp ID Trò chuyện Telegram. (sử dụng lệnh '/id' trong bot) hoặc (@userinfobot)"
"subscriptionDesc" = "Bạn có thể tìm liên kết gói đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau" "subscriptionDesc" = "Bạn có thể tìm liên kết gói đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau"
"info" = "Thông tin" "info" = "Thông tin"

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "IP 历史日志(要启用被禁用的入站流量,请清除日志)" "IPLimitlogDesc" = "IP 历史日志(要启用被禁用的入站流量,请清除日志)"
"IPLimitlogclear" = "清除日志" "IPLimitlogclear" = "清除日志"
"setDefaultCert" = "从面板设置证书" "setDefaultCert" = "从面板设置证书"
"xtlsDesc" = "Xray 核心需要 1.7.5"
"realityDesc" = "Xray 核心需要 1.8.0 及以上版本"
"telegramDesc" = "请提供Telegram聊天ID。在机器人中使用'/id'命令)或(@userinfobot" "telegramDesc" = "请提供Telegram聊天ID。在机器人中使用'/id'命令)或(@userinfobot"
"subscriptionDesc" = "要找到你的订阅 URL请导航到“详细信息”。此外你可以为多个客户端使用相同的名称。" "subscriptionDesc" = "要找到你的订阅 URL请导航到“详细信息”。此外你可以为多个客户端使用相同的名称。"
"info" = "信息" "info" = "信息"

View File

@@ -178,8 +178,6 @@
"IPLimitlogDesc" = "IP 歷史日誌(要啟用被禁用的入站流量,請清除日誌)" "IPLimitlogDesc" = "IP 歷史日誌(要啟用被禁用的入站流量,請清除日誌)"
"IPLimitlogclear" = "清除日誌" "IPLimitlogclear" = "清除日誌"
"setDefaultCert" = "從面板設定證書" "setDefaultCert" = "從面板設定證書"
"xtlsDesc" = "Xray 核心需要 1.7.5"
"realityDesc" = "Xray 核心需要 1.8.0 及以上版本"
"telegramDesc" = "請提供Telegram聊天ID。在機器人中使用'/id'命令)或(@userinfobot" "telegramDesc" = "請提供Telegram聊天ID。在機器人中使用'/id'命令)或(@userinfobot"
"subscriptionDesc" = "要找到你的訂閱 URL請導航到“詳細資訊”。此外你可以為多個客戶端使用相同的名稱。" "subscriptionDesc" = "要找到你的訂閱 URL請導航到“詳細資訊”。此外你可以為多個客戶端使用相同的名稱。"
"info" = "資訊" "info" = "資訊"

157
x-ui.sh
View File

@@ -46,6 +46,8 @@ elif [[ "${release}" == "manjaro" ]]; then
echo "Your OS is Manjaro" echo "Your OS is Manjaro"
elif [[ "${release}" == "armbian" ]]; then elif [[ "${release}" == "armbian" ]]; then
echo "Your OS is Armbian" echo "Your OS is Armbian"
elif [[ "${release}" == "alpine" ]]; then
echo "Your OS is Alpine Linux"
elif [[ "${release}" == "opensuse-tumbleweed" ]]; then elif [[ "${release}" == "opensuse-tumbleweed" ]]; then
echo "Your OS is OpenSUSE Tumbleweed" echo "Your OS is OpenSUSE Tumbleweed"
elif [[ "${release}" == "openEuler" ]]; then elif [[ "${release}" == "openEuler" ]]; then
@@ -190,7 +192,7 @@ update_menu() {
fi fi
} }
custom_version() { legacy_version() {
echo "Enter the panel version (like 2.4.0):" echo "Enter the panel version (like 2.4.0):"
read tag_version read tag_version
@@ -198,17 +200,8 @@ custom_version() {
echo "Panel version cannot be empty. Exiting." echo "Panel version cannot be empty. Exiting."
exit 1 exit 1
fi fi
min_version="2.3.5"
if [[ "$(printf '%s\n' "$tag_version" "$min_version" | sort -V | head -n1)" == "$tag_version" && "$tag_version" != "$min_version" ]]; then
echo "Please use a newer version (at least 2.3.5). Exiting."
exit 1
fi
download_link="https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh"
# Use the entered panel version in the download link # Use the entered panel version in the download link
install_command="bash <(curl -Ls $download_link) v$tag_version" install_command="bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/v$tag_version/install.sh") v$tag_version"
echo "Downloading and installing panel version $tag_version..." echo "Downloading and installing panel version $tag_version..."
eval $install_command eval $install_command
@@ -306,12 +299,19 @@ reset_config() {
} }
check_config() { check_config() {
info=$(/usr/local/x-ui/x-ui setting -show true) local info=$(/usr/local/x-ui/x-ui setting -show true)
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
LOGE "get current settings error, please check logs" LOGE "get current settings error, please check logs"
show_menu show_menu
return
fi fi
LOGI "${info}" LOGI "${info}"
local existing_webBasePath=$(echo "$info" | grep -Eo 'webBasePath: .+' | awk '{print $2}')
local existing_port=$(echo "$info" | grep -Eo 'port: .+' | awk '{print $2}')
local server_ip=$(curl -s https://api.ipify.org)
echo -e "${green}Access URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
} }
set_port() { set_port() {
@@ -1168,6 +1168,7 @@ run_speedtest() {
# Run Speedtest # Run Speedtest
speedtest speedtest
} }
create_iplimit_jails() { create_iplimit_jails() {
# Use default bantime if not passed => 15 minutes # Use default bantime if not passed => 15 minutes
local bantime="${1:-15}" local bantime="${1:-15}"
@@ -1175,7 +1176,7 @@ create_iplimit_jails() {
# Uncomment 'allowipv6 = auto' in fail2ban.conf # Uncomment 'allowipv6 = auto' in fail2ban.conf
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
#On Debian 12+ fail2ban's default backend should be changed to systemd # On Debian 12+ fail2ban's default backend should be changed to systemd
if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then
sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf
fi fi
@@ -1185,7 +1186,7 @@ create_iplimit_jails() {
enabled=true enabled=true
backend=auto backend=auto
filter=3x-ipl filter=3x-ipl
action=3x-ipl action = %(known/action)s[name=%(__name__)s, protocol="%(protocol)s", chain="%(chain)s"]
logpath=${iplimit_log_path} logpath=${iplimit_log_path}
maxretry=2 maxretry=2
findtime=32 findtime=32
@@ -1201,7 +1202,7 @@ EOF
cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
[INCLUDES] [INCLUDES]
before = iptables-allports.conf before = iptables-common.conf
[Definition] [Definition]
actionstart = <iptables> -N f2b-<name> actionstart = <iptables> -N f2b-<name>
@@ -1221,6 +1222,11 @@ 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} echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = <F-USER> [IP] = <ip> unbanned." >> ${iplimit_banned_log_path}
[Init] [Init]
# Use default settings from iptables-common.conf
# This will automatically handle both IPv4 and IPv6
name = default
protocol = tcp
chain = INPUT
EOF EOF
echo -e "${green}Ip Limit jail files created with a bantime of ${bantime} minutes.${plain}" echo -e "${green}Ip Limit jail files created with a bantime of ${bantime} minutes.${plain}"
@@ -1245,10 +1251,11 @@ 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"
echo -e "${green}\t3.${plain} Unban Everyone" echo -e "${green}\t3.${plain} Unban Everyone"
echo -e "${green}\t4.${plain} Check Logs" echo -e "${green}\t4.${plain} Ban Logs"
echo -e "${green}\t5.${plain} Fail2ban Status" echo -e "${green}\t5.${plain} Real-Time Logs"
echo -e "${green}\t6.${plain} Restart Fail2ban" echo -e "${green}\t6.${plain} Service Status"
echo -e "${green}\t7.${plain} Uninstall Fail2ban" echo -e "${green}\t7.${plain} Service Restart"
echo -e "${green}\t8.${plain} Uninstall Fail2ban and IP Limit"
echo -e "${green}\t0.${plain} Back to Main Menu" echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice read -p "Choose an option: " choice
case "$choice" in case "$choice" in
@@ -1289,12 +1296,15 @@ iplimit_main() {
show_banlog show_banlog
;; ;;
5) 5)
service fail2ban status tail -f /var/log/fail2ban.log
;; ;;
6) 6)
systemctl restart fail2ban service fail2ban status
;; ;;
7) 7)
systemctl restart fail2ban
;;
8)
remove_iplimit remove_iplimit
;; ;;
*) echo "Invalid choice" ;; *) echo "Invalid choice" ;;
@@ -1428,6 +1438,85 @@ remove_iplimit() {
esac esac
} }
SSH_port_forwarding() {
local server_ip=$(curl -s https://api.ipify.org)
local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
local existing_listenIP=$(/usr/local/x-ui/x-ui setting -getListen true | grep -Eo 'listenIP: .+' | awk '{print $2}')
local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'cert: .+' | awk '{print $2}')
local existing_key=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'key: .+' | awk '{print $2}')
local config_listenIP=""
local listen_choice=""
if [[ -n "$existing_cert" && -n "$existing_key" ]]; then
echo -e "${green}Panel is secure with SSL.${plain}"
return 0
fi
if [[ -z "$existing_cert" && -z "$existing_key" && -z "$existing_listenIP" ]]; then
echo -e "\n${red}Warning: No Cert and Key found! The panel is not secure.${plain}"
echo "Please obtain a certificate or set up SSH port forwarding."
fi
if [[ -n "$existing_listenIP" && (-z "$existing_cert" && -z "$existing_key") ]]; then
echo -e "\n${green}Current SSH Port Forwarding Configuration:${plain}"
echo -e "Standard SSH command:"
echo -e "${yellow}ssh -L 2222:${existing_listenIP}:${existing_port} root@${server_ip}${plain}"
echo -e "\nIf using SSH key:"
echo -e "${yellow}ssh -i <sshkeypath> -L 2222:${existing_listenIP}:${existing_port} root@${server_ip}${plain}"
echo -e "\nAfter connecting, access the panel at:"
echo -e "${yellow}http://localhost:2222${existing_webBasePath}${plain}"
fi
echo -e "\nChoose an option:"
echo -e "${green}1.${plain} Set listen IP"
echo -e "${green}2.${plain} Clear listen IP"
echo -e "${green}0.${plain} Abort"
read -p "Choose an option: " num
case "$num" in
1)
if [[ -z "$existing_listenIP" ]]; then
echo -e "\nNo listenIP configured. Choose an option:"
echo -e "1. Use default IP (127.0.0.1)"
echo -e "2. Set a custom IP"
read -p "Select an option (1 or 2): " listen_choice
config_listenIP="127.0.0.1"
[[ "$listen_choice" == "2" ]] && read -p "Enter custom IP to listen on: " config_listenIP
/usr/local/x-ui/x-ui setting -listenIP "${config_listenIP}" >/dev/null 2>&1
echo -e "${green}listen IP has been set to ${config_listenIP}.${plain}"
restart
else
config_listenIP="${existing_listenIP}"
echo -e "${green}Current listen IP is already set to ${config_listenIP}.${plain}"
fi
if [[ -n "${config_listenIP}" ]]; then
echo -e "\n${green}SSH Port Forwarding Configuration:${plain}"
echo -e "Standard SSH command:"
echo -e "${yellow}ssh -L 2222:${config_listenIP}:${existing_port} root@${server_ip}${plain}"
echo -e "\nIf using SSH key:"
echo -e "${yellow}ssh -i <sshkeypath> -L 2222:${config_listenIP}:${existing_port} root@${server_ip}${plain}"
echo -e "\nAfter connecting, access the panel at:"
echo -e "${yellow}http://localhost:2222${existing_webBasePath}${plain}"
fi
;;
2)
/usr/local/x-ui/x-ui setting -listenIP ' ' >/dev/null 2>&1
echo -e "${green}Listen IP has been cleared.${plain}"
restart
;;
0)
echo "Operation aborted."
;;
*)
echo "Invalid option. Exiting."
;;
esac
}
show_usage() { show_usage() {
echo "x-ui control menu usages: " echo "x-ui control menu usages: "
echo "------------------------------------------" echo "------------------------------------------"
@@ -1457,7 +1546,7 @@ show_menu() {
${green}1.${plain} Install ${green}1.${plain} Install
${green}2.${plain} Update ${green}2.${plain} Update
${green}3.${plain} Update Menu ${green}3.${plain} Update Menu
${green}4.${plain} Custom Version ${green}4.${plain} Legacy Version
${green}5.${plain} Uninstall ${green}5.${plain} Uninstall
———————————————— ————————————————
${green}6.${plain} Reset Username & Password & Secret Token ${green}6.${plain} Reset Username & Password & Secret Token
@@ -1479,13 +1568,14 @@ show_menu() {
${green}19.${plain} Cloudflare SSL Certificate ${green}19.${plain} Cloudflare SSL Certificate
${green}20.${plain} IP Limit Management ${green}20.${plain} IP Limit Management
${green}21.${plain} Firewall Management ${green}21.${plain} Firewall Management
${green}22.${plain} SSH Port Forwarding Management
———————————————— ————————————————
${green}22.${plain} Enable BBR ${green}23.${plain} Enable BBR
${green}23.${plain} Update Geo Files ${green}24.${plain} Update Geo Files
${green}24.${plain} Speedtest by Ookla ${green}25.${plain} Speedtest by Ookla
" "
show_status show_status
echo && read -p "Please enter your selection [0-24]: " num echo && read -p "Please enter your selection [0-25]: " num
case "${num}" in case "${num}" in
0) 0)
@@ -1501,7 +1591,7 @@ show_menu() {
check_install && update_menu check_install && update_menu
;; ;;
4) 4)
check_install && custom_version check_install && legacy_version
;; ;;
5) 5)
check_install && uninstall check_install && uninstall
@@ -1555,16 +1645,19 @@ show_menu() {
firewall_menu firewall_menu
;; ;;
22) 22)
bbr_menu SSH_port_forwarding
;; ;;
23) 23)
update_geo bbr_menu
;; ;;
24) 24)
update_geo
;;
25)
run_speedtest run_speedtest
;; ;;
*) *)
LOGE "Please enter the correct number [0-24]" LOGE "Please enter the correct number [0-25]"
;; ;;
esac esac
} }
@@ -1601,8 +1694,8 @@ if [[ $# > 0 ]]; then
"update") "update")
check_install 0 && update 0 check_install 0 && update 0
;; ;;
"custom") "legacy")
check_install 0 && custom_version 0 check_install 0 && legacy_version 0
;; ;;
"install") "install")
check_uninstall 0 && install 0 check_uninstall 0 && install 0