mirror of
https://github.com/XrayR-project/XrayR.git
synced 2025-07-24 03:47:57 +00:00
Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cb1638ac21 | ||
![]() |
de0da25c21 | ||
![]() |
faec840c23 | ||
![]() |
a6a1baf70c | ||
![]() |
4013f71e4c | ||
![]() |
c8f0981b0e | ||
![]() |
5274edf657 | ||
![]() |
e6232c1852 | ||
![]() |
e0688fc609 | ||
![]() |
ffa444f2ab | ||
![]() |
81ba4ebb43 | ||
![]() |
f50c61c782 | ||
![]() |
9c356cd28c | ||
![]() |
310353f344 | ||
![]() |
808b5ecc3c | ||
![]() |
c4ef5bb843 | ||
![]() |
ddce3fa86d | ||
![]() |
209f5a17d6 | ||
![]() |
af3fae9cdb | ||
![]() |
3b96b352cb | ||
![]() |
398c3133d3 | ||
![]() |
a397af5d73 | ||
![]() |
b47954ea64 | ||
![]() |
9a2188cb0c | ||
![]() |
c7af43fc49 | ||
![]() |
87aa855154 | ||
![]() |
37eff6755c | ||
![]() |
5e346ddfee | ||
![]() |
838c667a87 | ||
![]() |
057f4156bf | ||
![]() |
1f59a7cd7a | ||
![]() |
2b5fa4feee | ||
![]() |
571191a190 | ||
![]() |
3d5891fef3 | ||
![]() |
70a0099f2c | ||
![]() |
af8f24b5b1 | ||
![]() |
79528d3e17 | ||
![]() |
2f0461ddda | ||
![]() |
023680fec7 | ||
![]() |
708adf1e43 | ||
![]() |
0bbec7ebeb | ||
![]() |
a9dfd5404f | ||
![]() |
39c1036c4a | ||
![]() |
7604e33b03 | ||
![]() |
a906006015 | ||
![]() |
cb5cc17a82 | ||
![]() |
1cf8bca79e | ||
![]() |
cbffafbb4c |
126
.github/build/friendly-filenames.json
vendored
126
.github/build/friendly-filenames.json
vendored
@@ -1,33 +1,95 @@
|
||||
{
|
||||
"android-arm64": { "friendlyName": "android-arm64-v8a" },
|
||||
"darwin-amd64": { "friendlyName": "macos-64" },
|
||||
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
|
||||
"dragonfly-amd64": { "friendlyName": "dragonfly-64" },
|
||||
"freebsd-386": { "friendlyName": "freebsd-32" },
|
||||
"freebsd-amd64": { "friendlyName": "freebsd-64" },
|
||||
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
|
||||
"freebsd-arm7": { "friendlyName": "freebsd-arm32-v7a" },
|
||||
"linux-386": { "friendlyName": "linux-32" },
|
||||
"linux-amd64": { "friendlyName": "linux-64" },
|
||||
"linux-arm5": { "friendlyName": "linux-arm32-v5" },
|
||||
"linux-arm64": { "friendlyName": "linux-arm64-v8a" },
|
||||
"linux-arm6": { "friendlyName": "linux-arm32-v6" },
|
||||
"linux-arm7": { "friendlyName": "linux-arm32-v7a" },
|
||||
"linux-mips64le": { "friendlyName": "linux-mips64le" },
|
||||
"linux-mips64": { "friendlyName": "linux-mips64" },
|
||||
"linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
|
||||
"linux-mipsle": { "friendlyName": "linux-mips32le" },
|
||||
"linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
|
||||
"linux-mips": { "friendlyName": "linux-mips32" },
|
||||
"linux-ppc64le": { "friendlyName": "linux-ppc64le" },
|
||||
"linux-ppc64": { "friendlyName": "linux-ppc64" },
|
||||
"linux-riscv64": { "friendlyName": "linux-riscv64" },
|
||||
"linux-s390x": { "friendlyName": "linux-s390x" },
|
||||
"openbsd-386": { "friendlyName": "openbsd-32" },
|
||||
"openbsd-amd64": { "friendlyName": "openbsd-64" },
|
||||
"openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
|
||||
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
|
||||
"windows-386": { "friendlyName": "windows-32" },
|
||||
"windows-amd64": { "friendlyName": "windows-64" },
|
||||
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
||||
}
|
||||
"android-arm64": {
|
||||
"friendlyName": "android-arm64-v8a"
|
||||
},
|
||||
"darwin-amd64": {
|
||||
"friendlyName": "macos-64"
|
||||
},
|
||||
"darwin-arm64": {
|
||||
"friendlyName": "macos-arm64-v8a"
|
||||
},
|
||||
"dragonfly-amd64": {
|
||||
"friendlyName": "dragonfly-64"
|
||||
},
|
||||
"freebsd-386": {
|
||||
"friendlyName": "freebsd-32"
|
||||
},
|
||||
"freebsd-amd64": {
|
||||
"friendlyName": "freebsd-64"
|
||||
},
|
||||
"freebsd-arm64": {
|
||||
"friendlyName": "freebsd-arm64-v8a"
|
||||
},
|
||||
"freebsd-arm7": {
|
||||
"friendlyName": "freebsd-arm32-v7a"
|
||||
},
|
||||
"linux-386": {
|
||||
"friendlyName": "linux-32"
|
||||
},
|
||||
"linux-amd64": {
|
||||
"friendlyName": "linux-64"
|
||||
},
|
||||
"linux-arm5": {
|
||||
"friendlyName": "linux-arm32-v5"
|
||||
},
|
||||
"linux-arm64": {
|
||||
"friendlyName": "linux-arm64-v8a"
|
||||
},
|
||||
"linux-arm6": {
|
||||
"friendlyName": "linux-arm32-v6"
|
||||
},
|
||||
"linux-arm7": {
|
||||
"friendlyName": "linux-arm32-v7a"
|
||||
},
|
||||
"linux-mips64le": {
|
||||
"friendlyName": "linux-mips64le"
|
||||
},
|
||||
"linux-mips64": {
|
||||
"friendlyName": "linux-mips64"
|
||||
},
|
||||
"linux-mipslesoftfloat": {
|
||||
"friendlyName": "linux-mips32le-softfloat"
|
||||
},
|
||||
"linux-mipsle": {
|
||||
"friendlyName": "linux-mips32le"
|
||||
},
|
||||
"linux-mipssoftfloat": {
|
||||
"friendlyName": "linux-mips32-softfloat"
|
||||
},
|
||||
"linux-mips": {
|
||||
"friendlyName": "linux-mips32"
|
||||
},
|
||||
"linux-ppc64le": {
|
||||
"friendlyName": "linux-ppc64le"
|
||||
},
|
||||
"linux-ppc64": {
|
||||
"friendlyName": "linux-ppc64"
|
||||
},
|
||||
"linux-riscv64": {
|
||||
"friendlyName": "linux-riscv64"
|
||||
},
|
||||
"linux-s390x": {
|
||||
"friendlyName": "linux-s390x"
|
||||
},
|
||||
"openbsd-386": {
|
||||
"friendlyName": "openbsd-32"
|
||||
},
|
||||
"openbsd-amd64": {
|
||||
"friendlyName": "openbsd-64"
|
||||
},
|
||||
"openbsd-arm64": {
|
||||
"friendlyName": "openbsd-arm64-v8a"
|
||||
},
|
||||
"openbsd-arm7": {
|
||||
"friendlyName": "openbsd-arm32-v7a"
|
||||
},
|
||||
"windows-386": {
|
||||
"friendlyName": "windows-32"
|
||||
},
|
||||
"windows-amd64": {
|
||||
"friendlyName": "windows-64"
|
||||
},
|
||||
"windows-arm7": {
|
||||
"friendlyName": "windows-arm32-v7a"
|
||||
}
|
||||
}
|
50
.github/workflows/codeql-analysis.yml
vendored
50
.github/workflows/codeql-analysis.yml
vendored
@@ -34,34 +34,34 @@ jobs:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
12
.github/workflows/docker.yml
vendored
12
.github/workflows/docker.yml
vendored
@@ -21,24 +21,20 @@ jobs:
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Log in to the Container registry
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Docker meta
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
-
|
||||
name: Build and push
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
|
18
.github/workflows/release.yml
vendored
18
.github/workflows/release.yml
vendored
@@ -11,14 +11,14 @@ on:
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
types: [ opened, synchronize, reopened ]
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
release:
|
||||
types: [published]
|
||||
types: [ published ]
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -26,8 +26,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
# Include amd64 on all platforms.
|
||||
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
|
||||
goarch: [amd64, 386]
|
||||
goos: [ windows, freebsd, openbsd, linux, dragonfly, darwin ]
|
||||
goarch: [ amd64, 386 ]
|
||||
exclude:
|
||||
# Exclude i386 on darwin and dragonfly.
|
||||
- goarch: 386
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
# END S390X
|
||||
# END Other architectures
|
||||
fail-fast: false
|
||||
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
@@ -102,18 +102,18 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v2
|
||||
- name: Show workflow information
|
||||
- name: Show workflow information
|
||||
id: get_filename
|
||||
run: |
|
||||
export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
|
||||
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
|
||||
echo "::set-output name=ASSET_NAME::$_NAME"
|
||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.18
|
||||
go-version: ^1.19
|
||||
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
@@ -123,7 +123,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p build_assets
|
||||
go build -v -o build_assets/XrayR -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
|
||||
|
||||
- name: Build Mips softfloat XrayR
|
||||
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
||||
run: |
|
||||
|
20
.github/workflows/sync.yml
vendored
20
.github/workflows/sync.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Sync to Gitlab
|
||||
|
||||
on:
|
||||
push:
|
||||
delete:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
to_gitlab:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: pixta-dev/repository-mirroring-action@v1
|
||||
with:
|
||||
target_repo_url:
|
||||
git@gitlab.com:xrayr-project/XrayR.git
|
||||
ssh_private_key:
|
||||
${{ secrets.SSH_PRIVATEKEY }}
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,4 +13,5 @@ main/cert
|
||||
main/config.yml
|
||||
./vscode
|
||||
.idea/*
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
*.bak
|
@@ -1,5 +1,5 @@
|
||||
# Build go
|
||||
FROM golang:1.18-alpine AS builder
|
||||
FROM golang:1.19-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
ENV CGO_ENABLED=0
|
||||
|
31
README.md
31
README.md
@@ -1,4 +1,5 @@
|
||||
# XrayR
|
||||
|
||||
[](https://t.me/XrayR_project)
|
||||
[](https://t.me/XrayR_channel)
|
||||

|
||||
@@ -14,11 +15,13 @@ A Xray backend framework that can easily support many panels.
|
||||
如果您喜欢本项目,可以右上角点个star+watch,持续关注本项目的进展。
|
||||
|
||||
使用教程:[详细使用教程](https://xrayr-project.github.io/XrayR-doc/)
|
||||
|
||||
## 免责声明
|
||||
|
||||
本项目只是本人个人学习开发并维护,本人不保证任何可用性,也不对使用本软件造成的任何后果负责。
|
||||
|
||||
## 特点
|
||||
|
||||
* 永久开源且免费。
|
||||
* 支持V2ray,Trojan, Shadowsocks多种协议。
|
||||
* 支持Vless和XTLS等新特性。
|
||||
@@ -31,39 +34,45 @@ A Xray backend framework that can easily support many panels.
|
||||
|
||||
## 功能介绍
|
||||
|
||||
| 功能 | v2ray | trojan | shadowsocks |
|
||||
| --------------- | ----- | ------ | ----------- |
|
||||
| 功能 | v2ray | trojan | shadowsocks |
|
||||
|-----------|-------|--------|-------------|
|
||||
| 获取节点信息 | √ | √ | √ |
|
||||
| 获取用户信息 | √ | √ | √ |
|
||||
| 用户流量统计 | √ | √ | √ |
|
||||
| 服务器信息上报 | √ | √ | √ |
|
||||
| 服务器信息上报 | √ | √ | √ |
|
||||
| 自动申请tls证书 | √ | √ | √ |
|
||||
| 自动续签tls证书 | √ | √ | √ |
|
||||
| 在线人数统计 | √ | √ | √ |
|
||||
| 在线用户限制 | √ | √ | √ |
|
||||
| 审计规则 | √ | √ | √ |
|
||||
| 审计规则 | √ | √ | √ |
|
||||
| 节点端口限速 | √ | √ | √ |
|
||||
| 按照用户限速 | √ | √ | √ |
|
||||
| 自定义DNS | √ | √ | √ |
|
||||
| 自定义DNS | √ | √ | √ |
|
||||
|
||||
## 支持前端
|
||||
|
||||
| 前端 | v2ray | trojan | shadowsocks |
|
||||
| ------------------------------------------------------ | ----- | ------ | ------------------------------ |
|
||||
| 前端 | v2ray | trojan | shadowsocks |
|
||||
|--------------------------------------------------------|-------|--------|-------------------------|
|
||||
| sspanel-uim | √ | √ | √ (单端口多用户和V2ray-Plugin) |
|
||||
| v2board | √ | √ | √ |
|
||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||
| v2board | √ | √ | √ |
|
||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||
|
||||
## 软件安装
|
||||
|
||||
### 一键安装
|
||||
|
||||
```
|
||||
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
|
||||
```
|
||||
|
||||
### 使用Docker部署软件
|
||||
|
||||
[Docker部署教程](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
|
||||
|
||||
### 手动安装
|
||||
|
||||
[手动安装教程](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
|
||||
|
||||
## 配置文件及详细使用教程
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// API config
|
||||
// Config API config
|
||||
type Config struct {
|
||||
APIHost string `mapstructure:"ApiHost"`
|
||||
NodeID int `mapstructure:"NodeID"`
|
||||
@@ -20,7 +20,7 @@ type Config struct {
|
||||
DisableCustomConfig bool `mapstructure:"DisableCustomConfig"`
|
||||
}
|
||||
|
||||
// Node status
|
||||
// NodeStatus Node status
|
||||
type NodeStatus struct {
|
||||
CPU float64
|
||||
Mem float64
|
||||
|
@@ -16,7 +16,7 @@ type NodeInfoResponse struct {
|
||||
Host string `json:"host"`
|
||||
Path string `json:"path"`
|
||||
Grpc bool `json:"grpc"`
|
||||
Sni string `json:sni`
|
||||
Sni string `json:"sni"`
|
||||
}
|
||||
|
||||
// UserResponse is the response of user
|
||||
|
@@ -11,8 +11,9 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
// APIClient create a api client to the panel.
|
||||
@@ -76,7 +77,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
|
||||
//handle errors while opening
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
log.Printf("Error when opening file: %s", err)
|
||||
return LocalRuleList
|
||||
@@ -94,7 +95,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// handle first encountered error while reading
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
log.Fatalf("Error while reading file: %s", err)
|
||||
return make([]api.DetectRule, 0)
|
||||
return
|
||||
}
|
||||
|
||||
file.Close()
|
||||
@@ -130,7 +131,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
||||
|
||||
if response.Ret != 200 {
|
||||
res, _ := json.Marshal(&response)
|
||||
return nil, fmt.Errorf("Ret %s invalid", string(res))
|
||||
return nil, fmt.Errorf("ret %s invalid", string(res))
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
@@ -167,7 +168,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
nodeInfoResponse := new(NodeInfoResponse)
|
||||
|
||||
if err := json.Unmarshal(response.Data, nodeInfoResponse); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
|
||||
}
|
||||
switch c.NodeType {
|
||||
case "V2ray":
|
||||
@@ -177,7 +178,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
case "Shadowsocks":
|
||||
nodeInfo, err = c.ParseSSNodeResponse(nodeInfoResponse)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -219,12 +220,12 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
|
||||
var userListResponse *[]UserResponse
|
||||
if err := json.Unmarshal(response.Data, &userListResponse); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||
}
|
||||
userList, err := c.ParseUserListResponse(userListResponse)
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(userListResponse)
|
||||
return nil, fmt.Errorf("Parse user list failed: %s", string(res))
|
||||
return nil, fmt.Errorf("parse user list failed: %s", string(res))
|
||||
}
|
||||
return userList, nil
|
||||
}
|
||||
@@ -234,7 +235,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
//ReportNodeOnlineUsers reports online user ip
|
||||
// ReportNodeOnlineUsers reports online user ip
|
||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||
var nodeType = ""
|
||||
switch c.NodeType {
|
||||
@@ -338,7 +339,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
ruleListResponse := new([]RuleItem)
|
||||
|
||||
if err := json.Unmarshal(response.Data, ruleListResponse); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
|
||||
}
|
||||
|
||||
for _, r := range *ruleListResponse {
|
||||
|
@@ -85,7 +85,7 @@ func TestGetUserList(t *testing.T) {
|
||||
func TestReportNodeStatus(t *testing.T) {
|
||||
client := CreateClient()
|
||||
nodeStatus := &api.NodeStatus{
|
||||
1, 1, 1, 256,
|
||||
CPU: 1, Mem: 1, Disk: 1, Uptime: 256,
|
||||
}
|
||||
err := client.ReportNodeStatus(nodeStatus)
|
||||
if err != nil {
|
||||
@@ -107,7 +107,7 @@ func TestReportReportNodeOnlineUsers(t *testing.T) {
|
||||
IP: fmt.Sprintf("1.1.1.%d", i),
|
||||
}
|
||||
}
|
||||
//client.Debug()
|
||||
// client.Debug()
|
||||
err = client.ReportNodeOnlineUsers(&onlineUserList)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -128,7 +128,7 @@ func TestReportReportUserTraffic(t *testing.T) {
|
||||
Download: 114514,
|
||||
}
|
||||
}
|
||||
//client.Debug()
|
||||
// client.Debug()
|
||||
err = client.ReportUserTraffic(&generalUserTraffic)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -150,8 +150,8 @@ func TestReportIllegal(t *testing.T) {
|
||||
client := CreateClient()
|
||||
|
||||
detectResult := []api.DetectResult{
|
||||
api.DetectResult{1, 2},
|
||||
api.DetectResult{1, 3},
|
||||
{1, 2},
|
||||
{1, 3},
|
||||
}
|
||||
client.Debug()
|
||||
err := client.ReportIllegal(&detectResult)
|
||||
|
@@ -49,7 +49,7 @@ type TrojanNodeInfo struct {
|
||||
TrojanPort uint32 `json:"trojan_port"`
|
||||
}
|
||||
|
||||
// Node status report
|
||||
// NodeStatus Node status report
|
||||
type NodeStatus struct {
|
||||
CPU string `json:"cpu"`
|
||||
Mem string `json:"mem"`
|
||||
|
@@ -11,8 +11,9 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
// APIClient create a api client to the panel.
|
||||
@@ -72,7 +73,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
|
||||
//handle errors while opening
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
log.Printf("Error when opening file: %s", err)
|
||||
return LocalRuleList
|
||||
@@ -90,7 +91,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// handle first encountered error while reading
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
log.Fatalf("Error while reading file: %s", err)
|
||||
return make([]api.DetectRule, 0)
|
||||
return
|
||||
}
|
||||
|
||||
file.Close()
|
||||
@@ -134,7 +135,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
||||
|
||||
if response.Status != "success" {
|
||||
res, _ := json.Marshal(&response)
|
||||
return nil, fmt.Errorf("Ret %s invalid", string(res))
|
||||
return nil, fmt.Errorf("ret %s invalid", string(res))
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
@@ -150,7 +151,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
case "Shadowsocks":
|
||||
path = fmt.Sprintf("/api/ss/v1/node/%d", c.NodeID)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
res, err := c.createCommonRequest().
|
||||
@@ -171,7 +172,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
case "Shadowsocks":
|
||||
nodeInfo, err = c.ParseSSNodeResponse(&response.Data)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -193,7 +194,7 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
case "Shadowsocks":
|
||||
path = fmt.Sprintf("/api/ss/v1/userList/%d", c.NodeID)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
res, err := c.createCommonRequest().
|
||||
@@ -214,11 +215,11 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
case "Shadowsocks":
|
||||
userList, err = c.ParseSSUserListResponse(&response.Data)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(response.Data)
|
||||
return nil, fmt.Errorf("Parse user list failed: %s", string(res))
|
||||
return nil, fmt.Errorf("parse user list failed: %s", string(res))
|
||||
}
|
||||
return userList, nil
|
||||
}
|
||||
@@ -234,7 +235,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
case "Shadowsocks":
|
||||
path = fmt.Sprintf("/api/ss/v1/nodeStatus/%d", c.NodeID)
|
||||
default:
|
||||
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
systemload := NodeStatus{
|
||||
@@ -258,7 +259,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
//ReportNodeOnlineUsers reports online user ip
|
||||
// ReportNodeOnlineUsers reports online user ip
|
||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||
|
||||
var path string
|
||||
@@ -270,7 +271,7 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
|
||||
case "Shadowsocks":
|
||||
path = fmt.Sprintf("/api/ss/v1/nodeOnline/%d", c.NodeID)
|
||||
default:
|
||||
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
data := make([]NodeOnline, len(*onlineUserList))
|
||||
@@ -303,7 +304,7 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||
case "Shadowsocks":
|
||||
path = fmt.Sprintf("/api/ss/v1/userTraffic/%d", c.NodeID)
|
||||
default:
|
||||
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
data := make([]UserTraffic, len(*userTraffic))
|
||||
@@ -338,7 +339,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
case "Shadowsocks":
|
||||
path = fmt.Sprintf("/api/ss/v1/nodeRule/%d", c.NodeID)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
res, err := c.createCommonRequest().
|
||||
@@ -354,7 +355,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
ruleListResponse := new(NodeRule)
|
||||
|
||||
if err := json.Unmarshal(response.Data, ruleListResponse); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
|
||||
}
|
||||
ruleList := c.LocalRuleList
|
||||
// Only support reject rule type
|
||||
@@ -386,7 +387,7 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||
case "Shadowsocks":
|
||||
path = fmt.Sprintf("/api/ss/v1/trigger/%d", c.NodeID)
|
||||
default:
|
||||
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
|
||||
for _, r := range *detectResultList {
|
||||
@@ -421,7 +422,7 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*
|
||||
|
||||
v2rayNodeInfo := new(V2rayNodeInfo)
|
||||
if err := json.Unmarshal(*nodeInfoResponse, v2rayNodeInfo); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||
}
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
@@ -458,7 +459,7 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *json.RawMessage) (*api
|
||||
var speedlimit uint64 = 0
|
||||
shadowsocksNodeInfo := new(ShadowsocksNodeInfo)
|
||||
if err := json.Unmarshal(*nodeInfoResponse, shadowsocksNodeInfo); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||
}
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
@@ -495,7 +496,7 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (
|
||||
|
||||
trojanNodeInfo := new(TrojanNodeInfo)
|
||||
if err := json.Unmarshal(*nodeInfoResponse, trojanNodeInfo); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||
}
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
@@ -527,7 +528,7 @@ func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage
|
||||
|
||||
vmessUserList := new([]*VMessUser)
|
||||
if err := json.Unmarshal(*userInfoResponse, vmessUserList); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
|
||||
}
|
||||
|
||||
userList := make([]api.UserInfo, len(*vmessUserList))
|
||||
@@ -555,7 +556,7 @@ func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessag
|
||||
|
||||
trojanUserList := new([]*TrojanUser)
|
||||
if err := json.Unmarshal(*userInfoResponse, trojanUserList); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
|
||||
}
|
||||
|
||||
userList := make([]api.UserInfo, len(*trojanUserList))
|
||||
@@ -563,7 +564,7 @@ func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessag
|
||||
if c.SpeedLimit > 0 {
|
||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||
} else {
|
||||
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||
speedlimit = (user.SpeedLimit * 1000000) / 8
|
||||
}
|
||||
userList[i] = api.UserInfo{
|
||||
UID: user.UID,
|
||||
@@ -583,7 +584,7 @@ func (c *APIClient) ParseSSUserListResponse(userInfoResponse *json.RawMessage) (
|
||||
|
||||
ssUserList := new([]*SSUser)
|
||||
if err := json.Unmarshal(*userInfoResponse, ssUserList); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
|
||||
}
|
||||
|
||||
userList := make([]api.UserInfo, len(*ssUserList))
|
||||
|
@@ -89,7 +89,7 @@ func TestGetUserList(t *testing.T) {
|
||||
func TestReportNodeStatus(t *testing.T) {
|
||||
client := CreateClient()
|
||||
nodeStatus := &api.NodeStatus{
|
||||
1, 1, 1, 256,
|
||||
CPU: 1, Mem: 1, Disk: 1, Uptime: 256,
|
||||
}
|
||||
err := client.ReportNodeStatus(nodeStatus)
|
||||
if err != nil {
|
||||
@@ -111,7 +111,7 @@ func TestReportReportNodeOnlineUsers(t *testing.T) {
|
||||
IP: fmt.Sprintf("1.1.1.%d", i),
|
||||
}
|
||||
}
|
||||
//client.Debug()
|
||||
// client.Debug()
|
||||
err = client.ReportNodeOnlineUsers(&onlineUserList)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -154,8 +154,8 @@ func TestReportIllegal(t *testing.T) {
|
||||
client := CreateClient()
|
||||
|
||||
detectResult := []api.DetectResult{
|
||||
api.DetectResult{1, 1},
|
||||
api.DetectResult{1, 2},
|
||||
{1, 1},
|
||||
{1, 2},
|
||||
}
|
||||
client.Debug()
|
||||
err := client.ReportIllegal(&detectResult)
|
||||
|
@@ -13,8 +13,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -89,7 +90,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
|
||||
//handle errors while opening
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
log.Printf("Error when opening file: %s", err)
|
||||
return LocalRuleList
|
||||
@@ -107,7 +108,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// handle first encountered error while reading
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
log.Fatalf("Error while reading file: %s", err)
|
||||
return make([]api.DetectRule, 0)
|
||||
return
|
||||
}
|
||||
|
||||
file.Close()
|
||||
@@ -143,7 +144,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
||||
|
||||
if response.Ret != 1 {
|
||||
res, _ := json.Marshal(&response)
|
||||
return nil, fmt.Errorf("Ret %s invalid", string(res))
|
||||
return nil, fmt.Errorf("ret %s invalid", string(res))
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
@@ -164,7 +165,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
nodeInfoResponse := new(NodeInfoResponse)
|
||||
|
||||
if err := json.Unmarshal(response.Data, nodeInfoResponse); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
|
||||
}
|
||||
|
||||
// New sspanel API
|
||||
@@ -196,7 +197,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||
case "Shadowsocks-Plugin":
|
||||
nodeInfo, err = c.ParseSSPluginNodeResponse(nodeInfoResponse)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,12 +226,12 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||
userListResponse := new([]UserResponse)
|
||||
|
||||
if err := json.Unmarshal(response.Data, userListResponse); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||
}
|
||||
userList, err := c.ParseUserListResponse(userListResponse)
|
||||
if err != nil {
|
||||
res, _ := json.Marshal(userListResponse)
|
||||
return nil, fmt.Errorf("Parse user list failed: %s", string(res))
|
||||
return nil, fmt.Errorf("parse user list failed: %s", string(res))
|
||||
}
|
||||
return userList, nil
|
||||
}
|
||||
@@ -257,7 +258,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
//ReportNodeOnlineUsers reports online user ip
|
||||
// ReportNodeOnlineUsers reports online user ip
|
||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
@@ -334,7 +335,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||
ruleListResponse := new([]RuleItem)
|
||||
|
||||
if err := json.Unmarshal(response.Data, ruleListResponse); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
|
||||
}
|
||||
|
||||
for _, r := range *ruleListResponse {
|
||||
@@ -378,9 +379,9 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
||||
var header json.RawMessage
|
||||
var speedlimit uint64 = 0
|
||||
if nodeInfoResponse.RawServerString == "" {
|
||||
return nil, fmt.Errorf("No server info in response")
|
||||
return nil, fmt.Errorf("no server info in response")
|
||||
}
|
||||
//nodeInfo.RawServerString = strings.ToLower(nodeInfo.RawServerString)
|
||||
// nodeInfo.RawServerString = strings.ToLower(nodeInfo.RawServerString)
|
||||
serverConf := strings.Split(nodeInfoResponse.RawServerString, ";")
|
||||
|
||||
parsedPort, err := strconv.ParseInt(serverConf[1], 10, 32)
|
||||
@@ -444,7 +445,7 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Marshal Header Type %s into config fialed: %s", header, err)
|
||||
return nil, fmt.Errorf("marshal Header Type %s into config fialed: %s", header, err)
|
||||
}
|
||||
|
||||
// Create GeneralNodeInfo
|
||||
@@ -487,7 +488,7 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*ap
|
||||
userListResponse := new([]UserResponse)
|
||||
|
||||
if err := json.Unmarshal(response.Data, userListResponse); err != nil {
|
||||
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||
}
|
||||
// Find the multi-user
|
||||
for _, u := range *userListResponse {
|
||||
@@ -498,7 +499,7 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*ap
|
||||
}
|
||||
}
|
||||
if port == 0 || method == "" {
|
||||
return nil, fmt.Errorf("Cant find the single port multi user")
|
||||
return nil, fmt.Errorf("cant find the single port multi user")
|
||||
}
|
||||
|
||||
if c.SpeedLimit > 0 {
|
||||
@@ -603,7 +604,7 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
|
||||
}
|
||||
|
||||
if nodeInfoResponse.RawServerString == "" {
|
||||
return nil, fmt.Errorf("No server info in response")
|
||||
return nil, fmt.Errorf("no server info in response")
|
||||
}
|
||||
if result := firstPortRe.FindStringSubmatch(nodeInfoResponse.RawServerString); len(result) > 1 {
|
||||
outsidePort = result[1]
|
||||
@@ -678,7 +679,7 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
|
||||
|
||||
var deviceLimit, localDeviceLimit int = 0, 0
|
||||
var speedlimit uint64 = 0
|
||||
userList := []api.UserInfo{}
|
||||
var userList []api.UserInfo
|
||||
for _, user := range *userInfoResponse {
|
||||
if c.DeviceLimit > 0 {
|
||||
deviceLimit = c.DeviceLimit
|
||||
|
@@ -83,7 +83,7 @@ func TestGetUserList(t *testing.T) {
|
||||
func TestReportNodeStatus(t *testing.T) {
|
||||
client := CreateClient()
|
||||
nodeStatus := &api.NodeStatus{
|
||||
1, 1, 1, 256,
|
||||
CPU: 1, Mem: 1, Disk: 1, Uptime: 256,
|
||||
}
|
||||
err := client.ReportNodeStatus(nodeStatus)
|
||||
if err != nil {
|
||||
@@ -105,7 +105,7 @@ func TestReportReportNodeOnlineUsers(t *testing.T) {
|
||||
IP: fmt.Sprintf("1.1.1.%d", i),
|
||||
}
|
||||
}
|
||||
//client.Debug()
|
||||
// client.Debug()
|
||||
err = client.ReportNodeOnlineUsers(&onlineUserList)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -126,7 +126,7 @@ func TestReportReportUserTraffic(t *testing.T) {
|
||||
Download: 114514,
|
||||
}
|
||||
}
|
||||
//client.Debug()
|
||||
// client.Debug()
|
||||
err = client.ReportUserTraffic(&generalUserTraffic)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -148,8 +148,8 @@ func TestReportIllegal(t *testing.T) {
|
||||
client := CreateClient()
|
||||
|
||||
detectResult := []api.DetectResult{
|
||||
api.DetectResult{1, 2},
|
||||
api.DetectResult{1, 3},
|
||||
{1, 2},
|
||||
{1, 3},
|
||||
}
|
||||
client.Debug()
|
||||
err := client.ReportIllegal(&detectResult)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package v2board
|
||||
|
||||
type UserTraffic struct {
|
||||
UID int `json:"user_id"`
|
||||
Upload int64 `json:"u"`
|
||||
Download int64 `json:"d"`
|
||||
UID int `json:"user_id"`
|
||||
Upload int64 `json:"u"`
|
||||
Download int64 `json:"d"`
|
||||
}
|
||||
|
@@ -12,9 +12,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
// APIClient create an api client to the panel.
|
||||
@@ -81,7 +82,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
|
||||
//handle errors while opening
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
log.Printf("Error when opening file: %s", err)
|
||||
return LocalRuleList
|
||||
@@ -99,7 +100,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// handle first encountered error while reading
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
log.Fatalf("Error while reading file: %s", err)
|
||||
return make([]api.DetectRule, 0)
|
||||
return
|
||||
}
|
||||
|
||||
file.Close()
|
||||
@@ -133,7 +134,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
||||
}
|
||||
rtn, err := simplejson.NewJson(res.Body())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Ret %s invalid", res.String())
|
||||
return nil, fmt.Errorf("ret %s invalid", res.String())
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
@@ -294,7 +295,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
//ReportNodeOnlineUsers implements the API interface
|
||||
// ReportNodeOnlineUsers implements the API interface
|
||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||
return nil
|
||||
}
|
||||
@@ -371,7 +372,7 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
|
||||
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
|
||||
inboundInfo, _ = simplejson.NewJson(marshalByte)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Unable to find inbound(s) in the nodeInfo.")
|
||||
return nil, fmt.Errorf("unable to find inbound(s) in the nodeInfo")
|
||||
}
|
||||
|
||||
port := uint32(inboundInfo.Get("port").MustUint64())
|
||||
|
@@ -82,7 +82,7 @@ func TestReportReportUserTraffic(t *testing.T) {
|
||||
Download: 114514,
|
||||
}
|
||||
}
|
||||
//client.Debug()
|
||||
// client.Debug()
|
||||
err = client.ReportUserTraffic(&generalUserTraffic)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
@@ -12,9 +12,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
// APIClient create an api client to the panel.
|
||||
@@ -80,7 +81,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// open the file
|
||||
file, err := os.Open(path)
|
||||
|
||||
//handle errors while opening
|
||||
// handle errors while opening
|
||||
if err != nil {
|
||||
log.Printf("Error when opening file: %s", err)
|
||||
return LocalRuleList
|
||||
@@ -98,7 +99,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||
// handle first encountered error while reading
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
log.Fatalf("Error while reading file: %s", err)
|
||||
return make([]api.DetectRule, 0)
|
||||
return
|
||||
}
|
||||
|
||||
file.Close()
|
||||
@@ -132,7 +133,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
||||
}
|
||||
rtn, err := simplejson.NewJson(res.Body())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Ret %s invalid", res.String())
|
||||
return nil, fmt.Errorf("ret %s invalid", res.String())
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
@@ -287,7 +288,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
//ReportNodeOnlineUsers implements the API interface
|
||||
// ReportNodeOnlineUsers implements the API interface
|
||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||
return nil
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ func TestReportReportUserTraffic(t *testing.T) {
|
||||
Download: 114514,
|
||||
}
|
||||
}
|
||||
//client.Debug()
|
||||
// client.Debug()
|
||||
err = client.ReportUserTraffic(&generalUserTraffic)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
@@ -9,8 +9,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/limiter"
|
||||
"github.com/XrayR-project/XrayR/common/rule"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
@@ -22,10 +20,13 @@ import (
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
routing_session "github.com/xtls/xray-core/features/routing/session"
|
||||
routingSession "github.com/xtls/xray-core/features/routing/session"
|
||||
"github.com/xtls/xray-core/features/stats"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/pipe"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/limiter"
|
||||
"github.com/XrayR-project/XrayR/common/rule"
|
||||
)
|
||||
|
||||
var errSniffingTimeout = newError("timeout on sniffing")
|
||||
@@ -98,7 +99,7 @@ type DefaultDispatcher struct {
|
||||
dns dns.Client
|
||||
fdns dns.FakeDNSEngine
|
||||
Limiter *limiter.Limiter
|
||||
RuleManager *rule.RuleManager
|
||||
RuleManager *rule.Manager
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -172,7 +173,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sn
|
||||
newError("[fakedns client] create a new map").WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
domain := addr.Domain()
|
||||
ips, err := d.dns.LookupIP(domain, dns.IPOption{true, true, false})
|
||||
ips, err := d.dns.LookupIP(domain, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
|
||||
if err == nil {
|
||||
for _, ip := range ips {
|
||||
ip2domain.Store(ip.String(), domain)
|
||||
@@ -314,13 +315,13 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
||||
}
|
||||
|
||||
sniffingRequest := content.SniffingRequest
|
||||
inbound, outbound, err := d.getLink(ctx, destination.Network, sniffingRequest)
|
||||
in, out, err := d.getLink(ctx, destination.Network, sniffingRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch {
|
||||
case !sniffingRequest.Enabled:
|
||||
go d.routedDispatch(ctx, outbound, destination)
|
||||
go d.routedDispatch(ctx, out, destination)
|
||||
case destination.Network != net.Network_TCP:
|
||||
// Only metadata sniff will be used for non tcp connection
|
||||
result, err := sniffer(ctx, nil, true)
|
||||
@@ -337,13 +338,13 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
||||
}
|
||||
}
|
||||
}
|
||||
go d.routedDispatch(ctx, outbound, destination)
|
||||
go d.routedDispatch(ctx, out, destination)
|
||||
default:
|
||||
go func() {
|
||||
cReader := &cachedReader{
|
||||
reader: outbound.Reader.(*pipe.Reader),
|
||||
reader: out.Reader.(*pipe.Reader),
|
||||
}
|
||||
outbound.Reader = cReader
|
||||
out.Reader = cReader
|
||||
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
|
||||
if err == nil {
|
||||
content.Protocol = result.Protocol()
|
||||
@@ -358,10 +359,10 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
||||
ob.Target = destination
|
||||
}
|
||||
}
|
||||
d.routedDispatch(ctx, outbound, destination)
|
||||
d.routedDispatch(ctx, out, destination)
|
||||
}()
|
||||
}
|
||||
return inbound, nil
|
||||
return in, nil
|
||||
}
|
||||
|
||||
// DispatchLink implements routing.Dispatcher.
|
||||
@@ -501,7 +502,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
||||
}
|
||||
}
|
||||
|
||||
routingLink := routing_session.AsRoutingContext(ctx)
|
||||
routingLink := routingSession.AsRoutingContext(ctx)
|
||||
inTag := routingLink.GetInboundTag()
|
||||
isPickRoute := 0
|
||||
if forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != "" {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Package dispather implement the rate limiter and the onlie device counter
|
||||
// Package mydispatcher Package dispatcher implement the rate limiter and the online device counter
|
||||
package mydispatcher
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
||||
|
@@ -1,14 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import "github.com/urfave/cli"
|
||||
|
||||
// CreateCommands Creates all CLI commands.
|
||||
func CreateCommands() []cli.Command {
|
||||
return []cli.Command{
|
||||
createRun(),
|
||||
createRevoke(),
|
||||
createRenew(),
|
||||
createDNSHelp(),
|
||||
createList(),
|
||||
}
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/XrayR-project/XrayR/common/legocmd/log"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func Before(ctx *cli.Context) error {
|
||||
if ctx.GlobalString("path") == "" {
|
||||
log.Panic("Could not determine current working directory. Please pass --path.")
|
||||
}
|
||||
|
||||
err := createNonExistingFolder(ctx.GlobalString("path"))
|
||||
if err != nil {
|
||||
log.Panicf("Could not check/create path: %v", err)
|
||||
}
|
||||
|
||||
if ctx.GlobalString("server") == "" {
|
||||
log.Panic("Could not determine current working server. Please pass --server.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func createDNSHelp() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "dnshelp",
|
||||
Usage: "Shows additional help for the '--dns' global option",
|
||||
Action: dnsHelp,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "code, c",
|
||||
Usage: fmt.Sprintf("DNS code: %s", allDNSCodes()),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dnsHelp(ctx *cli.Context) error {
|
||||
code := ctx.String("code")
|
||||
if code == "" {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
ew := &errWriter{w: w}
|
||||
|
||||
ew.writeln(`Credentials for DNS providers must be passed through environment variables.`)
|
||||
ew.writeln()
|
||||
ew.writeln(`To display the documentation for a DNS providers:`)
|
||||
ew.writeln()
|
||||
ew.writeln("\t$ lego dnshelp -c code")
|
||||
ew.writeln()
|
||||
ew.writeln("All DNS codes:")
|
||||
ew.writef("\t%s\n", allDNSCodes())
|
||||
ew.writeln()
|
||||
ew.writeln("More information: https://go-acme.github.io/lego/dns")
|
||||
|
||||
if ew.err != nil {
|
||||
return ew.err
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
return displayDNSHelp(strings.ToLower(code))
|
||||
}
|
||||
|
||||
type errWriter struct {
|
||||
w io.Writer
|
||||
err error
|
||||
}
|
||||
|
||||
func (ew *errWriter) writeln(a ...interface{}) {
|
||||
if ew.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, ew.err = fmt.Fprintln(ew.w, a...)
|
||||
}
|
||||
|
||||
func (ew *errWriter) writef(format string, a ...interface{}) {
|
||||
if ew.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, ew.err = fmt.Fprintf(ew.w, format, a...)
|
||||
}
|
@@ -1,136 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func createList() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "list",
|
||||
Usage: "Display certificates and accounts information.",
|
||||
Action: list,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "accounts, a",
|
||||
Usage: "Display accounts.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "names, n",
|
||||
Usage: "Display certificate common names only.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func list(ctx *cli.Context) error {
|
||||
if ctx.Bool("accounts") && !ctx.Bool("names") {
|
||||
if err := listAccount(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return listCertificates(ctx)
|
||||
}
|
||||
|
||||
func listCertificates(ctx *cli.Context) error {
|
||||
certsStorage := NewCertificatesStorage(ctx)
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(certsStorage.GetRootPath(), "*.crt"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names := ctx.Bool("names")
|
||||
|
||||
if len(matches) == 0 {
|
||||
if !names {
|
||||
fmt.Println("No certificates found.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !names {
|
||||
fmt.Println("Found the following certs:")
|
||||
}
|
||||
|
||||
for _, filename := range matches {
|
||||
if strings.HasSuffix(filename, ".issuer.crt") {
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pCert, err := certcrypto.ParsePEMCertificate(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if names {
|
||||
fmt.Println(pCert.Subject.CommonName)
|
||||
} else {
|
||||
fmt.Println(" Certificate Name:", pCert.Subject.CommonName)
|
||||
fmt.Println(" Domains:", strings.Join(pCert.DNSNames, ", "))
|
||||
fmt.Println(" Expiry Date:", pCert.NotAfter)
|
||||
fmt.Println(" Certificate Path:", filename)
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func listAccount(ctx *cli.Context) error {
|
||||
// fake email, needed by NewAccountsStorage
|
||||
if err := ctx.GlobalSet("email", "unknown"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accountsStorage := NewAccountsStorage(ctx)
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(accountsStorage.GetRootPath(), "*", "*", "*.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(matches) == 0 {
|
||||
fmt.Println("No accounts found.")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("Found the following accounts:")
|
||||
for _, filename := range matches {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var account Account
|
||||
err = json.Unmarshal(data, &account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri, err := url.Parse(account.Registration.URI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(" Email:", account.Email)
|
||||
fmt.Println(" Server:", uri.Host)
|
||||
fmt.Println(" Path:", filepath.Dir(filename))
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -1,225 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/legocmd/log"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
renewEnvAccountEmail = "LEGO_ACCOUNT_EMAIL"
|
||||
renewEnvCertDomain = "LEGO_CERT_DOMAIN"
|
||||
renewEnvCertPath = "LEGO_CERT_PATH"
|
||||
renewEnvCertKeyPath = "LEGO_CERT_KEY_PATH"
|
||||
)
|
||||
|
||||
func createRenew() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "renew",
|
||||
Usage: "Renew a certificate",
|
||||
Action: renew,
|
||||
Before: func(ctx *cli.Context) error {
|
||||
// we require either domains or csr, but not both
|
||||
hasDomains := len(ctx.GlobalStringSlice("domains")) > 0
|
||||
hasCsr := len(ctx.GlobalString("csr")) > 0
|
||||
if hasDomains && hasCsr {
|
||||
log.Panic("Please specify either --domains/-d or --csr/-c, but not both")
|
||||
}
|
||||
if !hasDomains && !hasCsr {
|
||||
log.Panic("Please specify --domains/-d (or --csr/-c if you already have a CSR)")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{
|
||||
Name: "days",
|
||||
Value: 30,
|
||||
Usage: "The number of days left on a certificate to renew it.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "reuse-key",
|
||||
Usage: "Used to indicate you want to reuse your current private key for the new certificate.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-bundle",
|
||||
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "must-staple",
|
||||
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "renew-hook",
|
||||
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "preferred-chain",
|
||||
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func renew(ctx *cli.Context) error {
|
||||
account, client := setup(ctx, NewAccountsStorage(ctx))
|
||||
setupChallenges(ctx, client)
|
||||
|
||||
if account.Registration == nil {
|
||||
log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email)
|
||||
}
|
||||
|
||||
certsStorage := NewCertificatesStorage(ctx)
|
||||
|
||||
bundle := !ctx.Bool("no-bundle")
|
||||
|
||||
meta := map[string]string{renewEnvAccountEmail: account.Email}
|
||||
|
||||
// CSR
|
||||
if ctx.GlobalIsSet("csr") {
|
||||
return renewForCSR(ctx, client, certsStorage, bundle, meta)
|
||||
}
|
||||
|
||||
// Domains
|
||||
return renewForDomains(ctx, client, certsStorage, bundle, meta)
|
||||
}
|
||||
|
||||
func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
|
||||
domains := ctx.GlobalStringSlice("domains")
|
||||
domain := domains[0]
|
||||
|
||||
// load the cert resource from files.
|
||||
// We store the certificate, private key and metadata in different files
|
||||
// as web servers would not be able to work with a combined file.
|
||||
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
|
||||
if err != nil {
|
||||
log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err)
|
||||
}
|
||||
|
||||
cert := certificates[0]
|
||||
|
||||
if !needRenewal(cert, domain, ctx.Int("days")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is just meant to be informal for the user.
|
||||
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
|
||||
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
|
||||
|
||||
certDomains := certcrypto.ExtractDomains(cert)
|
||||
|
||||
var privateKey crypto.PrivateKey
|
||||
if ctx.Bool("reuse-key") {
|
||||
keyBytes, errR := certsStorage.ReadFile(domain, ".key")
|
||||
if errR != nil {
|
||||
log.Panicf("Error while loading the private key for domain %s\n\t%v", domain, errR)
|
||||
}
|
||||
|
||||
privateKey, errR = certcrypto.ParsePEMPrivateKey(keyBytes)
|
||||
if errR != nil {
|
||||
return errR
|
||||
}
|
||||
}
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: merge(certDomains, domains),
|
||||
Bundle: bundle,
|
||||
PrivateKey: privateKey,
|
||||
MustStaple: ctx.Bool("must-staple"),
|
||||
PreferredChain: ctx.String("preferred-chain"),
|
||||
}
|
||||
certRes, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
certsStorage.SaveResource(certRes)
|
||||
|
||||
meta[renewEnvCertDomain] = domain
|
||||
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
|
||||
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
|
||||
|
||||
return launchHook(ctx.String("renew-hook"), meta)
|
||||
}
|
||||
|
||||
func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
|
||||
csr, err := readCSRFile(ctx.GlobalString("csr"))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
domain := csr.Subject.CommonName
|
||||
|
||||
// load the cert resource from files.
|
||||
// We store the certificate, private key and metadata in different files
|
||||
// as web servers would not be able to work with a combined file.
|
||||
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
|
||||
if err != nil {
|
||||
log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err)
|
||||
}
|
||||
|
||||
cert := certificates[0]
|
||||
|
||||
if !needRenewal(cert, domain, ctx.Int("days")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is just meant to be informal for the user.
|
||||
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
|
||||
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
|
||||
|
||||
certRes, err := client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
Bundle: bundle,
|
||||
PreferredChain: ctx.String("preferred-chain"),
|
||||
})
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
certsStorage.SaveResource(certRes)
|
||||
|
||||
meta[renewEnvCertDomain] = domain
|
||||
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
|
||||
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
|
||||
|
||||
return launchHook(ctx.String("renew-hook"), meta)
|
||||
}
|
||||
|
||||
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
|
||||
if x509Cert.IsCA {
|
||||
log.Panicf("[%s] Certificate bundle starts with a CA certificate", domain)
|
||||
}
|
||||
|
||||
if days >= 0 {
|
||||
notAfter := int(time.Until(x509Cert.NotAfter).Hours() / 24.0)
|
||||
if notAfter > days {
|
||||
log.Printf("[%s] The certificate expires in %d days, the number of days defined to perform the renewal is %d: no renewal.",
|
||||
domain, notAfter, days)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func merge(prevDomains, nextDomains []string) []string {
|
||||
for _, next := range nextDomains {
|
||||
var found bool
|
||||
for _, prev := range prevDomains {
|
||||
if prev == next {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
prevDomains = append(prevDomains, next)
|
||||
}
|
||||
}
|
||||
return prevDomains
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/XrayR-project/XrayR/common/legocmd/log"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func createRevoke() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "revoke",
|
||||
Usage: "Revoke a certificate",
|
||||
Action: revoke,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "keep, k",
|
||||
Usage: "Keep the certificates after the revocation instead of archiving them.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func revoke(ctx *cli.Context) error {
|
||||
acc, client := setup(ctx, NewAccountsStorage(ctx))
|
||||
|
||||
if acc.Registration == nil {
|
||||
log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", acc.Email)
|
||||
}
|
||||
|
||||
certsStorage := NewCertificatesStorage(ctx)
|
||||
certsStorage.CreateRootFolder()
|
||||
|
||||
for _, domain := range ctx.GlobalStringSlice("domains") {
|
||||
log.Printf("Trying to revoke certificate for domain %s", domain)
|
||||
|
||||
certBytes, err := certsStorage.ReadFile(domain, ".crt")
|
||||
if err != nil {
|
||||
log.Panicf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
|
||||
}
|
||||
|
||||
err = client.Certificate.Revoke(certBytes)
|
||||
if err != nil {
|
||||
log.Panicf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
|
||||
}
|
||||
|
||||
log.Println("Certificate was revoked.")
|
||||
|
||||
if ctx.Bool("keep") {
|
||||
return nil
|
||||
}
|
||||
|
||||
certsStorage.CreateArchiveFolder()
|
||||
|
||||
err = certsStorage.MoveToArchive(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("Certificate was archived for domain:", domain)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -1,186 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/legocmd/log"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func createRun() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "run",
|
||||
Usage: "Register an account, then create and install a certificate",
|
||||
Before: func(ctx *cli.Context) error {
|
||||
// we require either domains or csr, but not both
|
||||
hasDomains := len(ctx.GlobalStringSlice("domains")) > 0
|
||||
hasCsr := len(ctx.GlobalString("csr")) > 0
|
||||
if hasDomains && hasCsr {
|
||||
log.Panic("Please specify either --domains/-d or --csr/-c, but not both")
|
||||
}
|
||||
if !hasDomains && !hasCsr {
|
||||
log.Panic("Please specify --domains/-d (or --csr/-c if you already have a CSR)")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Action: run,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "no-bundle",
|
||||
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "must-staple",
|
||||
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "run-hook",
|
||||
Usage: "Define a hook. The hook is executed when the certificates are effectively created.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "preferred-chain",
|
||||
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const rootPathWarningMessage = `!!!! HEADS UP !!!!
|
||||
|
||||
Your account credentials have been saved in your Let's Encrypt
|
||||
configuration directory at "%s".
|
||||
|
||||
You should make a secure backup of this folder now. This
|
||||
configuration directory will also contain certificates and
|
||||
private keys obtained from Let's Encrypt so making regular
|
||||
backups of this folder is ideal.
|
||||
`
|
||||
|
||||
func run(ctx *cli.Context) error {
|
||||
accountsStorage := NewAccountsStorage(ctx)
|
||||
|
||||
account, client := setup(ctx, accountsStorage)
|
||||
setupChallenges(ctx, client)
|
||||
|
||||
if account.Registration == nil {
|
||||
reg, err := register(ctx, client)
|
||||
if err != nil {
|
||||
log.Panicf("Could not complete registration\n\t%v", err)
|
||||
}
|
||||
|
||||
account.Registration = reg
|
||||
if err = accountsStorage.Save(account); err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
|
||||
}
|
||||
|
||||
certsStorage := NewCertificatesStorage(ctx)
|
||||
certsStorage.CreateRootFolder()
|
||||
|
||||
cert, err := obtainCertificate(ctx, client)
|
||||
if err != nil {
|
||||
// Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error.
|
||||
// Due to us not returning partial certificate we can just exit here instead of at the end.
|
||||
log.Panicf("Could not obtain certificates:\n\t%v", err)
|
||||
}
|
||||
|
||||
certsStorage.SaveResource(cert)
|
||||
|
||||
meta := map[string]string{
|
||||
renewEnvAccountEmail: account.Email,
|
||||
renewEnvCertDomain: cert.Domain,
|
||||
renewEnvCertPath: certsStorage.GetFileName(cert.Domain, ".crt"),
|
||||
renewEnvCertKeyPath: certsStorage.GetFileName(cert.Domain, ".key"),
|
||||
}
|
||||
|
||||
return launchHook(ctx.String("run-hook"), meta)
|
||||
}
|
||||
|
||||
func handleTOS(ctx *cli.Context, client *lego.Client) bool {
|
||||
// Check for a global accept override
|
||||
if ctx.GlobalBool("accept-tos") {
|
||||
return true
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
log.Printf("Please review the TOS at %s", client.GetToSURL())
|
||||
|
||||
for {
|
||||
fmt.Println("Do you accept the TOS? Y/n")
|
||||
text, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Panicf("Could not read from console: %v", err)
|
||||
}
|
||||
|
||||
text = strings.Trim(text, "\r\n")
|
||||
switch text {
|
||||
case "", "y", "Y":
|
||||
return true
|
||||
case "n", "N":
|
||||
return false
|
||||
default:
|
||||
fmt.Println("Your input was invalid. Please answer with one of Y/y, n/N or by pressing enter.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func register(ctx *cli.Context, client *lego.Client) (*registration.Resource, error) {
|
||||
accepted := handleTOS(ctx, client)
|
||||
if !accepted {
|
||||
log.Panic("You did not accept the TOS. Unable to proceed.")
|
||||
}
|
||||
|
||||
if ctx.GlobalBool("eab") {
|
||||
kid := ctx.GlobalString("kid")
|
||||
hmacEncoded := ctx.GlobalString("hmac")
|
||||
|
||||
if kid == "" || hmacEncoded == "" {
|
||||
log.Panicf("Requires arguments --kid and --hmac.")
|
||||
}
|
||||
|
||||
return client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: accepted,
|
||||
Kid: kid,
|
||||
HmacEncoded: hmacEncoded,
|
||||
})
|
||||
}
|
||||
|
||||
return client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
}
|
||||
|
||||
func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Resource, error) {
|
||||
bundle := !ctx.Bool("no-bundle")
|
||||
|
||||
domains := ctx.GlobalStringSlice("domains")
|
||||
if len(domains) > 0 {
|
||||
// obtain a certificate, generating a new private key
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: domains,
|
||||
Bundle: bundle,
|
||||
MustStaple: ctx.Bool("must-staple"),
|
||||
PreferredChain: ctx.String("preferred-chain"),
|
||||
}
|
||||
return client.Certificate.Obtain(request)
|
||||
}
|
||||
|
||||
// read the CSR
|
||||
csr, err := readCSRFile(ctx.GlobalString("csr"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// obtain a certificate for this CSR
|
||||
return client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
Bundle: bundle,
|
||||
PreferredChain: ctx.String("preferred-chain"),
|
||||
})
|
||||
}
|
@@ -1,120 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func CreateFlags(defaultPath string) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "domains, d",
|
||||
Usage: "Add a domain to the process. Can be specified multiple times.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "server, s",
|
||||
Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
|
||||
Value: lego.LEDirectoryProduction,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "accept-tos, a",
|
||||
Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "email, m",
|
||||
Usage: "Email used for registration and recovery contact.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "csr, c",
|
||||
Usage: "Certificate signing request filename, if an external CSR is to be used.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "eab",
|
||||
Usage: "Use External Account Binding for account registration. Requires --kid and --hmac.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "kid",
|
||||
Usage: "Key identifier from External CA. Used for External Account Binding.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hmac",
|
||||
Usage: "MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "key-type, k",
|
||||
Value: "ec256",
|
||||
Usage: "Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "filename",
|
||||
Usage: "(deprecated) Filename of the generated certificate.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "path",
|
||||
EnvVar: "LEGO_PATH",
|
||||
Usage: "Directory to use for storing the data.",
|
||||
Value: defaultPath,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "http",
|
||||
Usage: "Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "http.port",
|
||||
Usage: "Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port.",
|
||||
Value: ":80",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "http.proxy-header",
|
||||
Usage: "Validate against this HTTP header when solving HTTP based challenges behind a reverse proxy.",
|
||||
Value: "Host",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "http.webroot",
|
||||
Usage: "Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "http.memcached-host",
|
||||
Usage: "Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "tls",
|
||||
Usage: "Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tls.port",
|
||||
Usage: "Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port.",
|
||||
Value: ":443",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "dns",
|
||||
Usage: "Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "dns.disable-cp",
|
||||
Usage: "By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "dns.resolvers",
|
||||
Usage: "Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "http-timeout",
|
||||
Usage: "Set the HTTP timeout value to a specific value in seconds.",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "dns-timeout",
|
||||
Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries.",
|
||||
Value: 10,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "pem",
|
||||
Usage: "Generate a .pem file by concatenating the .key and .crt files together.",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "cert.timeout",
|
||||
Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.",
|
||||
Value: 30,
|
||||
},
|
||||
}
|
||||
}
|
@@ -1,47 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func launchHook(hook string, meta map[string]string) error {
|
||||
if hook == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctxCmd, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
defer cancel()
|
||||
|
||||
parts := strings.Fields(hook)
|
||||
|
||||
cmdCtx := exec.CommandContext(ctxCmd, parts[0], parts[1:]...)
|
||||
cmdCtx.Env = append(os.Environ(), metaToEnv(meta)...)
|
||||
|
||||
output, err := cmdCtx.CombinedOutput()
|
||||
|
||||
if len(output) > 0 {
|
||||
fmt.Println(string(output))
|
||||
}
|
||||
|
||||
if errors.Is(ctxCmd.Err(), context.DeadlineExceeded) {
|
||||
return errors.New("hook timed out")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func metaToEnv(meta map[string]string) []string {
|
||||
var envs []string
|
||||
|
||||
for k, v := range meta {
|
||||
envs = append(envs, k+"="+v)
|
||||
}
|
||||
|
||||
return envs
|
||||
}
|
@@ -1,129 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/legocmd/log"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const filePerm os.FileMode = 0o600
|
||||
|
||||
func setup(ctx *cli.Context, accountsStorage *AccountsStorage) (*Account, *lego.Client) {
|
||||
keyType := getKeyType(ctx)
|
||||
privateKey := accountsStorage.GetPrivateKey(keyType)
|
||||
|
||||
var account *Account
|
||||
if accountsStorage.ExistsAccountFilePath() {
|
||||
account = accountsStorage.LoadAccount(privateKey)
|
||||
} else {
|
||||
account = &Account{Email: accountsStorage.GetUserID(), key: privateKey}
|
||||
}
|
||||
|
||||
client := newClient(ctx, account, keyType)
|
||||
|
||||
return account, client
|
||||
}
|
||||
|
||||
func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
|
||||
config := lego.NewConfig(acc)
|
||||
config.CADirURL = ctx.GlobalString("server")
|
||||
|
||||
config.Certificate = lego.CertificateConfig{
|
||||
KeyType: keyType,
|
||||
Timeout: time.Duration(ctx.GlobalInt("cert.timeout")) * time.Second,
|
||||
}
|
||||
config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version)
|
||||
|
||||
if ctx.GlobalIsSet("http-timeout") {
|
||||
config.HTTPClient.Timeout = time.Duration(ctx.GlobalInt("http-timeout")) * time.Second
|
||||
}
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
log.Panicf("Could not create client: %v", err)
|
||||
}
|
||||
|
||||
if client.GetExternalAccountRequired() && !ctx.GlobalIsSet("eab") {
|
||||
log.Panic("Server requires External Account Binding. Use --eab with --kid and --hmac.")
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// getKeyType the type from which private keys should be generated.
|
||||
func getKeyType(ctx *cli.Context) certcrypto.KeyType {
|
||||
keyType := ctx.GlobalString("key-type")
|
||||
switch strings.ToUpper(keyType) {
|
||||
case "RSA2048":
|
||||
return certcrypto.RSA2048
|
||||
case "RSA4096":
|
||||
return certcrypto.RSA4096
|
||||
case "RSA8192":
|
||||
return certcrypto.RSA8192
|
||||
case "EC256":
|
||||
return certcrypto.EC256
|
||||
case "EC384":
|
||||
return certcrypto.EC384
|
||||
}
|
||||
|
||||
log.Panicf("Unsupported KeyType: %s", keyType)
|
||||
return ""
|
||||
}
|
||||
|
||||
func getEmail(ctx *cli.Context) string {
|
||||
email := ctx.GlobalString("email")
|
||||
if email == "" {
|
||||
log.Panic("You have to pass an account (email address) to the program using --email or -m")
|
||||
}
|
||||
return email
|
||||
}
|
||||
|
||||
func createNonExistingFolder(path string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return os.MkdirAll(path, 0o700)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readCSRFile(filename string) (*x509.CertificateRequest, error) {
|
||||
bytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := bytes
|
||||
|
||||
// see if we can find a PEM-encoded CSR
|
||||
var p *pem.Block
|
||||
rest := bytes
|
||||
for {
|
||||
// decode a PEM block
|
||||
p, rest = pem.Decode(rest)
|
||||
|
||||
// did we fail?
|
||||
if p == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// did we get a CSR?
|
||||
if p.Type == "CERTIFICATE REQUEST" {
|
||||
raw = p.Bytes
|
||||
}
|
||||
}
|
||||
|
||||
// no PEM-encoded CSR
|
||||
// assume we were given a DER-encoded ASN.1 CSR
|
||||
// (if this assumption is wrong, parsing these bytes will fail)
|
||||
return x509.ParseCertificateRequest(raw)
|
||||
}
|
@@ -1,126 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/legocmd/log"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/challenge/http01"
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/providers/dns"
|
||||
"github.com/go-acme/lego/v4/providers/http/memcached"
|
||||
"github.com/go-acme/lego/v4/providers/http/webroot"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func setupChallenges(ctx *cli.Context, client *lego.Client) {
|
||||
if !ctx.GlobalBool("http") && !ctx.GlobalBool("tls") && !ctx.GlobalIsSet("dns") {
|
||||
log.Panic("No challenge selected. You must specify at least one challenge: `--http`, `--tls`, `--dns`.")
|
||||
}
|
||||
|
||||
if ctx.GlobalBool("http") {
|
||||
err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(ctx))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.GlobalBool("tls") {
|
||||
err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet("dns") {
|
||||
setupDNS(ctx, client)
|
||||
}
|
||||
}
|
||||
|
||||
func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
|
||||
switch {
|
||||
case ctx.GlobalIsSet("http.webroot"):
|
||||
ps, err := webroot.NewHTTPProvider(ctx.GlobalString("http.webroot"))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
return ps
|
||||
case ctx.GlobalIsSet("http.memcached-host"):
|
||||
ps, err := memcached.NewMemcachedProvider(ctx.GlobalStringSlice("http.memcached-host"))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
return ps
|
||||
case ctx.GlobalIsSet("http.port"):
|
||||
iface := ctx.GlobalString("http.port")
|
||||
if !strings.Contains(iface, ":") {
|
||||
log.Panicf("The --http switch only accepts interface:port or :port for its argument.")
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(iface)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
srv := http01.NewProviderServer(host, port)
|
||||
if header := ctx.GlobalString("http.proxy-header"); header != "" {
|
||||
srv.SetProxyHeader(header)
|
||||
}
|
||||
return srv
|
||||
case ctx.GlobalBool("http"):
|
||||
srv := http01.NewProviderServer("", "")
|
||||
if header := ctx.GlobalString("http.proxy-header"); header != "" {
|
||||
srv.SetProxyHeader(header)
|
||||
}
|
||||
return srv
|
||||
default:
|
||||
log.Panic("Invalid HTTP challenge options.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func setupTLSProvider(ctx *cli.Context) challenge.Provider {
|
||||
switch {
|
||||
case ctx.GlobalIsSet("tls.port"):
|
||||
iface := ctx.GlobalString("tls.port")
|
||||
if !strings.Contains(iface, ":") {
|
||||
log.Panicf("The --tls switch only accepts interface:port or :port for its argument.")
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(iface)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
return tlsalpn01.NewProviderServer(host, port)
|
||||
case ctx.GlobalBool("tls"):
|
||||
return tlsalpn01.NewProviderServer("", "")
|
||||
default:
|
||||
log.Panic("Invalid HTTP challenge options.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func setupDNS(ctx *cli.Context, client *lego.Client) {
|
||||
provider, err := dns.NewDNSChallengeProviderByName(ctx.GlobalString("dns"))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
servers := ctx.GlobalStringSlice("dns.resolvers")
|
||||
err = client.Challenge.SetDNS01Provider(provider,
|
||||
dns01.CondOption(len(servers) > 0,
|
||||
dns01.AddRecursiveNameservers(dns01.ParseNameservers(ctx.GlobalStringSlice("dns.resolvers")))),
|
||||
dns01.CondOption(ctx.GlobalBool("dns.disable-cp"),
|
||||
dns01.DisableCompletePropagationRequirement()),
|
||||
dns01.CondOption(ctx.GlobalIsSet("dns-timeout"),
|
||||
dns01.AddDNSTimeout(time.Duration(ctx.GlobalInt("dns-timeout"))*time.Second)),
|
||||
)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,189 +0,0 @@
|
||||
// Let's Encrypt client to go!
|
||||
// CLI application for generating Let's Encrypt certificates using the ACME package.
|
||||
package legocmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/legocmd/cmd"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var version = "dev"
|
||||
var defaultPath string
|
||||
|
||||
type LegoCMD struct {
|
||||
cmdClient *cli.App
|
||||
}
|
||||
|
||||
func New() (*LegoCMD, error) {
|
||||
app := cli.NewApp()
|
||||
app.Name = "lego"
|
||||
app.HelpName = "lego"
|
||||
app.Usage = "Let's Encrypt client written in Go"
|
||||
app.EnableBashCompletion = true
|
||||
|
||||
app.Version = version
|
||||
cli.VersionPrinter = func(c *cli.Context) {
|
||||
fmt.Printf("lego version %s %s/%s\n", c.App.Version, runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// Set default path to configPath/cert
|
||||
var path string = ""
|
||||
configPath := os.Getenv("XRAY_LOCATION_CONFIG")
|
||||
if configPath != "" {
|
||||
path = configPath
|
||||
} else if cwd, err := os.Getwd(); err==nil{
|
||||
path = cwd
|
||||
} else {
|
||||
path = "."
|
||||
}
|
||||
|
||||
defaultPath = filepath.Join(path, "cert")
|
||||
|
||||
app.Flags = cmd.CreateFlags(defaultPath)
|
||||
|
||||
app.Before = cmd.Before
|
||||
|
||||
app.Commands = cmd.CreateCommands()
|
||||
|
||||
lego := &LegoCMD{
|
||||
cmdClient: app,
|
||||
}
|
||||
|
||||
return lego, nil
|
||||
}
|
||||
|
||||
// DNSCert cert a domain using DNS API
|
||||
func (l *LegoCMD) DNSCert(domain, email, provider string, DNSEnv map[string]string) (CertPath string, KeyPath string, err error) {
|
||||
defer func() (string, string, error) {
|
||||
// Handle any error
|
||||
if r := recover(); r != nil {
|
||||
switch x := r.(type) {
|
||||
case string:
|
||||
err = errors.New(x)
|
||||
case error:
|
||||
err = x
|
||||
default:
|
||||
err = errors.New("unknow panic")
|
||||
}
|
||||
return "", "", err
|
||||
}
|
||||
return CertPath, KeyPath, nil
|
||||
}()
|
||||
// Set Env for DNS configuration
|
||||
for key, value := range DNSEnv {
|
||||
os.Setenv(key, value)
|
||||
}
|
||||
// First check if the certificate exists
|
||||
CertPath, KeyPath, err = checkCertfile(domain)
|
||||
if err == nil {
|
||||
return CertPath, KeyPath, err
|
||||
}
|
||||
|
||||
argstring := fmt.Sprintf("lego -a -d %s -m %s --dns %s run", domain, email, provider)
|
||||
err = l.cmdClient.Run(strings.Split(argstring, " "))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
CertPath, KeyPath, err = checkCertfile(domain)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return CertPath, KeyPath, nil
|
||||
}
|
||||
|
||||
// HTTPCert cert a domain using http methods
|
||||
func (l *LegoCMD) HTTPCert(domain, email string) (CertPath string, KeyPath string, err error) {
|
||||
defer func() (string, string, error) {
|
||||
// Handle any error
|
||||
if r := recover(); r != nil {
|
||||
switch x := r.(type) {
|
||||
case string:
|
||||
err = errors.New(x)
|
||||
case error:
|
||||
err = x
|
||||
default:
|
||||
err = errors.New("unknow panic")
|
||||
}
|
||||
return "", "", err
|
||||
}
|
||||
return CertPath, KeyPath, nil
|
||||
}()
|
||||
// First check if the certificate exists
|
||||
CertPath, KeyPath, err = checkCertfile(domain)
|
||||
if err == nil {
|
||||
return CertPath, KeyPath, err
|
||||
}
|
||||
argstring := fmt.Sprintf("lego -a -d %s -m %s --http run", domain, email)
|
||||
err = l.cmdClient.Run(strings.Split(argstring, " "))
|
||||
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
CertPath, KeyPath, err = checkCertfile(domain)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return CertPath, KeyPath, nil
|
||||
}
|
||||
|
||||
//RenewCert renew a domain cert
|
||||
func (l *LegoCMD) RenewCert(domain, email, certMode, provider string, DNSEnv map[string]string) (CertPath string, KeyPath string, err error) {
|
||||
var argstring string
|
||||
defer func() (string, string, error) {
|
||||
// Handle any error
|
||||
if r := recover(); r != nil {
|
||||
switch x := r.(type) {
|
||||
case string:
|
||||
err = errors.New(x)
|
||||
case error:
|
||||
err = x
|
||||
default:
|
||||
err = errors.New("unknow panic")
|
||||
}
|
||||
return "", "", err
|
||||
}
|
||||
return CertPath, KeyPath, nil
|
||||
}()
|
||||
if certMode == "http" {
|
||||
argstring = fmt.Sprintf("lego -a -d %s -m %s --http renew --days 30", domain, email)
|
||||
} else if certMode == "dns" {
|
||||
// Set Env for DNS configuration
|
||||
for key, value := range DNSEnv {
|
||||
os.Setenv(key, value)
|
||||
}
|
||||
argstring = fmt.Sprintf("lego -a -d %s -m %s --dns %s renew --days 30", domain, email, provider)
|
||||
} else {
|
||||
return "", "", fmt.Errorf("Unsupport cert mode: %s", certMode)
|
||||
}
|
||||
err = l.cmdClient.Run(strings.Split(argstring, " "))
|
||||
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
CertPath, KeyPath, err = checkCertfile(domain)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return CertPath, KeyPath, nil
|
||||
}
|
||||
func checkCertfile(domain string) (string, string, error) {
|
||||
keyPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.key", domain))
|
||||
certPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.crt", domain))
|
||||
if _, err := os.Stat(keyPath); os.IsNotExist(err) {
|
||||
return "", "", fmt.Errorf("Cert key failed: %s", domain)
|
||||
}
|
||||
if _, err := os.Stat(certPath); os.IsNotExist(err) {
|
||||
return "", "", fmt.Errorf("Cert cert failed: %s", domain)
|
||||
}
|
||||
absKeyPath, _ := filepath.Abs(keyPath)
|
||||
absCertPath, _ := filepath.Abs(certPath)
|
||||
return absCertPath, absKeyPath, nil
|
||||
}
|
@@ -1,82 +0,0 @@
|
||||
package legocmd_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/legocmd"
|
||||
)
|
||||
|
||||
func TestLegoClient(t *testing.T) {
|
||||
_, err := legocmd.New()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegoDNSCert(t *testing.T) {
|
||||
lego, err := legocmd.New()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var (
|
||||
domain string = "node1.test.com"
|
||||
email string = "test@gmail.com"
|
||||
provider string = "alidns"
|
||||
DNSEnv map[string]string
|
||||
)
|
||||
DNSEnv = make(map[string]string)
|
||||
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
||||
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
|
||||
certPath, keyPath, err := lego.DNSCert(domain, email, provider, DNSEnv)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(certPath)
|
||||
t.Log(keyPath)
|
||||
}
|
||||
|
||||
func TestLegoHTTPCert(t *testing.T) {
|
||||
lego, err := legocmd.New()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var (
|
||||
domain string = "node1.test.com"
|
||||
email string = "test@gmail.com"
|
||||
)
|
||||
certPath, keyPath, err := lego.HTTPCert(domain, email)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(certPath)
|
||||
t.Log(keyPath)
|
||||
}
|
||||
|
||||
func TestLegoRenewCert(t *testing.T) {
|
||||
lego, err := legocmd.New()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var (
|
||||
domain string = "node1.test.com"
|
||||
email string = "test@gmail.com"
|
||||
provider string = "alidns"
|
||||
DNSEnv map[string]string
|
||||
)
|
||||
DNSEnv = make(map[string]string)
|
||||
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
||||
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
|
||||
certPath, keyPath, err := lego.RenewCert(domain, email, "dns", provider, DNSEnv)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(certPath)
|
||||
t.Log(keyPath)
|
||||
|
||||
certPath, keyPath, err = lego.RenewCert(domain, email, "http", provider, DNSEnv)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(certPath)
|
||||
t.Log(keyPath)
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Logger is an optional custom logger.
|
||||
var Logger StdLogger = log.New(os.Stdout, "", log.LstdFlags)
|
||||
|
||||
// StdLogger interface for Standard Logger.
|
||||
type StdLogger interface {
|
||||
Panic(args ...interface{})
|
||||
Fatalln(args ...interface{})
|
||||
Panicf(format string, args ...interface{})
|
||||
Print(args ...interface{})
|
||||
Println(args ...interface{})
|
||||
Printf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// Panic writes a log entry.
|
||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||
func Panic(args ...interface{}) {
|
||||
Logger.Panic(args...)
|
||||
}
|
||||
|
||||
// Panicf writes a log entry.
|
||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||
func Panicf(format string, args ...interface{}) {
|
||||
Logger.Panicf(format, args...)
|
||||
}
|
||||
|
||||
// Print writes a log entry.
|
||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||
func Print(args ...interface{}) {
|
||||
Logger.Print(args...)
|
||||
}
|
||||
|
||||
// Println writes a log entry.
|
||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||
func Println(args ...interface{}) {
|
||||
Logger.Println(args...)
|
||||
}
|
||||
|
||||
// Printf writes a log entry.
|
||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||
func Printf(format string, args ...interface{}) {
|
||||
Logger.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Warnf writes a log entry.
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
Printf("[WARN] "+format, args...)
|
||||
}
|
||||
|
||||
// Infof writes a log entry.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
Printf("[INFO] "+format, args...)
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/juju/ratelimit"
|
||||
)
|
||||
|
||||
type UserInfo struct {
|
||||
@@ -20,12 +24,18 @@ type InboundInfo struct {
|
||||
Tag string
|
||||
NodeSpeedLimit uint64
|
||||
UserInfo *sync.Map // Key: Email value: UserInfo
|
||||
BucketHub *sync.Map // key: Email, value: *ratelimit.Bucket
|
||||
BucketHub *sync.Map // key: Email, value: *rate.Limiter
|
||||
UserOnlineIP *sync.Map // Key: Email Value: *sync.Map: Key: IP, Value: UID
|
||||
}
|
||||
|
||||
type Limiter struct {
|
||||
InboundInfo *sync.Map // Key: Tag, Value: *InboundInfo
|
||||
r *redis.Client
|
||||
g struct {
|
||||
limit int
|
||||
timeout int
|
||||
expiry int
|
||||
}
|
||||
}
|
||||
|
||||
func New() *Limiter {
|
||||
@@ -34,7 +44,19 @@ func New() *Limiter {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo) error {
|
||||
func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo, globalDeviceLimit *GlobalDeviceLimitConfig) error {
|
||||
// global limit
|
||||
if globalDeviceLimit.Limit > 0 {
|
||||
l.r = redis.NewClient(&redis.Options{
|
||||
Addr: globalDeviceLimit.RedisAddr,
|
||||
Password: globalDeviceLimit.RedisPassword,
|
||||
DB: globalDeviceLimit.RedisDB,
|
||||
})
|
||||
l.g.limit = globalDeviceLimit.Limit
|
||||
l.g.timeout = globalDeviceLimit.Timeout
|
||||
l.g.expiry = globalDeviceLimit.Expiry
|
||||
}
|
||||
|
||||
inboundInfo := &InboundInfo{
|
||||
Tag: tag,
|
||||
NodeSpeedLimit: nodeSpeedLimit,
|
||||
@@ -65,7 +87,17 @@ func (l *Limiter) UpdateInboundLimiter(tag string, updatedUserList *[]api.UserIn
|
||||
SpeedLimit: u.SpeedLimit,
|
||||
DeviceLimit: u.DeviceLimit,
|
||||
})
|
||||
inboundInfo.BucketHub.Delete(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID)) // Delete old limiter bucket
|
||||
// Update old limiter bucket
|
||||
limit := determineRate(inboundInfo.NodeSpeedLimit, u.SpeedLimit)
|
||||
if limit > 0 {
|
||||
if bucket, ok := inboundInfo.BucketHub.Load(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID)); ok {
|
||||
limiter := bucket.(*rate.Limiter)
|
||||
limiter.SetLimit(rate.Limit(limit))
|
||||
limiter.SetBurst(int(limit))
|
||||
}
|
||||
} else {
|
||||
inboundInfo.BucketHub.Delete(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("no such inbound in limiter: %s", tag)
|
||||
@@ -108,20 +140,48 @@ func (l *Limiter) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
|
||||
return &onlineUser, nil
|
||||
}
|
||||
|
||||
func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *ratelimit.Bucket, SpeedLimit bool, Reject bool) {
|
||||
func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *rate.Limiter, SpeedLimit bool, Reject bool) {
|
||||
if value, ok := l.InboundInfo.Load(tag); ok {
|
||||
inboundInfo := value.(*InboundInfo)
|
||||
nodeLimit := inboundInfo.NodeSpeedLimit
|
||||
var userLimit uint64 = 0
|
||||
var deviceLimit int = 0
|
||||
var uid int = 0
|
||||
var (
|
||||
userLimit uint64 = 0
|
||||
deviceLimit, uid int
|
||||
)
|
||||
|
||||
if v, ok := inboundInfo.UserInfo.Load(email); ok {
|
||||
u := v.(UserInfo)
|
||||
uid = u.UID
|
||||
userLimit = u.SpeedLimit
|
||||
deviceLimit = u.DeviceLimit
|
||||
}
|
||||
// Report online device
|
||||
|
||||
// Global device limit
|
||||
if l.g.limit > 0 {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(l.g.timeout))
|
||||
defer cancel()
|
||||
uidString := strconv.Itoa(uid)
|
||||
// If any device is online
|
||||
if exists, err := l.r.Exists(ctx, uidString).Result(); err != nil {
|
||||
newError(fmt.Sprintf("Redis: %v", err)).AtError().WriteToLog()
|
||||
} else if exists == 0 { // No user is online
|
||||
l.r.SAdd(ctx, uidString, ip)
|
||||
l.r.Expire(ctx, uidString, time.Second*time.Duration(l.g.expiry))
|
||||
} else {
|
||||
// If this ip is a new device
|
||||
if online, err := l.r.SIsMember(ctx, uidString, ip).Result(); err != nil {
|
||||
newError(fmt.Sprintf("Redis: %v", err)).AtError().WriteToLog()
|
||||
} else if !online {
|
||||
l.r.SAdd(ctx, uidString, ip)
|
||||
if l.r.SCard(ctx, uidString).Val() > int64(l.g.limit) {
|
||||
l.r.SRem(ctx, uidString, ip)
|
||||
return nil, false, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Local device limit
|
||||
ipMap := new(sync.Map)
|
||||
ipMap.Store(ip, uid)
|
||||
// If any device is online
|
||||
@@ -142,9 +202,9 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
|
||||
}
|
||||
limit := determineRate(nodeLimit, userLimit) // If need the Speed limit
|
||||
if limit > 0 {
|
||||
limiter := ratelimit.NewBucketWithQuantum(time.Duration(int64(time.Second)), int64(limit), int64(limit)) // Byte/s
|
||||
limiter := rate.NewLimiter(rate.Limit(limit), int(limit)) // Byte/s
|
||||
if v, ok := inboundInfo.BucketHub.LoadOrStore(email, limiter); ok {
|
||||
bucket := v.(*ratelimit.Bucket)
|
||||
bucket := v.(*rate.Limiter)
|
||||
return bucket, true, false
|
||||
} else {
|
||||
return limiter, true, false
|
||||
|
10
common/limiter/model.go
Normal file
10
common/limiter/model.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package limiter
|
||||
|
||||
type GlobalDeviceLimitConfig struct {
|
||||
Limit int `mapstructure:"Limit"`
|
||||
RedisAddr string `mapstructure:"RedisAddr"` // host:port
|
||||
RedisPassword string `mapstructure:"RedisPassword"`
|
||||
RedisDB int `mapstructure:"RedisDB"`
|
||||
Timeout int `mapstructure:"Timeout"`
|
||||
Expiry int `mapstructure:"Expiry"` // second
|
||||
}
|
@@ -1,20 +1,21 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/juju/ratelimit"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
writer buf.Writer
|
||||
limiter *ratelimit.Bucket
|
||||
limiter *rate.Limiter
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (l *Limiter) RateWriter(writer buf.Writer, limiter *ratelimit.Bucket) buf.Writer {
|
||||
func (l *Limiter) RateWriter(writer buf.Writer, limiter *rate.Limiter) buf.Writer {
|
||||
return &Writer{
|
||||
writer: writer,
|
||||
limiter: limiter,
|
||||
@@ -26,6 +27,7 @@ func (w *Writer) Close() error {
|
||||
}
|
||||
|
||||
func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
w.limiter.Wait(int64(mb.Len()))
|
||||
ctx := context.Background()
|
||||
w.limiter.WaitN(ctx, int(mb.Len()))
|
||||
return w.writer.WriteMultiBuffer(mb)
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package mylego
|
||||
|
||||
import (
|
||||
"crypto"
|
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package mylego
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
@@ -6,18 +6,16 @@ import (
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/legocmd/log"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/crypto/acme"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -30,56 +28,53 @@ const (
|
||||
//
|
||||
// rootPath:
|
||||
//
|
||||
// ./.lego/accounts/
|
||||
// │ └── root accounts directory
|
||||
// └── "path" option
|
||||
// ./.lego/accounts/
|
||||
// │ └── root accounts directory
|
||||
// └── "path" option
|
||||
//
|
||||
// rootUserPath:
|
||||
//
|
||||
// ./.lego/accounts/localhost_14000/hubert@hubert.com/
|
||||
// │ │ │ └── userID ("email" option)
|
||||
// │ │ └── CA server ("server" option)
|
||||
// │ └── root accounts directory
|
||||
// └── "path" option
|
||||
// ./.lego/accounts/localhost_14000/hubert@hubert.com/
|
||||
// │ │ │ └── userID ("email" option)
|
||||
// │ │ └── CA server ("server" option)
|
||||
// │ └── root accounts directory
|
||||
// └── "path" option
|
||||
//
|
||||
// keysPath:
|
||||
//
|
||||
// ./.lego/accounts/localhost_14000/hubert@hubert.com/keys/
|
||||
// │ │ │ │ └── root keys directory
|
||||
// │ │ │ └── userID ("email" option)
|
||||
// │ │ └── CA server ("server" option)
|
||||
// │ └── root accounts directory
|
||||
// └── "path" option
|
||||
// ./.lego/accounts/localhost_14000/hubert@hubert.com/keys/
|
||||
// │ │ │ │ └── root keys directory
|
||||
// │ │ │ └── userID ("email" option)
|
||||
// │ │ └── CA server ("server" option)
|
||||
// │ └── root accounts directory
|
||||
// └── "path" option
|
||||
//
|
||||
// accountFilePath:
|
||||
//
|
||||
// ./.lego/accounts/localhost_14000/hubert@hubert.com/account.json
|
||||
// │ │ │ │ └── account file
|
||||
// │ │ │ └── userID ("email" option)
|
||||
// │ │ └── CA server ("server" option)
|
||||
// │ └── root accounts directory
|
||||
// └── "path" option
|
||||
//
|
||||
// ./.lego/accounts/localhost_14000/hubert@hubert.com/account.json
|
||||
// │ │ │ │ └── account file
|
||||
// │ │ │ └── userID ("email" option)
|
||||
// │ │ └── CA server ("server" option)
|
||||
// │ └── root accounts directory
|
||||
// └── "path" option
|
||||
type AccountsStorage struct {
|
||||
userID string
|
||||
rootPath string
|
||||
rootUserPath string
|
||||
keysPath string
|
||||
accountFilePath string
|
||||
ctx *cli.Context
|
||||
}
|
||||
|
||||
// NewAccountsStorage Creates a new AccountsStorage.
|
||||
func NewAccountsStorage(ctx *cli.Context) *AccountsStorage {
|
||||
// TODO: move to account struct? Currently MUST pass email.
|
||||
email := getEmail(ctx)
|
||||
func NewAccountsStorage(l *LegoCMD) *AccountsStorage {
|
||||
email := l.C.Email
|
||||
|
||||
serverURL, err := url.Parse(ctx.GlobalString("server"))
|
||||
serverURL, err := url.Parse(acme.LetsEncryptURL)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
rootPath := filepath.Join(ctx.GlobalString("path"), baseAccountsRootFolderName)
|
||||
rootPath := filepath.Join(l.path, baseAccountsRootFolderName)
|
||||
serverPath := strings.NewReplacer(":", "_", "/", string(os.PathSeparator)).Replace(serverURL.Host)
|
||||
accountsPath := filepath.Join(rootPath, serverPath)
|
||||
rootUserPath := filepath.Join(accountsPath, email)
|
||||
@@ -90,7 +85,6 @@ func NewAccountsStorage(ctx *cli.Context) *AccountsStorage {
|
||||
rootUserPath: rootUserPath,
|
||||
keysPath: filepath.Join(rootUserPath, baseKeysFolderName),
|
||||
accountFilePath: filepath.Join(rootUserPath, accountFileName),
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,11 +116,11 @@ func (s *AccountsStorage) Save(account *Account) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(s.accountFilePath, jsonBytes, filePerm)
|
||||
return os.WriteFile(s.accountFilePath, jsonBytes, filePerm)
|
||||
}
|
||||
|
||||
func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account {
|
||||
fileBytes, err := ioutil.ReadFile(s.accountFilePath)
|
||||
fileBytes, err := os.ReadFile(s.accountFilePath)
|
||||
if err != nil {
|
||||
log.Panicf("Could not load file for account %s: %v", s.userID, err)
|
||||
}
|
||||
@@ -140,7 +134,7 @@ func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account {
|
||||
account.key = privateKey
|
||||
|
||||
if account.Registration == nil || account.Registration.Body.Status == "" {
|
||||
reg, err := tryRecoverRegistration(s.ctx, privateKey)
|
||||
reg, err := tryRecoverRegistration(privateKey)
|
||||
if err != nil {
|
||||
log.Panicf("Could not load account for %s. Registration is nil: %#v", s.userID, err)
|
||||
}
|
||||
@@ -207,7 +201,7 @@ func generatePrivateKey(file string, keyType certcrypto.KeyType) (crypto.Private
|
||||
}
|
||||
|
||||
func loadPrivateKey(file string) (crypto.PrivateKey, error) {
|
||||
keyBytes, err := ioutil.ReadFile(file)
|
||||
keyBytes, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -224,11 +218,11 @@ func loadPrivateKey(file string) (crypto.PrivateKey, error) {
|
||||
return nil, errors.New("unknown private key type")
|
||||
}
|
||||
|
||||
func tryRecoverRegistration(ctx *cli.Context, privateKey crypto.PrivateKey) (*registration.Resource, error) {
|
||||
func tryRecoverRegistration(privateKey crypto.PrivateKey) (*registration.Resource, error) {
|
||||
// couldn't load account but got a key. Try to look the account up.
|
||||
config := lego.NewConfig(&Account{key: privateKey})
|
||||
config.CADirURL = ctx.GlobalString("server")
|
||||
config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version)
|
||||
config.CADirURL = acme.LetsEncryptURL
|
||||
config.UserAgent = "lego-cli/dev"
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
@@ -1,56 +1,45 @@
|
||||
package cmd
|
||||
package mylego
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/legocmd/log"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
const (
|
||||
baseCertificatesFolderName = "certificates"
|
||||
baseArchivesFolderName = "archives"
|
||||
)
|
||||
|
||||
// CertificatesStorage a certificates storage.
|
||||
// CertificatesStorage a certificates' storage.
|
||||
//
|
||||
// rootPath:
|
||||
//
|
||||
// ./.lego/certificates/
|
||||
// │ └── root certificates directory
|
||||
// └── "path" option
|
||||
// ./.lego/certificates/
|
||||
// │ └── root certificates directory
|
||||
// └── "path" option
|
||||
//
|
||||
// archivePath:
|
||||
//
|
||||
// ./.lego/archives/
|
||||
// │ └── archived certificates directory
|
||||
// └── "path" option
|
||||
//
|
||||
// ./.lego/archives/
|
||||
// │ └── archived certificates directory
|
||||
// └── "path" option
|
||||
type CertificatesStorage struct {
|
||||
rootPath string
|
||||
archivePath string
|
||||
pem bool
|
||||
filename string // Deprecated
|
||||
rootPath string
|
||||
pem bool
|
||||
}
|
||||
|
||||
// NewCertificatesStorage create a new certificates storage.
|
||||
func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage {
|
||||
func NewCertificatesStorage(path string) *CertificatesStorage {
|
||||
return &CertificatesStorage{
|
||||
rootPath: filepath.Join(ctx.GlobalString("path"), baseCertificatesFolderName),
|
||||
archivePath: filepath.Join(ctx.GlobalString("path"), baseArchivesFolderName),
|
||||
pem: ctx.GlobalBool("pem"),
|
||||
filename: ctx.GlobalString("filename"),
|
||||
rootPath: filepath.Join(path, baseCertificatesFolderName),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,13 +50,6 @@ func (s *CertificatesStorage) CreateRootFolder() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CertificatesStorage) CreateArchiveFolder() {
|
||||
err := createNonExistingFolder(s.archivePath)
|
||||
if err != nil {
|
||||
log.Panicf("Could not check/create path: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CertificatesStorage) GetRootPath() string {
|
||||
return s.rootPath
|
||||
}
|
||||
@@ -144,7 +126,7 @@ func (s *CertificatesStorage) ExistsFile(domain, extension string) bool {
|
||||
}
|
||||
|
||||
func (s *CertificatesStorage) ReadFile(domain, extension string) ([]byte, error) {
|
||||
return ioutil.ReadFile(s.GetFileName(domain, extension))
|
||||
return os.ReadFile(s.GetFileName(domain, extension))
|
||||
}
|
||||
|
||||
func (s *CertificatesStorage) GetFileName(domain, extension string) string {
|
||||
@@ -163,36 +145,11 @@ func (s *CertificatesStorage) ReadCertificate(domain, extension string) ([]*x509
|
||||
}
|
||||
|
||||
func (s *CertificatesStorage) WriteFile(domain, extension string, data []byte) error {
|
||||
var baseFileName string
|
||||
if s.filename != "" {
|
||||
baseFileName = s.filename
|
||||
} else {
|
||||
baseFileName = sanitizedDomain(domain)
|
||||
}
|
||||
var baseFileName = sanitizedDomain(domain)
|
||||
|
||||
filePath := filepath.Join(s.rootPath, baseFileName+extension)
|
||||
|
||||
return ioutil.WriteFile(filePath, data, filePerm)
|
||||
}
|
||||
|
||||
func (s *CertificatesStorage) MoveToArchive(domain string) error {
|
||||
matches, err := filepath.Glob(filepath.Join(s.rootPath, sanitizedDomain(domain)+".*"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, oldFile := range matches {
|
||||
date := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
filename := date + "." + filepath.Base(oldFile)
|
||||
newFile := filepath.Join(s.archivePath, filename)
|
||||
|
||||
err = os.Rename(oldFile, newFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return os.WriteFile(filePath, data, filePerm)
|
||||
}
|
||||
|
||||
// sanitizedDomain Make sure no funny chars are in the cert names (like wildcards ;)).
|
77
common/mylego/cmd_renew.go
Normal file
77
common/mylego/cmd_renew.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package mylego
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
)
|
||||
|
||||
func (l *LegoCMD) Renew() (bool, error) {
|
||||
account, client := setup(NewAccountsStorage(l))
|
||||
setupChallenges(l, client)
|
||||
|
||||
if account.Registration == nil {
|
||||
log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email)
|
||||
}
|
||||
|
||||
return renewForDomains(l.C.CertDomain, client, NewCertificatesStorage(l.path))
|
||||
}
|
||||
|
||||
func renewForDomains(domain string, client *lego.Client, certsStorage *CertificatesStorage) (bool, error) {
|
||||
// load the cert resource from files.
|
||||
// We store the certificate, private key and metadata in different files
|
||||
// as web servers would not be able to work with a combined file.
|
||||
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
|
||||
if err != nil {
|
||||
log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err)
|
||||
}
|
||||
|
||||
cert := certificates[0]
|
||||
|
||||
if !needRenewal(cert, domain, 30) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// This is just meant to be informal for the user.
|
||||
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
|
||||
log.Printf("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
|
||||
|
||||
certDomains := certcrypto.ExtractDomains(cert)
|
||||
|
||||
var privateKey crypto.PrivateKey
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: certDomains,
|
||||
Bundle: true,
|
||||
PrivateKey: privateKey,
|
||||
}
|
||||
certRes, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
certsStorage.SaveResource(certRes)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
|
||||
if x509Cert.IsCA {
|
||||
log.Panicf("[%s] Certificate bundle starts with a CA certificate", domain)
|
||||
}
|
||||
|
||||
if days >= 0 {
|
||||
notAfter := int(time.Until(x509Cert.NotAfter).Hours() / 24.0)
|
||||
if notAfter > days {
|
||||
log.Printf("[%s] The certificate expires in %d days, the number of days defined to perform the renewal is %d: no renewal.",
|
||||
domain, notAfter, days)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package mylego
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
@@ -116,3 +116,19 @@ func Test_needRenewal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func merge(prevDomains, nextDomains []string) []string {
|
||||
for _, next := range nextDomains {
|
||||
var found bool
|
||||
for _, prev := range prevDomains {
|
||||
if prev == next {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
prevDomains = append(prevDomains, next)
|
||||
}
|
||||
}
|
||||
return prevDomains
|
||||
}
|
68
common/mylego/cmd_run.go
Normal file
68
common/mylego/cmd_run.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package mylego
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
)
|
||||
|
||||
const rootPathWarningMessage = `!!!! HEADS UP !!!!
|
||||
|
||||
Your account credentials have been saved in your Let's Encrypt
|
||||
configuration directory at "%s".
|
||||
|
||||
You should make a secure backup of this folder now. This
|
||||
configuration directory will also contain certificates and
|
||||
private keys obtained from Let's Encrypt so making regular
|
||||
backups of this folder is ideal.
|
||||
`
|
||||
|
||||
func (l *LegoCMD) Run() error {
|
||||
accountsStorage := NewAccountsStorage(l)
|
||||
|
||||
account, client := setup(accountsStorage)
|
||||
setupChallenges(l, client)
|
||||
|
||||
if account.Registration == nil {
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
log.Panicf("Could not complete registration\n\t%v", err)
|
||||
}
|
||||
|
||||
account.Registration = reg
|
||||
if err = accountsStorage.Save(account); err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
|
||||
}
|
||||
|
||||
certsStorage := NewCertificatesStorage(l.path)
|
||||
certsStorage.CreateRootFolder()
|
||||
|
||||
cert, err := obtainCertificate([]string{l.C.CertDomain}, client)
|
||||
if err != nil {
|
||||
// Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error.
|
||||
// Due to us not returning partial certificate we can just exit here instead of at the end.
|
||||
log.Panicf("Could not obtain certificates:\n\t%v", err)
|
||||
}
|
||||
|
||||
certsStorage.SaveResource(cert)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func obtainCertificate(domains []string, client *lego.Client) (*certificate.Resource, error) {
|
||||
if len(domains) > 0 {
|
||||
// obtain a certificate, generating a new private key
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: domains,
|
||||
Bundle: true,
|
||||
}
|
||||
return client.Certificate.Obtain(request)
|
||||
}
|
||||
return nil, fmt.Errorf("not a valid domain")
|
||||
}
|
87
common/mylego/lego_test.go
Normal file
87
common/mylego/lego_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package mylego_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/XrayR-project/XrayR/common/mylego"
|
||||
)
|
||||
|
||||
func TestLegoClient(t *testing.T) {
|
||||
_, err := mylego.New(&mylego.CertConfig{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegoDNSCert(t *testing.T) {
|
||||
lego, err := mylego.New(&mylego.CertConfig{
|
||||
CertDomain: "node1.test.com",
|
||||
Provider: "alidns",
|
||||
Email: "test@gmail.com",
|
||||
DNSEnv: map[string]string{
|
||||
"ALICLOUD_ACCESS_KEY": "aaa",
|
||||
"ALICLOUD_SECRET_KEY": "bbb",
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
certPath, keyPath, err := lego.DNSCert()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(certPath)
|
||||
t.Log(keyPath)
|
||||
}
|
||||
|
||||
func TestLegoHTTPCert(t *testing.T) {
|
||||
lego, err := mylego.New(&mylego.CertConfig{
|
||||
CertMode: "http",
|
||||
CertDomain: "node1.test.com",
|
||||
Email: "test@gmail.com",
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
certPath, keyPath, err := lego.HTTPCert()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(certPath)
|
||||
t.Log(keyPath)
|
||||
}
|
||||
|
||||
func TestLegoRenewCert(t *testing.T) {
|
||||
lego, err := mylego.New(&mylego.CertConfig{
|
||||
CertDomain: "node1.test.com",
|
||||
Email: "test@gmail.com",
|
||||
Provider: "alidns",
|
||||
DNSEnv: map[string]string{
|
||||
"ALICLOUD_ACCESS_KEY": "aaa",
|
||||
"ALICLOUD_SECRET_KEY": "bbb",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
lego.C.CertMode = "http"
|
||||
certPath, keyPath, ok, err := lego.RenewCert()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(certPath)
|
||||
t.Log(keyPath)
|
||||
t.Log(ok)
|
||||
|
||||
lego.C.CertMode = "dns"
|
||||
certPath, keyPath, ok, err = lego.RenewCert()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log(certPath)
|
||||
t.Log(keyPath)
|
||||
t.Log(ok)
|
||||
}
|
17
common/mylego/model.go
Normal file
17
common/mylego/model.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package mylego
|
||||
|
||||
type CertConfig struct {
|
||||
CertMode string `mapstructure:"CertMode"` // none, file, http, dns
|
||||
CertDomain string `mapstructure:"CertDomain"`
|
||||
CertFile string `mapstructure:"CertFile"`
|
||||
KeyFile string `mapstructure:"KeyFile"`
|
||||
Provider string `mapstructure:"Provider"` // alidns, cloudflare, gandi, godaddy....
|
||||
Email string `mapstructure:"Email"`
|
||||
DNSEnv map[string]string `mapstructure:"DNSEnv"`
|
||||
RejectUnknownSni bool `mapstructure:"RejectUnknownSni"`
|
||||
}
|
||||
|
||||
type LegoCMD struct {
|
||||
C *CertConfig
|
||||
path string
|
||||
}
|
163
common/mylego/mylego.go
Normal file
163
common/mylego/mylego.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package mylego
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var defaultPath string
|
||||
|
||||
func New(certConf *CertConfig) (*LegoCMD, error) {
|
||||
// Set default path to configPath/cert
|
||||
var p = ""
|
||||
configPath := os.Getenv("XRAY_LOCATION_CONFIG")
|
||||
if configPath != "" {
|
||||
p = configPath
|
||||
} else if cwd, err := os.Getwd(); err == nil {
|
||||
p = cwd
|
||||
} else {
|
||||
p = "."
|
||||
}
|
||||
|
||||
defaultPath = filepath.Join(p, "cert")
|
||||
lego := &LegoCMD{
|
||||
C: certConf,
|
||||
path: defaultPath,
|
||||
}
|
||||
|
||||
return lego, nil
|
||||
}
|
||||
|
||||
func (l *LegoCMD) getPath() string {
|
||||
return l.path
|
||||
}
|
||||
|
||||
func (l *LegoCMD) getCertConfig() *CertConfig {
|
||||
return l.C
|
||||
}
|
||||
|
||||
// DNSCert cert a domain using DNS API
|
||||
func (l *LegoCMD) DNSCert() (CertPath string, KeyPath string, err error) {
|
||||
defer func() (string, string, error) {
|
||||
// Handle any error
|
||||
if r := recover(); r != nil {
|
||||
switch x := r.(type) {
|
||||
case string:
|
||||
err = errors.New(x)
|
||||
case error:
|
||||
err = x
|
||||
default:
|
||||
err = errors.New("unknown panic")
|
||||
}
|
||||
return "", "", err
|
||||
}
|
||||
return CertPath, KeyPath, nil
|
||||
}()
|
||||
|
||||
// Set Env for DNS configuration
|
||||
for key, value := range l.C.DNSEnv {
|
||||
os.Setenv(strings.ToUpper(key), value)
|
||||
}
|
||||
|
||||
// First check if the certificate exists
|
||||
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
|
||||
if err == nil {
|
||||
return CertPath, KeyPath, err
|
||||
}
|
||||
|
||||
err = l.Run()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return CertPath, KeyPath, nil
|
||||
}
|
||||
|
||||
// HTTPCert cert a domain using http methods
|
||||
func (l *LegoCMD) HTTPCert() (CertPath string, KeyPath string, err error) {
|
||||
defer func() (string, string, error) {
|
||||
// Handle any error
|
||||
if r := recover(); r != nil {
|
||||
switch x := r.(type) {
|
||||
case string:
|
||||
err = errors.New(x)
|
||||
case error:
|
||||
err = x
|
||||
default:
|
||||
err = errors.New("unknown panic")
|
||||
}
|
||||
return "", "", err
|
||||
}
|
||||
return CertPath, KeyPath, nil
|
||||
}()
|
||||
|
||||
// First check if the certificate exists
|
||||
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
|
||||
if err == nil {
|
||||
return CertPath, KeyPath, err
|
||||
}
|
||||
|
||||
err = l.Run()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return CertPath, KeyPath, nil
|
||||
}
|
||||
|
||||
// RenewCert renew a domain cert
|
||||
func (l *LegoCMD) RenewCert() (CertPath string, KeyPath string, ok bool, err error) {
|
||||
defer func() (string, string, bool, error) {
|
||||
// Handle any error
|
||||
if r := recover(); r != nil {
|
||||
switch x := r.(type) {
|
||||
case string:
|
||||
err = errors.New(x)
|
||||
case error:
|
||||
err = x
|
||||
default:
|
||||
err = errors.New("unknown panic")
|
||||
}
|
||||
return "", "", false, err
|
||||
}
|
||||
return CertPath, KeyPath, ok, nil
|
||||
}()
|
||||
|
||||
ok, err = l.Renew()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkCertFile(domain string) (string, string, error) {
|
||||
keyPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.key", domain))
|
||||
certPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.crt", domain))
|
||||
if _, err := os.Stat(keyPath); os.IsNotExist(err) {
|
||||
return "", "", fmt.Errorf("cert key failed: %s", domain)
|
||||
}
|
||||
if _, err := os.Stat(certPath); os.IsNotExist(err) {
|
||||
return "", "", fmt.Errorf("cert cert failed: %s", domain)
|
||||
}
|
||||
absKeyPath, _ := filepath.Abs(keyPath)
|
||||
absCertPath, _ := filepath.Abs(certPath)
|
||||
return absCertPath, absKeyPath, nil
|
||||
}
|
95
common/mylego/setup.go
Normal file
95
common/mylego/setup.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package mylego
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/challenge/http01"
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/providers/dns"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"golang.org/x/crypto/acme"
|
||||
)
|
||||
|
||||
const filePerm os.FileMode = 0o600
|
||||
|
||||
func setup(accountsStorage *AccountsStorage) (*Account, *lego.Client) {
|
||||
keyType := certcrypto.EC256
|
||||
privateKey := accountsStorage.GetPrivateKey(keyType)
|
||||
|
||||
var account *Account
|
||||
if accountsStorage.ExistsAccountFilePath() {
|
||||
account = accountsStorage.LoadAccount(privateKey)
|
||||
} else {
|
||||
account = &Account{Email: accountsStorage.GetUserID(), key: privateKey}
|
||||
}
|
||||
|
||||
client := newClient(account, keyType)
|
||||
|
||||
return account, client
|
||||
}
|
||||
|
||||
func newClient(acc registration.User, keyType certcrypto.KeyType) *lego.Client {
|
||||
config := lego.NewConfig(acc)
|
||||
config.CADirURL = acme.LetsEncryptURL
|
||||
|
||||
config.Certificate = lego.CertificateConfig{
|
||||
KeyType: keyType,
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
config.UserAgent = "lego-cli/dev"
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
log.Panicf("Could not create client: %v", err)
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func createNonExistingFolder(path string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return os.MkdirAll(path, 0o700)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupChallenges(l *LegoCMD, client *lego.Client) {
|
||||
switch l.C.CertMode {
|
||||
case "http":
|
||||
err := client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5001"))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
case "tls":
|
||||
err := client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5002"))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
case "dns":
|
||||
setupDNS(l.C.Provider, client)
|
||||
default:
|
||||
log.Panic("No challenge selected. You must specify at least one challenge: `http`, `tls`, `dns`.")
|
||||
}
|
||||
}
|
||||
|
||||
func setupDNS(p string, client *lego.Client) {
|
||||
provider, err := dns.NewDNSChallengeProviderByName(p)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
err = client.Challenge.SetDNS01Provider(
|
||||
provider,
|
||||
dns01.CondOption(true, dns01.AddDNSTimeout(10*time.Second)),
|
||||
)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
@@ -8,23 +8,24 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
type RuleManager struct {
|
||||
type Manager struct {
|
||||
InboundRule *sync.Map // Key: Tag, Value: []api.DetectRule
|
||||
InboundDetectResult *sync.Map // key: Tag, Value: mapset.NewSet []api.DetectResult
|
||||
}
|
||||
|
||||
func New() *RuleManager {
|
||||
return &RuleManager{
|
||||
func New() *Manager {
|
||||
return &Manager{
|
||||
InboundRule: new(sync.Map),
|
||||
InboundDetectResult: new(sync.Map),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuleManager) UpdateRule(tag string, newRuleList []api.DetectRule) error {
|
||||
func (r *Manager) UpdateRule(tag string, newRuleList []api.DetectRule) error {
|
||||
if value, ok := r.InboundRule.LoadOrStore(tag, newRuleList); ok {
|
||||
oldRuleList := value.([]api.DetectRule)
|
||||
if !reflect.DeepEqual(oldRuleList, newRuleList) {
|
||||
@@ -34,7 +35,7 @@ func (r *RuleManager) UpdateRule(tag string, newRuleList []api.DetectRule) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RuleManager) GetDetectResult(tag string) (*[]api.DetectResult, error) {
|
||||
func (r *Manager) GetDetectResult(tag string) (*[]api.DetectResult, error) {
|
||||
detectResult := make([]api.DetectResult, 0)
|
||||
if value, ok := r.InboundDetectResult.LoadAndDelete(tag); ok {
|
||||
resultSet := value.(mapset.Set)
|
||||
@@ -46,9 +47,9 @@ func (r *RuleManager) GetDetectResult(tag string) (*[]api.DetectResult, error) {
|
||||
return &detectResult, nil
|
||||
}
|
||||
|
||||
func (r *RuleManager) Detect(tag string, destination string, email string) (reject bool) {
|
||||
func (r *Manager) Detect(tag string, destination string, email string) (reject bool) {
|
||||
reject = false
|
||||
var hitRuleID int = -1
|
||||
var hitRuleID = -1
|
||||
// If we have some rule for this inbound
|
||||
if value, ok := r.InboundRule.Load(tag); ok {
|
||||
ruleList := value.([]api.DetectRule)
|
||||
|
@@ -13,7 +13,7 @@ import (
|
||||
// GetSystemInfo get the system info of a given periodic
|
||||
func GetSystemInfo() (Cpu float64, Mem float64, Disk float64, Uptime uint64, err error) {
|
||||
|
||||
error_string := ""
|
||||
errorString := ""
|
||||
|
||||
cpuPercent, err := cpu.Percent(0, false)
|
||||
// Check if cpuPercent is empty
|
||||
@@ -21,32 +21,32 @@ func GetSystemInfo() (Cpu float64, Mem float64, Disk float64, Uptime uint64, err
|
||||
Cpu = cpuPercent[0]
|
||||
} else {
|
||||
Cpu = 0
|
||||
error_string += fmt.Sprintf("get cpu usage failed: %s ", err)
|
||||
errorString += fmt.Sprintf("get cpu usage failed: %s ", err)
|
||||
}
|
||||
|
||||
memUsage, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
error_string += fmt.Sprintf("get mem usage failed: %s ", err)
|
||||
errorString += fmt.Sprintf("get mem usage failed: %s ", err)
|
||||
} else {
|
||||
Mem = memUsage.UsedPercent
|
||||
}
|
||||
|
||||
diskUsage, err := disk.Usage("/")
|
||||
if err != nil {
|
||||
error_string += fmt.Sprintf("get disk usage failed: %s ", err)
|
||||
errorString += fmt.Sprintf("get disk usage failed: %s ", err)
|
||||
} else {
|
||||
Disk = diskUsage.UsedPercent
|
||||
}
|
||||
|
||||
uptime, err := host.Uptime()
|
||||
if err != nil {
|
||||
error_string += fmt.Sprintf("get uptime failed: %s ", err)
|
||||
errorString += fmt.Sprintf("get uptime failed: %s ", err)
|
||||
} else {
|
||||
Uptime = uptime
|
||||
}
|
||||
|
||||
if error_string != "" {
|
||||
err = fmt.Errorf(error_string)
|
||||
if errorString != "" {
|
||||
err = fmt.Errorf(errorString)
|
||||
}
|
||||
|
||||
return Cpu, Mem, Disk, Uptime, err
|
||||
|
145
go.mod
145
go.mod
@@ -1,98 +1,103 @@
|
||||
module github.com/XrayR-project/XrayR
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/bitly/go-simplejson v0.5.0
|
||||
github.com/deckarep/golang-set v1.8.0
|
||||
github.com/fsnotify/fsnotify v1.5.4
|
||||
github.com/go-acme/lego/v4 v4.8.0
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-acme/lego/v4 v4.9.0
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
github.com/imdario/mergo v0.3.13
|
||||
github.com/juju/ratelimit v1.0.2
|
||||
github.com/r3labs/diff/v2 v2.15.1
|
||||
github.com/shirou/gopsutil/v3 v3.22.8
|
||||
github.com/spf13/viper v1.13.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/urfave/cli v1.22.10
|
||||
github.com/xtls/xray-core v1.5.10
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
|
||||
github.com/shirou/gopsutil/v3 v3.22.10
|
||||
github.com/spf13/viper v1.14.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/xtls/xray-core v1.6.3
|
||||
golang.org/x/crypto v0.2.0
|
||||
golang.org/x/net v0.2.0
|
||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
|
||||
google.golang.org/protobuf v1.28.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.6.1 // indirect
|
||||
cloud.google.com/go/compute v1.12.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.19 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
|
||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.20.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/civo/civogo v0.3.11 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.49.0 // indirect
|
||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.6.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/dnsimple/dnsimple-go v0.70.1 // indirect
|
||||
github.com/exoscale/egoscale v0.67.0 // indirect
|
||||
github.com/dnsimple/dnsimple-go v0.71.1 // indirect
|
||||
github.com/exoscale/egoscale v0.90.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gofrs/uuid v3.2.0+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
|
||||
github.com/gophercloud/gophercloud v0.16.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.6.0 // indirect
|
||||
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
|
||||
github.com/jarcoal/httpmock v1.0.8 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.2 // indirect
|
||||
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||
github.com/linode/linodego v0.31.1 // indirect
|
||||
github.com/linode/linodego v1.9.1 // indirect
|
||||
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.29.0 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.30.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/mimuret/golang-iij-dpf v0.7.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
@@ -100,15 +105,14 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||
github.com/nrdcg/auroradns v1.0.1 // indirect
|
||||
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||
github.com/nrdcg/desec v0.6.0 // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.2.0 // indirect
|
||||
github.com/nrdcg/goinwx v0.8.1 // indirect
|
||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||
github.com/nrdcg/porkbun v0.1.1 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.4.0 // indirect
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
||||
github.com/ovh/go-ovh v1.1.0 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
@@ -119,61 +123,56 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/pquerna/otp v1.3.0 // indirect
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2 // indirect
|
||||
github.com/refraction-networking/utls v1.1.1 // indirect
|
||||
github.com/refraction-networking/utls v1.1.5 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/sacloud/libsacloud v1.36.2 // indirect
|
||||
github.com/sacloud/api-client-go v0.2.1 // indirect
|
||||
github.com/sacloud/go-http v0.1.2 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.3.2 // indirect
|
||||
github.com/sacloud/packages-go v0.0.5 // indirect
|
||||
github.com/sagernet/sing v0.0.0-20220801112236-1bb95f9661fc // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 // indirect
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/softlayer/softlayer-go v1.0.3 // indirect
|
||||
github.com/softlayer/softlayer-go v1.0.6 // indirect
|
||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/afero v1.9.2 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.4.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.287 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.287 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/transip/gotransip/v6 v6.6.1 // indirect
|
||||
github.com/transip/gotransip/v6 v6.17.0 // indirect
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/vultr/govultr/v2 v2.16.0 // indirect
|
||||
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 // indirect
|
||||
github.com/vultr/govultr/v2 v2.17.2 // indirect
|
||||
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.starlark.net v0.0.0-20220817180228-f738f5508c12 // indirect
|
||||
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect
|
||||
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
google.golang.org/api v0.81.0 // indirect
|
||||
go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 // indirect
|
||||
go.uber.org/ratelimit v0.2.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221106115401-f9659909a136 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/api v0.102.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
|
||||
google.golang.org/grpc v1.49.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.6.2 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.6.5 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.1.7 // indirect
|
||||
)
|
||||
|
||||
replace github.com/linode/linodego => github.com/linode/linodego v0.31.1
|
||||
|
||||
replace github.com/exoscale/egoscale => github.com/exoscale/egoscale v0.67.0
|
||||
|
@@ -6,7 +6,7 @@ DnsConfigPath: # /etc/XrayR/dns.json # Path to dns config, check https://xtls.gi
|
||||
RouteConfigPath: # /etc/XrayR/route.json # Path to route config, check https://xtls.github.io/config/routing.html for help
|
||||
InboundConfigPath: # /etc/XrayR/custom_inbound.json # Path to custom inbound config, check https://xtls.github.io/config/inbound.html for help
|
||||
OutboundConfigPath: # /etc/XrayR/custom_outbound.json # Path to custom outbound config, check https://xtls.github.io/config/outbound.html for help
|
||||
ConnetionConfig:
|
||||
ConnectionConfig:
|
||||
Handshake: 4 # Handshake time limit, Second
|
||||
ConnIdle: 30 # Connection idle time limit, Second
|
||||
UplinkOnly: 2 # Time limit when the connection downstream is closed, Second
|
||||
@@ -33,6 +33,18 @@ Nodes:
|
||||
EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
||||
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
|
||||
EnableProxyProtocol: false # Only works for WebSocket and TCP
|
||||
AutoSpeedLimitConfig:
|
||||
Limit: 0 # Warned speed. Set to 0 to disable AutoSpeedLimit (mbps)
|
||||
WarnTimes: 0 # After (WarnTimes) consecutive warnings, the user will be limited. Set to 0 to punish overspeed user immediately.
|
||||
LimitSpeed: 0 # The speedlimit of a limited user (unit: mbps)
|
||||
LimitDuration: 0 # How many minutes will the limiting last (unit: minute)
|
||||
GlobalDeviceLimitConfig:
|
||||
Limit: 0 # The global device limit of a user, 0 means disable
|
||||
RedisAddr: 127.0.0.1:6379 # The redis server address
|
||||
RedisPassword: YOUR PASSWORD # Redis password
|
||||
RedisDB: 0 # Redis DB
|
||||
Timeout: 5 # Timeout for redis request
|
||||
Expiry: 60 # Expiry time (second)
|
||||
EnableFallback: false # Only support for Trojan and Vless
|
||||
FallBackConfigs: # Support multiple fallbacks
|
||||
-
|
||||
|
@@ -1,19 +1,19 @@
|
||||
[
|
||||
{
|
||||
"listen": "0.0.0.0",
|
||||
"port": 1234,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "noauth",
|
||||
"accounts": [
|
||||
{
|
||||
"user": "my-username",
|
||||
"pass": "my-password"
|
||||
}
|
||||
],
|
||||
"udp": false,
|
||||
"ip": "127.0.0.1",
|
||||
"userLevel": 0
|
||||
{
|
||||
"listen": "0.0.0.0",
|
||||
"port": 1234,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "noauth",
|
||||
"accounts": [
|
||||
{
|
||||
"user": "my-username",
|
||||
"pass": "my-password"
|
||||
}
|
||||
],
|
||||
"udp": false,
|
||||
"ip": "127.0.0.1",
|
||||
"userLevel": 0
|
||||
}
|
||||
}
|
||||
]
|
@@ -1,28 +1,30 @@
|
||||
[
|
||||
{
|
||||
"tag": "IPv4_out",
|
||||
"protocol": "freedom",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "IPv6_out",
|
||||
"protocol": "freedom",
|
||||
"settings": {
|
||||
"domainStrategy": "UseIPv6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "socks5-warp",
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"servers": [{
|
||||
"address": "127.0.0.1",
|
||||
"port": 1080
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"protocol": "blackhole",
|
||||
"tag": "block"
|
||||
{
|
||||
"tag": "IPv4_out",
|
||||
"protocol": "freedom",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "IPv6_out",
|
||||
"protocol": "freedom",
|
||||
"settings": {
|
||||
"domainStrategy": "UseIPv6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "socks5-warp",
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"port": 1080
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"protocol": "blackhole",
|
||||
"tag": "block"
|
||||
}
|
||||
]
|
@@ -3,11 +3,12 @@ package all
|
||||
import (
|
||||
// The following are necessary as they register handlers in their init functions.
|
||||
|
||||
_ "github.com/xtls/xray-core/app/proxyman/inbound"
|
||||
_ "github.com/xtls/xray-core/app/proxyman/outbound"
|
||||
|
||||
// Required features. Can't remove unless there is replacements.
|
||||
// _ "github.com/xtls/xray-core/app/dispatcher"
|
||||
_ "github.com/XrayR-project/XrayR/app/mydispatcher"
|
||||
_ "github.com/xtls/xray-core/app/proxyman/inbound"
|
||||
_ "github.com/xtls/xray-core/app/proxyman/outbound"
|
||||
|
||||
// Default commander and all its services. This is an optional feature.
|
||||
_ "github.com/xtls/xray-core/app/commander"
|
||||
|
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"servers": [
|
||||
"1.1.1.1",
|
||||
"8.8.8.8",
|
||||
"localhost"
|
||||
],
|
||||
"tag": "dns_inbound"
|
||||
"servers": [
|
||||
"1.1.1.1",
|
||||
"8.8.8.8",
|
||||
"localhost"
|
||||
],
|
||||
"tag": "dns_inbound"
|
||||
}
|
11
main/main.go
11
main/main.go
@@ -12,9 +12,10 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/panel"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/XrayR-project/XrayR/panel"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -23,7 +24,7 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "0.8.3"
|
||||
version = "0.8.6"
|
||||
codename = "XrayR"
|
||||
intro = "A Xray backend that supports many panels"
|
||||
)
|
||||
@@ -74,7 +75,7 @@ func main() {
|
||||
config := getConfig()
|
||||
panelConfig := &panel.Config{}
|
||||
if err := config.Unmarshal(panelConfig); err != nil {
|
||||
log.Panicf("Parse config file %s failed: %s \n", configFile, err)
|
||||
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
|
||||
}
|
||||
p := panel.New(panelConfig)
|
||||
lastTime := time.Now()
|
||||
@@ -87,7 +88,7 @@ func main() {
|
||||
// Delete old instance and trigger GC
|
||||
runtime.GC()
|
||||
if err := config.Unmarshal(panelConfig); err != nil {
|
||||
log.Panicf("Parse config file %s failed: %s \n", configFile, err)
|
||||
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
|
||||
}
|
||||
p.Start()
|
||||
lastTime = time.Now()
|
||||
@@ -96,7 +97,7 @@ func main() {
|
||||
p.Start()
|
||||
defer p.Close()
|
||||
|
||||
//Explicitly triggering GC to remove garbage from config loading.
|
||||
// Explicitly triggering GC to remove garbage from config loading.
|
||||
runtime.GC()
|
||||
// Running backend
|
||||
{
|
||||
|
@@ -1,36 +1,36 @@
|
||||
{
|
||||
"domainStrategy": "IPOnDemand",
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "block",
|
||||
"ip": [
|
||||
"geoip:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "block",
|
||||
"protocol": [
|
||||
"bittorrent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "socks5-warp",
|
||||
"domain": []
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "IPv6_out",
|
||||
"domain": [
|
||||
"geosite:netflix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "IPv4_out",
|
||||
"network": "udp,tcp"
|
||||
}
|
||||
]
|
||||
"domainStrategy": "IPOnDemand",
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "block",
|
||||
"ip": [
|
||||
"geoip:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "block",
|
||||
"protocol": [
|
||||
"bittorrent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "socks5-warp",
|
||||
"domain": []
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "IPv6_out",
|
||||
"domain": [
|
||||
"geosite:netflix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "IPv4_out",
|
||||
"network": "udp,tcp"
|
||||
}
|
||||
]
|
||||
}
|
@@ -6,13 +6,13 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
LogConfig *LogConfig `mapstructure:"Log"`
|
||||
DnsConfigPath string `mapstructure:"DnsConfigPath"`
|
||||
InboundConfigPath string `mapstructure:"InboundConfigPath"`
|
||||
OutboundConfigPath string `mapstructure:"OutboundConfigPath"`
|
||||
RouteConfigPath string `mapstructure:"RouteConfigPath"`
|
||||
ConnetionConfig *ConnetionConfig `mapstructure:"ConnetionConfig"`
|
||||
NodesConfig []*NodesConfig `mapstructure:"Nodes"`
|
||||
LogConfig *LogConfig `mapstructure:"Log"`
|
||||
DnsConfigPath string `mapstructure:"DnsConfigPath"`
|
||||
InboundConfigPath string `mapstructure:"InboundConfigPath"`
|
||||
OutboundConfigPath string `mapstructure:"OutboundConfigPath"`
|
||||
RouteConfigPath string `mapstructure:"RouteConfigPath"`
|
||||
ConnectionConfig *ConnectionConfig `mapstructure:"ConnectionConfig"`
|
||||
NodesConfig []*NodesConfig `mapstructure:"Nodes"`
|
||||
}
|
||||
|
||||
type NodesConfig struct {
|
||||
@@ -27,7 +27,7 @@ type LogConfig struct {
|
||||
ErrorPath string `mapstructure:"ErrorPath"`
|
||||
}
|
||||
|
||||
type ConnetionConfig struct {
|
||||
type ConnectionConfig struct {
|
||||
Handshake uint32 `mapstructure:"handshake"`
|
||||
ConnIdle uint32 `mapstructure:"connIdle"`
|
||||
UplinkOnly uint32 `mapstructure:"uplinkOnly"`
|
||||
|
@@ -10,8 +10,8 @@ func getDefaultLogConfig() *LogConfig {
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultConnetionConfig() *ConnetionConfig {
|
||||
return &ConnetionConfig{
|
||||
func getDefaultConnectionConfig() *ConnectionConfig {
|
||||
return &ConnectionConfig{
|
||||
Handshake: 4,
|
||||
ConnIdle: 30,
|
||||
UplinkOnly: 2,
|
||||
|
@@ -2,12 +2,20 @@ package panel
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
io "io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/r3labs/diff/v2"
|
||||
"github.com/xtls/xray-core/app/proxyman"
|
||||
"github.com/xtls/xray-core/app/stats"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/api/pmpanel"
|
||||
"github.com/XrayR-project/XrayR/api/proxypanel"
|
||||
@@ -17,13 +25,6 @@ import (
|
||||
_ "github.com/XrayR-project/XrayR/main/distro/all"
|
||||
"github.com/XrayR-project/XrayR/service"
|
||||
"github.com/XrayR-project/XrayR/service/controller"
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/r3labs/diff/v2"
|
||||
"github.com/xtls/xray-core/app/proxyman"
|
||||
"github.com/xtls/xray-core/app/stats"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
)
|
||||
|
||||
// Panel Structure
|
||||
@@ -56,7 +57,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
|
||||
// DNS config
|
||||
coreDnsConfig := &conf.DNSConfig{}
|
||||
if panelConfig.DnsConfigPath != "" {
|
||||
if data, err := io.ReadFile(panelConfig.DnsConfigPath); err != nil {
|
||||
if data, err := os.ReadFile(panelConfig.DnsConfigPath); err != nil {
|
||||
log.Panicf("Failed to read DNS config file at: %s", panelConfig.DnsConfigPath)
|
||||
} else {
|
||||
if err = json.Unmarshal(data, coreDnsConfig); err != nil {
|
||||
@@ -71,7 +72,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
|
||||
// Routing config
|
||||
coreRouterConfig := &conf.RouterConfig{}
|
||||
if panelConfig.RouteConfigPath != "" {
|
||||
if data, err := io.ReadFile(panelConfig.RouteConfigPath); err != nil {
|
||||
if data, err := os.ReadFile(panelConfig.RouteConfigPath); err != nil {
|
||||
log.Panicf("Failed to read Routing config file at: %s", panelConfig.RouteConfigPath)
|
||||
} else {
|
||||
if err = json.Unmarshal(data, coreRouterConfig); err != nil {
|
||||
@@ -84,9 +85,9 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
|
||||
log.Panicf("Failed to understand Routing config Please check: https://xtls.github.io/config/routing.html for help: %s", err)
|
||||
}
|
||||
// Custom Inbound config
|
||||
coreCustomInboundConfig := []conf.InboundDetourConfig{}
|
||||
var coreCustomInboundConfig []conf.InboundDetourConfig
|
||||
if panelConfig.InboundConfigPath != "" {
|
||||
if data, err := io.ReadFile(panelConfig.InboundConfigPath); err != nil {
|
||||
if data, err := os.ReadFile(panelConfig.InboundConfigPath); err != nil {
|
||||
log.Panicf("Failed to read Custom Inbound config file at: %s", panelConfig.OutboundConfigPath)
|
||||
} else {
|
||||
if err = json.Unmarshal(data, &coreCustomInboundConfig); err != nil {
|
||||
@@ -94,7 +95,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
|
||||
}
|
||||
}
|
||||
}
|
||||
inBoundConfig := []*core.InboundHandlerConfig{}
|
||||
var inBoundConfig []*core.InboundHandlerConfig
|
||||
for _, config := range coreCustomInboundConfig {
|
||||
oc, err := config.Build()
|
||||
if err != nil {
|
||||
@@ -103,9 +104,9 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
|
||||
inBoundConfig = append(inBoundConfig, oc)
|
||||
}
|
||||
// Custom Outbound config
|
||||
coreCustomOutboundConfig := []conf.OutboundDetourConfig{}
|
||||
var coreCustomOutboundConfig []conf.OutboundDetourConfig
|
||||
if panelConfig.OutboundConfigPath != "" {
|
||||
if data, err := io.ReadFile(panelConfig.OutboundConfigPath); err != nil {
|
||||
if data, err := os.ReadFile(panelConfig.OutboundConfigPath); err != nil {
|
||||
log.Panicf("Failed to read Custom Outbound config file at: %s", panelConfig.OutboundConfigPath)
|
||||
} else {
|
||||
if err = json.Unmarshal(data, &coreCustomOutboundConfig); err != nil {
|
||||
@@ -113,7 +114,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
|
||||
}
|
||||
}
|
||||
}
|
||||
outBoundConfig := []*core.OutboundHandlerConfig{}
|
||||
var outBoundConfig []*core.OutboundHandlerConfig
|
||||
for _, config := range coreCustomOutboundConfig {
|
||||
oc, err := config.Build()
|
||||
if err != nil {
|
||||
@@ -122,7 +123,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
|
||||
outBoundConfig = append(outBoundConfig, oc)
|
||||
}
|
||||
// Policy config
|
||||
levelPolicyConfig := parseConnectionConfig(panelConfig.ConnetionConfig)
|
||||
levelPolicyConfig := parseConnectionConfig(panelConfig.ConnectionConfig)
|
||||
corePolicyConfig := &conf.PolicyConfig{}
|
||||
corePolicyConfig.Levels = map[uint32]*conf.Policy{0: levelPolicyConfig}
|
||||
policyConfig, _ := corePolicyConfig.Build()
|
||||
@@ -218,11 +219,11 @@ func (p *Panel) Close() {
|
||||
return
|
||||
}
|
||||
|
||||
func parseConnectionConfig(c *ConnetionConfig) (policy *conf.Policy) {
|
||||
connetionConfig := getDefaultConnetionConfig()
|
||||
func parseConnectionConfig(c *ConnectionConfig) (policy *conf.Policy) {
|
||||
connetionConfig := getDefaultConnectionConfig()
|
||||
if c != nil {
|
||||
if _, err := diff.Merge(connetionConfig, c, connetionConfig); err != nil {
|
||||
log.Panicf("Read ConnetionConfig failed: %s", err)
|
||||
log.Panicf("Read ConnectionConfig failed: %s", err)
|
||||
}
|
||||
}
|
||||
policy = &conf.Policy{
|
||||
|
@@ -1,30 +1,33 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/XrayR-project/XrayR/common/limiter"
|
||||
"github.com/XrayR-project/XrayR/common/mylego"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ListenIP string `mapstructure:"ListenIP"`
|
||||
SendIP string `mapstructure:"SendIP"`
|
||||
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
|
||||
CertConfig *CertConfig `mapstructure:"CertConfig"`
|
||||
EnableDNS bool `mapstructure:"EnableDNS"`
|
||||
DNSType string `mapstructure:"DNSType"`
|
||||
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
|
||||
DisableGetRule bool `mapstructure:"DisableGetRule"`
|
||||
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
|
||||
EnableFallback bool `mapstructure:"EnableFallback"`
|
||||
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
|
||||
DisableSniffing bool `mapstructure:"DisableSniffing"`
|
||||
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
|
||||
ListenIP string `mapstructure:"ListenIP"`
|
||||
SendIP string `mapstructure:"SendIP"`
|
||||
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
|
||||
CertConfig *mylego.CertConfig `mapstructure:"CertConfig"`
|
||||
EnableDNS bool `mapstructure:"EnableDNS"`
|
||||
DNSType string `mapstructure:"DNSType"`
|
||||
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
|
||||
DisableGetRule bool `mapstructure:"DisableGetRule"`
|
||||
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
|
||||
EnableFallback bool `mapstructure:"EnableFallback"`
|
||||
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
|
||||
DisableSniffing bool `mapstructure:"DisableSniffing"`
|
||||
AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"`
|
||||
GlobalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig `mapstructure:"GlobalDeviceLimitConfig"`
|
||||
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
|
||||
}
|
||||
|
||||
type CertConfig struct {
|
||||
CertMode string `mapstructure:"CertMode"` // none, file, http, dns
|
||||
RejectUnknownSni bool `mapstructure:"RejectUnknownSni"`
|
||||
CertDomain string `mapstructure:"CertDomain"`
|
||||
CertFile string `mapstructure:"CertFile"`
|
||||
KeyFile string `mapstructure:"KeyFile"`
|
||||
Provider string `mapstructure:"Provider"` // alidns, cloudflare, gandi, godaddy....
|
||||
Email string `mapstructure:"Email"`
|
||||
DNSEnv map[string]string `mapstructure:"DNSEnv"`
|
||||
type AutoSpeedLimitConfig struct {
|
||||
Limit int `mapstructure:"Limit"` // mbps
|
||||
WarnTimes int `mapstructure:"WarnTimes"`
|
||||
LimitSpeed int `mapstructure:"LimitSpeed"` // mbps
|
||||
LimitDuration int `mapstructure:"LimitDuration"` // minute
|
||||
}
|
||||
|
||||
type FallBackConfig struct {
|
||||
|
@@ -4,13 +4,15 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/inbound"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"github.com/xtls/xray-core/features/stats"
|
||||
"github.com/xtls/xray-core/proxy"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/common/limiter"
|
||||
)
|
||||
|
||||
func (c *Controller) removeInbound(tag string) error {
|
||||
@@ -56,7 +58,7 @@ func (c *Controller) addOutbound(config *core.OutboundHandlerConfig) error {
|
||||
func (c *Controller) addUsers(users []*protocol.User, tag string) error {
|
||||
handler, err := c.ihm.GetHandler(context.Background(), tag)
|
||||
if err != nil {
|
||||
return fmt.Errorf("No such inbound tag: %s", err)
|
||||
return fmt.Errorf("no such inbound tag: %s", err)
|
||||
}
|
||||
inboundInstance, ok := handler.(proxy.GetInbound)
|
||||
if !ok {
|
||||
@@ -83,7 +85,7 @@ func (c *Controller) addUsers(users []*protocol.User, tag string) error {
|
||||
func (c *Controller) removeUsers(users []string, tag string) error {
|
||||
handler, err := c.ihm.GetHandler(context.Background(), tag)
|
||||
if err != nil {
|
||||
return fmt.Errorf("No such inbound tag: %s", err)
|
||||
return fmt.Errorf("no such inbound tag: %s", err)
|
||||
}
|
||||
inboundInstance, ok := handler.(proxy.GetInbound)
|
||||
if !ok {
|
||||
@@ -130,8 +132,8 @@ func (c *Controller) resetTraffic(upCounterList *[]stats.Counter, downCounterLis
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo) error {
|
||||
err := c.dispatcher.Limiter.AddInboundLimiter(tag, nodeSpeedLimit, userList)
|
||||
func (c *Controller) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo, globalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig) error {
|
||||
err := c.dispatcher.Limiter.AddInboundLimiter(tag, nodeSpeedLimit, userList, globalDeviceLimitConfig)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@@ -6,10 +6,6 @@ import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
||||
"github.com/XrayR-project/XrayR/common/legocmd"
|
||||
"github.com/XrayR-project/XrayR/common/serverstatus"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/core"
|
||||
@@ -17,8 +13,20 @@ import (
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/features/stats"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
||||
"github.com/XrayR-project/XrayR/common/limiter"
|
||||
"github.com/XrayR-project/XrayR/common/mylego"
|
||||
"github.com/XrayR-project/XrayR/common/serverstatus"
|
||||
)
|
||||
|
||||
type LimitInfo struct {
|
||||
end int64
|
||||
currentSpeedLimit int
|
||||
originSpeedLimit uint64
|
||||
}
|
||||
|
||||
type Controller struct {
|
||||
server *core.Instance
|
||||
config *Config
|
||||
@@ -29,11 +37,14 @@ type Controller struct {
|
||||
userList *[]api.UserInfo
|
||||
nodeInfoMonitorPeriodic *task.Periodic
|
||||
userReportPeriodic *task.Periodic
|
||||
limitedUsers map[api.UserInfo]LimitInfo
|
||||
warnedUsers map[api.UserInfo]int
|
||||
panelType string
|
||||
ihm inbound.Manager
|
||||
ohm outbound.Manager
|
||||
stm stats.Manager
|
||||
dispatcher *mydispatcher.DefaultDispatcher
|
||||
startAt time.Time
|
||||
}
|
||||
|
||||
// New return a Controller service with default parameters.
|
||||
@@ -47,6 +58,7 @@ func New(server *core.Instance, api api.API, config *Config, panelType string) *
|
||||
ohm: server.GetFeature(outbound.ManagerType()).(outbound.Manager),
|
||||
stm: server.GetFeature(stats.ManagerType()).(stats.Manager),
|
||||
dispatcher: server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher),
|
||||
startAt: time.Now(),
|
||||
}
|
||||
return controller
|
||||
}
|
||||
@@ -77,11 +89,15 @@ func (c *Controller) Start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//sync controller userList
|
||||
// sync controller userList
|
||||
c.userList = userInfo
|
||||
|
||||
// Init global device limit
|
||||
if c.config.GlobalDeviceLimitConfig == nil {
|
||||
c.config.GlobalDeviceLimitConfig = &limiter.GlobalDeviceLimitConfig{Limit: 0}
|
||||
}
|
||||
// Add Limiter
|
||||
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo); err != nil {
|
||||
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo, c.config.GlobalDeviceLimitConfig); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
// Add Rule Manager
|
||||
@@ -102,19 +118,22 @@ func (c *Controller) Start() error {
|
||||
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
|
||||
Execute: c.userInfoMonitor,
|
||||
}
|
||||
log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.NodeType, c.nodeInfo.NodeID)
|
||||
// delay to start nodeInfoMonitor
|
||||
go func() {
|
||||
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
|
||||
_ = c.nodeInfoMonitorPeriodic.Start()
|
||||
}()
|
||||
if c.config.AutoSpeedLimitConfig == nil {
|
||||
c.config.AutoSpeedLimitConfig = &AutoSpeedLimitConfig{0, 0, 0, 0}
|
||||
}
|
||||
if c.config.AutoSpeedLimitConfig.Limit > 0 {
|
||||
c.limitedUsers = make(map[api.UserInfo]LimitInfo)
|
||||
c.warnedUsers = make(map[api.UserInfo]int)
|
||||
}
|
||||
|
||||
// start nodeInfoMonitor
|
||||
log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.NodeType, c.nodeInfo.NodeID)
|
||||
go c.nodeInfoMonitorPeriodic.Start()
|
||||
|
||||
// start userReport
|
||||
log.Printf("[%s: %d] Start report node status", c.nodeInfo.NodeType, c.nodeInfo.NodeID)
|
||||
// delay to start userReport
|
||||
go func() {
|
||||
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
|
||||
_ = c.userReportPeriodic.Start()
|
||||
}()
|
||||
go c.userReportPeriodic.Start()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -137,6 +156,11 @@ func (c *Controller) Close() error {
|
||||
}
|
||||
|
||||
func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
// delay to start
|
||||
if time.Since(c.startAt) < time.Duration(c.config.UpdatePeriodic)*time.Second {
|
||||
return nil
|
||||
}
|
||||
|
||||
// First fetch Node Info
|
||||
newNodeInfo, err := c.apiClient.GetNodeInfo()
|
||||
if err != nil {
|
||||
@@ -155,8 +179,8 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
// If nodeInfo changed
|
||||
if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) {
|
||||
// Remove old tag
|
||||
oldtag := c.Tag
|
||||
err := c.removeOldTag(oldtag)
|
||||
oldTag := c.Tag
|
||||
err := c.removeOldTag(oldTag)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
@@ -178,7 +202,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
}
|
||||
nodeInfoChanged = true
|
||||
// Remove Old limiter
|
||||
if err = c.DeleteInboundLimiter(oldtag); err != nil {
|
||||
if err = c.DeleteInboundLimiter(oldTag); err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
@@ -197,12 +221,12 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
|
||||
// Check Cert
|
||||
if c.nodeInfo.EnableTLS && (c.config.CertConfig.CertMode == "dns" || c.config.CertConfig.CertMode == "http") {
|
||||
lego, err := legocmd.New()
|
||||
lego, err := mylego.New(c.config.CertConfig)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
// Xray-core supports the OcspStapling certification hot renew
|
||||
_, _, err = lego.RenewCert(c.config.CertConfig.CertDomain, c.config.CertConfig.Email, c.config.CertConfig.CertMode, c.config.CertConfig.Provider, c.config.CertConfig.DNSEnv)
|
||||
_, _, _, err = lego.RenewCert()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
@@ -215,7 +239,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
return nil
|
||||
}
|
||||
// Add Limiter
|
||||
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo); err != nil {
|
||||
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo, c.config.GlobalDeviceLimitConfig); err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
@@ -247,12 +271,12 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) removeOldTag(oldtag string) (err error) {
|
||||
err = c.removeInbound(oldtag)
|
||||
func (c *Controller) removeOldTag(oldTag string) (err error) {
|
||||
err = c.removeInbound(oldTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.removeOutbound(oldtag)
|
||||
err = c.removeOutbound(oldTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -288,7 +312,7 @@ func (c *Controller) addNewTag(newNodeInfo *api.NodeInfo) (err error) {
|
||||
}
|
||||
|
||||
func (c *Controller) addInboundForSSPlugin(newNodeInfo api.NodeInfo) (err error) {
|
||||
// Shadowsocks-Plugin require a seaperate inbound for other TransportProtocol likes: ws, grpc
|
||||
// Shadowsocks-Plugin require a separate inbound for other TransportProtocol likes: ws, grpc
|
||||
fakeNodeInfo := newNodeInfo
|
||||
fakeNodeInfo.TransportProtocol = "tcp"
|
||||
fakeNodeInfo.EnableTLS = false
|
||||
@@ -372,33 +396,33 @@ func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo
|
||||
}
|
||||
|
||||
func compareUserList(old, new *[]api.UserInfo) (deleted, added []api.UserInfo) {
|
||||
msrc := make(map[api.UserInfo]byte) //按源数组建索引
|
||||
mall := make(map[api.UserInfo]byte) //源+目所有元素建索引
|
||||
mSrc := make(map[api.UserInfo]byte) // 按源数组建索引
|
||||
mAll := make(map[api.UserInfo]byte) // 源+目所有元素建索引
|
||||
|
||||
var set []api.UserInfo //交集
|
||||
var set []api.UserInfo // 交集
|
||||
|
||||
//1.源数组建立map
|
||||
// 1.源数组建立map
|
||||
for _, v := range *old {
|
||||
msrc[v] = 0
|
||||
mall[v] = 0
|
||||
mSrc[v] = 0
|
||||
mAll[v] = 0
|
||||
}
|
||||
//2.目数组中,存不进去,即重复元素,所有存不进去的集合就是并集
|
||||
// 2.目数组中,存不进去,即重复元素,所有存不进去的集合就是并集
|
||||
for _, v := range *new {
|
||||
l := len(mall)
|
||||
mall[v] = 1
|
||||
if l != len(mall) { //长度变化,即可以存
|
||||
l = len(mall)
|
||||
} else { //存不了,进并集
|
||||
l := len(mAll)
|
||||
mAll[v] = 1
|
||||
if l != len(mAll) { // 长度变化,即可以存
|
||||
l = len(mAll)
|
||||
} else { // 存不了,进并集
|
||||
set = append(set, v)
|
||||
}
|
||||
}
|
||||
//3.遍历交集,在并集中找,找到就从并集中删,删完后就是补集(即并-交=所有变化的元素)
|
||||
// 3.遍历交集,在并集中找,找到就从并集中删,删完后就是补集(即并-交=所有变化的元素)
|
||||
for _, v := range set {
|
||||
delete(mall, v)
|
||||
delete(mAll, v)
|
||||
}
|
||||
//4.此时,mall是补集,所有元素去源中找,找到就是删除的,找不到的必定能在目数组中找到,即新加的
|
||||
for v := range mall {
|
||||
_, exist := msrc[v]
|
||||
// 4.此时,mall是补集,所有元素去源中找,找到就是删除的,找不到的必定能在目数组中找到,即新加的
|
||||
for v := range mAll {
|
||||
_, exist := mSrc[v]
|
||||
if exist {
|
||||
deleted = append(deleted, v)
|
||||
} else {
|
||||
@@ -409,7 +433,23 @@ func compareUserList(old, new *[]api.UserInfo) (deleted, added []api.UserInfo) {
|
||||
return deleted, added
|
||||
}
|
||||
|
||||
func limitUser(c *Controller, user api.UserInfo, silentUsers *[]api.UserInfo) {
|
||||
c.limitedUsers[user] = LimitInfo{
|
||||
end: time.Now().Unix() + int64(c.config.AutoSpeedLimitConfig.LimitDuration*60),
|
||||
currentSpeedLimit: c.config.AutoSpeedLimitConfig.LimitSpeed,
|
||||
originSpeedLimit: user.SpeedLimit,
|
||||
}
|
||||
log.Printf("Limit User: %s Speed: %d End: %s", c.buildUserTag(&user), c.config.AutoSpeedLimitConfig.LimitSpeed, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
||||
user.SpeedLimit = uint64((c.config.AutoSpeedLimitConfig.LimitSpeed * 1000000) / 8)
|
||||
*silentUsers = append(*silentUsers, user)
|
||||
}
|
||||
|
||||
func (c *Controller) userInfoMonitor() (err error) {
|
||||
// delay to start
|
||||
if time.Since(c.startAt) < time.Duration(c.config.UpdatePeriodic)*time.Second {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get server status
|
||||
CPU, Mem, Disk, Uptime, err := serverstatus.GetSystemInfo()
|
||||
if err != nil {
|
||||
@@ -425,14 +465,55 @@ func (c *Controller) userInfoMonitor() (err error) {
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
// Unlock users
|
||||
if c.config.AutoSpeedLimitConfig.Limit > 0 && len(c.limitedUsers) > 0 {
|
||||
log.Printf("Limited users:")
|
||||
toReleaseUsers := make([]api.UserInfo, 0)
|
||||
for user, limitInfo := range c.limitedUsers {
|
||||
if time.Now().Unix() > limitInfo.end {
|
||||
user.SpeedLimit = limitInfo.originSpeedLimit
|
||||
toReleaseUsers = append(toReleaseUsers, user)
|
||||
log.Printf("User: %s Speed: %d End: nil (Unlimit)", c.buildUserTag(&user), user.SpeedLimit)
|
||||
delete(c.limitedUsers, user)
|
||||
} else {
|
||||
log.Printf("User: %s Speed: %d End: %s", c.buildUserTag(&user), limitInfo.currentSpeedLimit, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
||||
}
|
||||
}
|
||||
if len(toReleaseUsers) > 0 {
|
||||
if err := c.UpdateInboundLimiter(c.Tag, &toReleaseUsers); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get User traffic
|
||||
var userTraffic []api.UserTraffic
|
||||
var upCounterList []stats.Counter
|
||||
var downCounterList []stats.Counter
|
||||
AutoSpeedLimit := int64(c.config.AutoSpeedLimitConfig.Limit)
|
||||
UpdatePeriodic := int64(c.config.UpdatePeriodic)
|
||||
limitedUsers := make([]api.UserInfo, 0)
|
||||
for _, user := range *c.userList {
|
||||
up, down, upCounter, downCounter := c.getTraffic(c.buildUserTag(&user))
|
||||
if up > 0 || down > 0 {
|
||||
// Over speed users
|
||||
if AutoSpeedLimit > 0 {
|
||||
if down > AutoSpeedLimit*1000000*UpdatePeriodic/8 || up > AutoSpeedLimit*1000000*UpdatePeriodic/8 {
|
||||
if _, ok := c.limitedUsers[user]; !ok {
|
||||
if c.config.AutoSpeedLimitConfig.WarnTimes == 0 {
|
||||
limitUser(c, user, &limitedUsers)
|
||||
} else {
|
||||
c.warnedUsers[user] += 1
|
||||
if c.warnedUsers[user] > c.config.AutoSpeedLimitConfig.WarnTimes {
|
||||
limitUser(c, user, &limitedUsers)
|
||||
delete(c.warnedUsers, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
delete(c.warnedUsers, user)
|
||||
}
|
||||
}
|
||||
userTraffic = append(userTraffic, api.UserTraffic{
|
||||
UID: user.UID,
|
||||
Email: user.Email,
|
||||
@@ -445,6 +526,13 @@ func (c *Controller) userInfoMonitor() (err error) {
|
||||
if downCounter != nil {
|
||||
downCounterList = append(downCounterList, downCounter)
|
||||
}
|
||||
} else {
|
||||
delete(c.warnedUsers, user)
|
||||
}
|
||||
}
|
||||
if len(limitedUsers) > 0 {
|
||||
if err := c.UpdateInboundLimiter(c.Tag, &limitedUsers); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
if len(userTraffic) > 0 {
|
||||
|
@@ -8,12 +8,14 @@ import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/api/sspanel"
|
||||
_ "github.com/XrayR-project/XrayR/main/distro/all"
|
||||
. "github.com/XrayR-project/XrayR/service/controller"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/api/sspanel"
|
||||
"github.com/XrayR-project/XrayR/common/mylego"
|
||||
_ "github.com/XrayR-project/XrayR/main/distro/all"
|
||||
. "github.com/XrayR-project/XrayR/service/controller"
|
||||
)
|
||||
|
||||
func TestController(t *testing.T) {
|
||||
@@ -22,7 +24,7 @@ func TestController(t *testing.T) {
|
||||
LogConfig: &conf.LogConfig{LogLevel: "debug"},
|
||||
}
|
||||
policyConfig := &conf.PolicyConfig{}
|
||||
policyConfig.Levels = map[uint32]*conf.Policy{0: &conf.Policy{
|
||||
policyConfig.Levels = map[uint32]*conf.Policy{0: {
|
||||
StatsUserUplink: true,
|
||||
StatsUserDownlink: true,
|
||||
}}
|
||||
@@ -45,13 +47,13 @@ func TestController(t *testing.T) {
|
||||
if err = server.Start(); err != nil {
|
||||
t.Errorf("Failed to start instance: %s", err)
|
||||
}
|
||||
certConfig := &CertConfig{
|
||||
certConfig := &mylego.CertConfig{
|
||||
CertMode: "http",
|
||||
CertDomain: "test.ss.tk",
|
||||
Provider: "alidns",
|
||||
Email: "ss@ss.com",
|
||||
}
|
||||
controlerconfig := &Config{
|
||||
controlerConfig := &Config{
|
||||
UpdatePeriodic: 5,
|
||||
CertConfig: certConfig,
|
||||
}
|
||||
@@ -61,14 +63,14 @@ func TestController(t *testing.T) {
|
||||
NodeID: 41,
|
||||
NodeType: "V2ray",
|
||||
}
|
||||
apiclient := sspanel.New(apiConfig)
|
||||
c := New(server, apiclient, controlerconfig, "SSpanel")
|
||||
apiClient := sspanel.New(apiConfig)
|
||||
c := New(server, apiClient, controlerConfig, "SSpanel")
|
||||
fmt.Println("Sleep 1s")
|
||||
err = c.Start()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
//Explicitly triggering GC to remove garbage from config loading.
|
||||
// Explicitly triggering GC to remove garbage from config loading.
|
||||
runtime.GC()
|
||||
|
||||
{
|
||||
|
@@ -1,28 +1,29 @@
|
||||
//Package generate the InbounderConfig used by add inbound
|
||||
// Package controller Package generate the InboundConfig used by add inbound
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/common/legocmd"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/uuid"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/common/mylego"
|
||||
)
|
||||
|
||||
//InboundBuilder build Inbound config for different protocol
|
||||
// InboundBuilder build Inbound config for different protocol
|
||||
func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.InboundHandlerConfig, error) {
|
||||
inboundDetourConfig := &conf.InboundDetourConfig{}
|
||||
// Build Listen IP address
|
||||
if nodeInfo.NodeType == "Shadowsocks-Plugin" {
|
||||
// Shdowsocks listen in 127.0.0.1 for safety
|
||||
inboundDetourConfig.ListenOn = &conf.Address{net.ParseAddress("127.0.0.1")}
|
||||
inboundDetourConfig.ListenOn = &conf.Address{Address: net.ParseAddress("127.0.0.1")}
|
||||
} else if config.ListenIP != "" {
|
||||
ipAddress := net.ParseAddress(config.ListenIP)
|
||||
inboundDetourConfig.ListenOn = &conf.Address{ipAddress}
|
||||
inboundDetourConfig.ListenOn = &conf.Address{Address: ipAddress}
|
||||
}
|
||||
|
||||
// Build Port
|
||||
@@ -114,12 +115,12 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
||||
NetworkList: []string{"tcp", "udp"},
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("Unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks, and Shadowsocks-Plugin", nodeInfo.NodeType)
|
||||
return nil, fmt.Errorf("unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks, and Shadowsocks-Plugin", nodeInfo.NodeType)
|
||||
}
|
||||
|
||||
setting, err := json.Marshal(proxySetting)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
|
||||
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
|
||||
}
|
||||
|
||||
// Build streamSettings
|
||||
@@ -195,53 +196,53 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
||||
return inboundDetourConfig.Build()
|
||||
}
|
||||
|
||||
func getCertFile(certConfig *CertConfig) (certFile string, keyFile string, err error) {
|
||||
func getCertFile(certConfig *mylego.CertConfig) (certFile string, keyFile string, err error) {
|
||||
if certConfig.CertMode == "file" {
|
||||
if certConfig.CertFile == "" || certConfig.KeyFile == "" {
|
||||
return "", "", fmt.Errorf("Cert file path or key file path not exist")
|
||||
return "", "", fmt.Errorf("cert file path or key file path not exist")
|
||||
}
|
||||
return certConfig.CertFile, certConfig.KeyFile, nil
|
||||
} else if certConfig.CertMode == "dns" {
|
||||
lego, err := legocmd.New()
|
||||
lego, err := mylego.New(certConfig)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
certPath, keyPath, err := lego.DNSCert(certConfig.CertDomain, certConfig.Email, certConfig.Provider, certConfig.DNSEnv)
|
||||
certPath, keyPath, err := lego.DNSCert()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return certPath, keyPath, err
|
||||
} else if certConfig.CertMode == "http" {
|
||||
lego, err := legocmd.New()
|
||||
lego, err := mylego.New(certConfig)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
certPath, keyPath, err := lego.HTTPCert(certConfig.CertDomain, certConfig.Email)
|
||||
certPath, keyPath, err := lego.HTTPCert()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return certPath, keyPath, err
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("Unsupported certmode: %s", certConfig.CertMode)
|
||||
return "", "", fmt.Errorf("unsupported certmode: %s", certConfig.CertMode)
|
||||
}
|
||||
|
||||
func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboundFallback, error) {
|
||||
if fallbackConfigs == nil {
|
||||
return nil, fmt.Errorf("You must provide FallBackConfigs")
|
||||
return nil, fmt.Errorf("you must provide FallBackConfigs")
|
||||
}
|
||||
|
||||
vlessFallBacks := make([]*conf.VLessInboundFallback, len(fallbackConfigs))
|
||||
for i, c := range fallbackConfigs {
|
||||
|
||||
if c.Dest == "" {
|
||||
return nil, fmt.Errorf("Dest is required for fallback fialed")
|
||||
return nil, fmt.Errorf("dest is required for fallback fialed")
|
||||
}
|
||||
|
||||
var dest json.RawMessage
|
||||
dest, err := json.Marshal(c.Dest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Marshal dest %s config fialed: %s", dest, err)
|
||||
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
|
||||
}
|
||||
vlessFallBacks[i] = &conf.VLessInboundFallback{
|
||||
Name: c.SNI,
|
||||
@@ -256,20 +257,20 @@ func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboun
|
||||
|
||||
func buildTrojanFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.TrojanInboundFallback, error) {
|
||||
if fallbackConfigs == nil {
|
||||
return nil, fmt.Errorf("You must provide FallBackConfigs")
|
||||
return nil, fmt.Errorf("you must provide FallBackConfigs")
|
||||
}
|
||||
|
||||
trojanFallBacks := make([]*conf.TrojanInboundFallback, len(fallbackConfigs))
|
||||
for i, c := range fallbackConfigs {
|
||||
|
||||
if c.Dest == "" {
|
||||
return nil, fmt.Errorf("Dest is required for fallback fialed")
|
||||
return nil, fmt.Errorf("dest is required for fallback fialed")
|
||||
}
|
||||
|
||||
var dest json.RawMessage
|
||||
dest, err := json.Marshal(c.Dest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Marshal dest %s config fialed: %s", dest, err)
|
||||
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
|
||||
}
|
||||
trojanFallBacks[i] = &conf.TrojanInboundFallback{
|
||||
Name: c.SNI,
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/XrayR-project/XrayR/common/mylego"
|
||||
. "github.com/XrayR-project/XrayR/service/controller"
|
||||
)
|
||||
|
||||
@@ -20,7 +21,7 @@ func TestBuildV2ray(t *testing.T) {
|
||||
EnableTLS: false,
|
||||
TLSType: "tls",
|
||||
}
|
||||
certConfig := &CertConfig{
|
||||
certConfig := &mylego.CertConfig{
|
||||
CertMode: "http",
|
||||
CertDomain: "test.test.tk",
|
||||
Provider: "alidns",
|
||||
@@ -51,7 +52,7 @@ func TestBuildTrojan(t *testing.T) {
|
||||
DNSEnv := make(map[string]string)
|
||||
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
||||
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
|
||||
certConfig := &CertConfig{
|
||||
certConfig := &mylego.CertConfig{
|
||||
CertMode: "dns",
|
||||
CertDomain: "trojan.test.tk",
|
||||
Provider: "alidns",
|
||||
@@ -83,7 +84,7 @@ func TestBuildSS(t *testing.T) {
|
||||
DNSEnv := make(map[string]string)
|
||||
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
||||
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
|
||||
certConfig := &CertConfig{
|
||||
certConfig := &mylego.CertConfig{
|
||||
CertMode: "dns",
|
||||
CertDomain: "trojan.test.tk",
|
||||
Provider: "alidns",
|
||||
|
@@ -4,13 +4,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
//OutboundBuilder build freedom outbund config for addoutbound
|
||||
// OutboundBuilder build freedom outbound config for addOutbound
|
||||
func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.OutboundHandlerConfig, error) {
|
||||
outboundDetourConfig := &conf.OutboundDetourConfig{}
|
||||
outboundDetourConfig.Protocol = "freedom"
|
||||
@@ -19,11 +20,11 @@ func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.
|
||||
// Build Send IP address
|
||||
if config.SendIP != "" {
|
||||
ipAddress := net.ParseAddress(config.SendIP)
|
||||
outboundDetourConfig.SendThrough = &conf.Address{ipAddress}
|
||||
outboundDetourConfig.SendThrough = &conf.Address{Address: ipAddress}
|
||||
}
|
||||
|
||||
// Freedom Protocol setting
|
||||
var domainStrategy string = "Asis"
|
||||
var domainStrategy = "Asis"
|
||||
if config.EnableDNS {
|
||||
if config.DNSType != "" {
|
||||
domainStrategy = config.DNSType
|
||||
@@ -41,7 +42,7 @@ func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.
|
||||
var setting json.RawMessage
|
||||
setting, err := json.Marshal(proxySetting)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
|
||||
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
|
||||
}
|
||||
outboundDetourConfig.Settings = &setting
|
||||
return outboundDetourConfig.Build()
|
||||
|
@@ -4,13 +4,14 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/proxy/shadowsocks"
|
||||
"github.com/xtls/xray-core/proxy/trojan"
|
||||
"github.com/xtls/xray-core/proxy/vless"
|
||||
|
||||
"github.com/XrayR-project/XrayR/api"
|
||||
)
|
||||
|
||||
var AEADMethod = []shadowsocks.CipherType{shadowsocks.CipherType_AES_128_GCM, shadowsocks.CipherType_AES_256_GCM, shadowsocks.CipherType_CHACHA20_POLY1305, shadowsocks.CipherType_XCHACHA20_POLY1305}
|
||||
|
Reference in New Issue
Block a user