178 Commits

Author SHA1 Message Date
pocketW
f95825395d update to v0.9.0 2023-02-22 22:38:53 +11:00
dependabot[bot]
e279d39d5a build(deps): bump github.com/sagernet/sing from 0.1.6 to 0.1.7 (#263)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.6 to 0.1.7.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.6...v0.1.7)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 22:48:01 +08:00
dependabot[bot]
74038add93 build(deps): bump golang.org/x/net from 0.6.0 to 0.7.0 (#259)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-16 07:34:28 +08:00
dependabot[bot]
0a2b8612dc build(deps): bump golang.org/x/crypto from 0.5.0 to 0.6.0 (#253)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-10 15:55:37 +08:00
dependabot[bot]
5923018a9d build(deps): bump github.com/xtls/xray-core from 1.7.3 to 1.7.5 (#252)
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.7.3 to 1.7.5.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.7.3...v1.7.5)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-08 23:49:27 +08:00
pocketW
239a0eea78 fix: compatiable VLESS+TCP+TLS #245 2023-02-03 13:30:38 +11:00
pocketW
456951155d chore: support xtls-rprx-vision for VLESS 2023-02-02 22:05:33 +11:00
pocketW
a7a3d0220d update to v0.8.9 2023-02-02 22:03:09 +11:00
pocketW
b857bfac3e fix docker build 2023-01-21 15:30:25 +11:00
pocketW
f4ea59493f fix docker build 2023-01-21 14:08:46 +11:00
dependabot[bot]
957b439ced build(deps): bump github.com/spf13/viper from 1.14.0 to 1.15.0 (#228)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-20 23:31:28 +08:00
pocketW
19583503cd chore: keep dispatcher updated to Xray-core 2023-01-20 23:07:36 +11:00
pocketW
d8a3242e22 Merge pull request #227 from Github-Aiko/master
Translate Readme for English(en) users
2023-01-20 21:10:17 +11:00
Aiko
007d9a0717 Merge branch 'master' into master 2023-01-20 16:47:13 +07:00
pocketW
016051892b Update README.md 2023-01-20 20:17:15 +11:00
Aiko
5764c3e610 Update Readme 2023-01-20 16:02:02 +07:00
Aiko
5bacee35aa Add En 2023-01-20 16:00:50 +07:00
Aiko
e6b6abee24 Create README-en.md 2023-01-20 16:00:15 +07:00
pocketW
edddd2f965 Update README.md 2023-01-20 19:33:24 +11:00
pocketW
0cd17f2b55 Merge pull request #226 from Github-Aiko/patch-2
Translate Readme for Vietnamese(vi) users
2023-01-20 19:28:58 +11:00
pocketW
3838b2d333 Merge pull request #224 from DanielBlackBeard/master
translate Readme for Iranian(farsi) users
2023-01-20 19:26:42 +11:00
Aiko
7a90f157b8 Translate Readme for Vietnam(vi) users 2023-01-19 23:46:06 +07:00
Persian Prince
a10efcaea0 README_Fa.md
*Add Persian Translation of Readme
2023-01-19 20:12:43 +03:30
dependabot[bot]
1f85ce3762 build(deps): bump github.com/sagernet/sing from 0.1.5 to 0.1.6 (#216)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.5 to 0.1.6.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.5...v0.1.6)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-17 01:34:39 +08:00
dependabot[bot]
eed0b8ae32 build(deps): bump github.com/sagernet/sing from 0.1.3 to 0.1.5 (#209)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.3 to 0.1.5.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.3...v0.1.5)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-13 00:36:02 +08:00
Senis John
ea2f7a64e1 fix: newV2board cannot parse tcp http header
fix: typo
chore: update deps
2023-01-11 13:17:53 +08:00
dependabot[bot]
ad8243fcf5 build(deps): bump github.com/sagernet/sing from 0.1.2 to 0.1.3 (#206)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.2 to 0.1.3.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.2...v0.1.3)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 23:39:18 +08:00
pocketW
66fccfd422 Merge pull request #194 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.5.0
build(deps): bump golang.org/x/crypto from 0.4.0 to 0.5.0
2023-01-06 09:11:11 +11:00
dependabot[bot]
448d44ff46 build(deps): bump golang.org/x/crypto from 0.4.0 to 0.5.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-05 22:11:02 +00:00
pocketW
891068a091 Merge pull request #193 from XrayR-project/dependabot/go_modules/golang.org/x/net-0.5.0
build(deps): bump golang.org/x/net from 0.4.0 to 0.5.0
2023-01-06 09:09:04 +11:00
pocketW
127ccef619 Merge pull request #191 from XrayR-project/dependabot/go_modules/github.com/sagernet/sing-0.1.2
build(deps): bump github.com/sagernet/sing from 0.1.1 to 0.1.2
2023-01-06 09:08:51 +11:00
dependabot[bot]
84bc47ba18 build(deps): bump golang.org/x/net from 0.4.0 to 0.5.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-05 15:25:54 +00:00
dependabot[bot]
a139d48959 build(deps): bump github.com/sagernet/sing from 0.1.1 to 0.1.2
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.1 to 0.1.2.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.1...v0.1.2)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-03 15:08:22 +00:00
dependabot[bot]
180a4af7ca build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.11 to 3.22.12 (#190)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.11 to 3.22.12.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/commits/v3.22.12)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-03 00:59:23 +08:00
Senis John
8512c354f5 revert: dns hot load feat
the dispatcher mod cannot reload dns instance. So I revert the DNS feat and will create a new branch to test it.
2023-01-02 21:46:30 +08:00
Senis John
4746bd57d0 fix: reload dns service when need 2023-01-01 02:28:38 +08:00
Senis John
6477ef6c44 fix: dns memory leak
It can be properly working after xray-core accept the patch
2022-12-31 12:35:55 +08:00
Senis John
21e0ebc428 fix: Try to fix dns memory leak
update: release.yml
2022-12-30 22:19:48 +08:00
Senis John
40c65a86fc update: reformat newV2board serverConfig structure 2022-12-28 10:36:29 +08:00
Senis John
b5a8f4464c fix: ss2022 not working as expect
fix: minor bugs
2022-12-27 17:28:56 +08:00
Senis John
451b5a1fd4 fix: typo 2022-12-27 16:10:51 +08:00
Senis John
8ddf257aa4 update: release.yml
fix: typo
2022-12-27 15:10:17 +08:00
Senis John
a8742426b3 update: refactor newV2board parser's structure 2022-12-27 12:00:34 +08:00
Senis John
d44fa11eee update: support v2board v1.7.2 routes structure
After update this version, the v2board version must >= 1.7.2 release
2022-12-26 12:41:18 +08:00
Senis John
0c2d03f3e0 update: add error message for none user 2022-12-25 09:01:26 +08:00
Senis John
9c6ae5b7ab update: speedup docker build 2022-12-22 09:20:16 +08:00
Senis John
4c699d86f2 update: compatible with v2board v1.7.1 route rules 2022-12-21 20:09:09 +08:00
pocketW
be940fdcea add cache to speedup docker build 2022-12-20 11:39:48 +11:00
pocketW
619a455432 refactor: remove unused regexp 2022-12-20 09:19:17 +11:00
pocketW
03b7bf6ed1 fix: only encode userKey with base64 for V2board 2022-12-20 09:05:18 +11:00
dependabot[bot]
224224084e build(deps): bump github.com/sagernet/sing from 0.1.0 to 0.1.1 (#171)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.0 to 0.1.1.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.0...v0.1.1)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 23:24:31 +08:00
pocketW
ac460c2f71 Merge branch 'master' of https://github.com/XrayR-project/XrayR 2022-12-19 21:48:29 +11:00
pocketW
e1b512ef5b fix: check userKey for ss2022 2022-12-19 21:48:19 +11:00
Senis John
d5ab69d703 update: support v2board shadowsocks obfs settings
fix: v2board route block bugs
2022-12-19 01:21:27 +08:00
Senis John
d9bbb836b8 fix: minor bugs 2022-12-18 16:08:56 +08:00
Senis John
1de5143fde update: support v2board DNS rules 2022-12-18 15:31:23 +08:00
Senis John
77814acd1a fix: typo and remove redundant conversion 2022-12-18 10:27:59 +08:00
pocketW
3926463c59 feat: add shadowsocks2022 support for v2raysocks 2022-12-18 12:11:55 +11:00
pocketW
05ff6dff1b chore: fix build error 2022-12-18 11:26:38 +11:00
Senis John
1d5d4564e5 chore: update go.mod 2022-12-18 01:51:22 +08:00
Senis John
0777cc8892 update: add ss plugin support ss2022
The error message will have more details
2022-12-16 20:43:38 +08:00
Senis John
b09f371447 fix: typo 2022-12-16 14:43:56 +08:00
pocketW
fc71d76f4d update to v0.8.8 2022-12-16 09:51:34 +11:00
pocketW
443fb959f6 fix: type conversion 2022-12-15 23:00:56 +11:00
Senis John
040e19ee87 fix: Priority local speed limit 2022-12-15 19:51:43 +08:00
pocketW
addde0f8d4 feat: read speed limit from V2board
chore: update dependence
2022-12-15 22:31:35 +11:00
Senis John
9ef39925fd fix: newV2board rule regexp pattern 2022-12-15 09:55:49 +08:00
Senis John
a6dbf757e9 fix: add error to ss2022's length check 2022-12-14 10:12:19 +08:00
Senis John
acd3cce667 fix: trim length of userKey for ss2022 2022-12-13 20:43:02 +08:00
Senis John
eea6c0dea6 fix: typo 2022-12-11 06:51:25 +08:00
Senis John
889160b908 update: xray-core dev v1.6.6 2022-12-09 19:31:19 +08:00
dependabot[bot]
0bea22e145 build(deps): bump golang.org/x/crypto from 0.3.0 to 0.4.0 (#145)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: thank243 <thank243@gmail.com>
2022-12-07 23:28:46 +08:00
dependabot[bot]
f25d5993f3 build(deps): bump golang.org/x/net from 0.3.0 to 0.4.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-07 23:27:46 +08:00
Senis John
676365b13b update: Add support NewV2board api block rules 2022-12-07 23:26:09 +08:00
thank243
e774d5c822 Merge branch 'master' into globalLimit 2022-12-07 20:19:43 +08:00
pocketW
be4f9cdac1 reduce clear interval 2022-12-07 21:58:12 +11:00
Senis John
9a06f78653 fix: Add deviceLimit to the prefix of email string
Solve different node and different device limit the less limit node cannot work normally.
2022-12-07 18:49:08 +08:00
Senis John
85d73408c3 update: refactor global limit cache. Use gocache lib for level 2 cache
fix: shadowsocks2022 init feat
2022-12-07 13:29:05 +08:00
dependabot[bot]
c93fdb4cf3 build(deps): bump golang.org/x/net from 0.2.0 to 0.3.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.2.0 to 0.3.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.2.0...v0.3.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-06 23:25:19 +08:00
dependabot[bot]
0d565b034b build(deps): bump golang.org/x/time from 0.2.0 to 0.3.0
Bumps [golang.org/x/time](https://github.com/golang/time) from 0.2.0 to 0.3.0.
- [Release notes](https://github.com/golang/time/releases)
- [Commits](https://github.com/golang/time/compare/v0.2.0...v0.3.0)

---
updated-dependencies:
- dependency-name: golang.org/x/time
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 23:31:39 +08:00
Senis John
212f0ff135 fix: log prefix display not completed 2022-12-04 08:43:10 +08:00
Senis John
608764a8a0 fix: shadowsocks2022 user manager not working 2022-12-04 00:50:54 +08:00
Senis John
fc16cb0972 fix: shadowsocks2022 user manager not working 2022-12-04 00:42:35 +08:00
pocketW
5397720430 fix: expire time error 2022-12-03 19:17:33 +11:00
pocketW
839b15c22c fix: fix condition of check cert service
fix: check devices on redis first
2022-12-03 14:28:32 +11:00
Senis John
5b45b8ffe8 fix: refactor the InboundInfo struct
Global limit can separate settings for each node
2022-12-03 09:11:43 +08:00
dependabot[bot]
a56cd91b34 build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.10 to 3.22.11 (#132)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.10 to 3.22.11.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/commits)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-01 23:18:31 +08:00
Senis John
b6600729b2 update: code clean 2022-11-30 09:13:35 +08:00
Senis John
8d0225bcbb fix: On enable global limit, the online device report feat will failure
update: add pushIP method for limiter
2022-11-29 20:42:09 +08:00
pocketW
ce5fe799f4 Merge pull request #126 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.5
build(deps): bump github.com/xtls/xray-core from 1.6.4 to 1.6.5
2022-11-29 09:51:07 +11:00
pocketW
8763d5960f Merge pull request #125 from XrayR-project/dependabot/go_modules/github.com/go-acme/lego/v4-4.9.1
build(deps): bump github.com/go-acme/lego/v4 from 4.9.0 to 4.9.1
2022-11-29 09:50:55 +11:00
dependabot[bot]
7a452a4a53 build(deps): bump github.com/xtls/xray-core from 1.6.4 to 1.6.5
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.4 to 1.6.5.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.4...v1.6.5)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 15:10:20 +00:00
dependabot[bot]
fac8b62286 build(deps): bump github.com/go-acme/lego/v4 from 4.9.0 to 4.9.1
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.9.0 to 4.9.1.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.9.0...v4.9.1)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 15:10:08 +00:00
thank243
d320aadb54 fix: global limit will failure on some situation (#124)
fix: typo
2022-11-28 11:16:27 +08:00
thank243
40ae48f507 update: refactor the global limit (#121)
fix: some log prefix

It an enhance device limit. Redis just use to sync local ipMap.
If global limit is enabled. The online report will faliure. All enabled nodes will report the device from redis.
2022-11-27 10:12:30 +08:00
pocketW
d1bc36782b add timeout 2022-11-26 21:44:14 +11:00
AikoCute
c04330d0bf Merge branch 'master' of https://github.com/Github-Aiko/XrayR-Real 2022-11-26 16:14:06 +07:00
AikoCute
872eb12d35 Check Connect Redis Server 2022-11-26 16:13:45 +07:00
pocketW
e1d4428d98 Merge pull request #118 from amin550/master
fix: ssPanel Report Node Status
2022-11-26 17:41:00 +11:00
pocketW
0fef3cf278 update config example 2022-11-26 17:30:20 +11:00
Amin
a4ca37b1de Fix sspanel ReportNodeStatus 2022-11-26 00:52:30 +03:30
Senis Y
74f3a75682 fix: delete unused struct 2022-11-25 00:25:39 +08:00
Senis Y
183b1be519 feature: add support v2board new API
update: add eTag on fetch users for save resources
update: split cert monitor service
update: refactor log format
update: add support shadowsocks_2022

The cert monitor period = UpdatePeriodic * 60

The xray-core official is not implementation of proxy.UserManager feature. So it may change in the future.
2022-11-24 15:11:01 +08:00
pocketW
2f10c3f6b8 revert error format 2022-11-20 14:57:20 +11:00
AikoCute
86324ff1ae Logs for GlobalDeviceLimit 2022-11-20 10:10:03 +07:00
pocketW
1897404c9d Merge pull request #104 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.3.0
build(deps): bump golang.org/x/crypto from 0.2.0 to 0.3.0
2022-11-18 17:25:38 +11:00
dependabot[bot]
ee53e746c5 build(deps): bump golang.org/x/crypto from 0.2.0 to 0.3.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.2.0 to 0.3.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.2.0...v0.3.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-17 15:15:59 +00:00
pocketW
656df61c17 Merge pull request #101 from thank243/master
fix: http mode not working
fix: use "email|uid" for global limit user identify
update: tls alpn challenge for apply certificate
2022-11-17 12:42:53 +11:00
Senis
e0237f5c54 fix: http mode not working
fix: use "email|uid" for global limit user identify
update: tls alpn challenge for apply certificate
2022-11-16 12:59:44 +08:00
pocketW
e16d94fb4a Merge pull request #97 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.4
build(deps): bump github.com/xtls/xray-core from 1.6.3 to 1.6.4
2022-11-15 11:25:59 +11:00
dependabot[bot]
e357fc438f build(deps): bump github.com/xtls/xray-core from 1.6.3 to 1.6.4
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.3 to 1.6.4.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.3...v1.6.4)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 15:12:52 +00:00
pocketW
cb1638ac21 update to v0.8.6 2022-11-13 13:08:12 +11:00
pocketW
de0da25c21 chore: update dependence 2022-11-12 21:22:13 +11:00
pocketW
faec840c23 Merge pull request #90 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.2.0
build(deps): bump golang.org/x/crypto from 0.1.0 to 0.2.0
2022-11-12 02:26:08 +11:00
pocketW
a6a1baf70c Merge pull request #89 from thank243/master
fix: adding the issuers certificate to the new certificate
2022-11-12 02:25:54 +11:00
pocketW
4013f71e4c fix: update global device limit 2022-11-12 02:22:53 +11:00
dependabot[bot]
c8f0981b0e build(deps): bump golang.org/x/crypto from 0.1.0 to 0.2.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.1.0 to 0.2.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.1.0...v0.2.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-11 15:16:31 +00:00
Senis
5274edf657 fix: adding the issuers certificate to the new certificate 2022-11-11 22:42:31 +08:00
pocketW
e6232c1852 Merge pull request #88 from XrayR-project/dependabot/go_modules/golang.org/x/net-0.2.0
build(deps): bump golang.org/x/net from 0.1.0 to 0.2.0
2022-11-11 23:59:47 +11:00
dependabot[bot]
e0688fc609 build(deps): bump golang.org/x/net from 0.1.0 to 0.2.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.1.0 to 0.2.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.1.0...v0.2.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-11 12:58:43 +00:00
pocketW
ffa444f2ab Merge pull request #86 from XrayR-project/dependabot/go_modules/github.com/spf13/viper-1.14.0
build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0
2022-11-11 23:57:06 +11:00
dependabot[bot]
81ba4ebb43 build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-11 12:56:11 +00:00
pocketW
f50c61c782 fix: fix cert storage path 2022-11-11 23:44:43 +11:00
Senis
9c356cd28c Merge remote-tracking branch 'origin/master' 2022-11-08 17:50:53 +08:00
Senis
310353f344 fix: Redundant character escape 2022-11-08 17:50:03 +08:00
thank243
808b5ecc3c Merge branch 'master' into master 2022-11-08 14:48:44 +08:00
Senis
c4ef5bb843 update: xray-core v1.6.3 2022-11-08 14:46:06 +08:00
Senis
ddce3fa86d fix: typo 2022-11-08 11:57:06 +08:00
Senis
209f5a17d6 update: rebuild the implementation of legocmd 2022-11-08 11:13:27 +08:00
pocketW
af3fae9cdb Merge pull request #82 from thank243/master
fix: on some circumstance, periodic will run twice
2022-11-07 23:30:32 +11:00
Senis
3b96b352cb fix: on some circumstance, periodic will run twice 2022-11-07 15:55:30 +08:00
pocketW
398c3133d3 Merge pull request #79 from thank243/master
feat: add global user ip limit
2022-11-06 14:31:22 +11:00
Senis
a397af5d73 fix: init global limit 2022-11-04 14:36:34 +08:00
Senis
b47954ea64 fix: minor bugs 2022-11-04 08:58:41 +08:00
Senis
9a2188cb0c update: Global User IP limit. 2022-11-04 08:26:56 +08:00
pocketW
c7af43fc49 Merge pull request #75 from XrayR-project/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.22.10
build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.9 to 3.22.10
2022-11-03 22:49:11 +11:00
pocketW
87aa855154 Merge pull request #74 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.2
build(deps): bump github.com/xtls/xray-core from 1.6.1 to 1.6.2
2022-11-03 22:48:58 +11:00
dependabot[bot]
37eff6755c build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.9 to 3.22.10
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.9 to 3.22.10.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.22.9...v3.22.10)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 15:53:37 +00:00
dependabot[bot]
5e346ddfee build(deps): bump github.com/xtls/xray-core from 1.6.1 to 1.6.2
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.1 to 1.6.2.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.1...v1.6.2)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-31 16:00:54 +00:00
Senis
838c667a87 fix: typo 2022-10-27 08:26:23 +08:00
pocketW
057f4156bf Merge pull request #71 from XrayR-project/dependabot/go_modules/github.com/stretchr/testify-1.8.1
build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
2022-10-26 13:40:42 +11:00
pocketW
1f59a7cd7a Merge pull request #70 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.1
build(deps): bump github.com/xtls/xray-core from 1.6.0 to 1.6.1
2022-10-26 13:40:30 +11:00
dependabot[bot]
2b5fa4feee build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 16:02:42 +00:00
dependabot[bot]
571191a190 build(deps): bump github.com/xtls/xray-core from 1.6.0 to 1.6.1
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.0...v1.6.1)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 16:02:30 +00:00
pocketW
3d5891fef3 update to v0.8.5 2022-10-14 15:19:16 +11:00
pocketW
70a0099f2c Merge pull request #67 from XrayR-project/dependabot/go_modules/github.com/fsnotify/fsnotify-1.6.0
build(deps): bump github.com/fsnotify/fsnotify from 1.5.4 to 1.6.0
2022-10-14 08:44:50 +11:00
dependabot[bot]
af8f24b5b1 build(deps): bump github.com/fsnotify/fsnotify from 1.5.4 to 1.6.0
Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.5.4 to 1.6.0.
- [Release notes](https://github.com/fsnotify/fsnotify/releases)
- [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsnotify/fsnotify/compare/v1.5.4...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/fsnotify/fsnotify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-13 15:51:51 +00:00
pocketW
79528d3e17 feat: dynamic update speedlimit bucket 2022-10-13 08:37:59 +11:00
pocketW
2f0461ddda chore: update lego to v4.9.0 2022-10-05 10:25:07 +11:00
pocketW
023680fec7 Merge pull request #62 from XrayR-project/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.22.9
build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.8 to 3.22.9
2022-10-04 11:50:38 +11:00
dependabot[bot]
708adf1e43 build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.8 to 3.22.9
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.8 to 3.22.9.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.22.8...v3.22.9)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-03 15:40:50 +00:00
pocketW
0bbec7ebeb Merge pull request #61 from JoshuaCylinder/master
feat: add dynamic speed limit #39
添加动态限速功能
2022-10-03 13:03:58 +11:00
JoshuaCylinder
a9dfd5404f Add default value for AutoSpeedLimitConfig 2022-10-01 07:11:20 +00:00
JoshuaCylinder
39c1036c4a Add AutoSpeedLimitConfig for some special node (For example: IPLC, IEPL, GameNode) 2022-10-01 06:49:51 +00:00
pocketW
7604e33b03 chore: update go to 1.19 2022-09-22 16:12:37 +10:00
pocketW
a906006015 Merge pull request #54 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.0
build(deps): bump github.com/xtls/xray-core from 1.5.10 to 1.6.0
2022-09-20 17:48:04 +10:00
dependabot[bot]
cb5cc17a82 build(deps): bump github.com/xtls/xray-core from 1.5.10 to 1.6.0
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.5.10 to 1.6.0.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.5.10...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-19 15:52:56 +00:00
pocketW
1cf8bca79e update v0.8.4 2022-09-18 14:26:48 +10:00
pocketW
cbffafbb4c fix: fix CF dns env error #51 2022-09-18 14:19:55 +10:00
pocketW
b37705b374 update v0.8.3 2022-09-11 00:05:22 +10:00
pocketW
e00d228c3d Merge branch 'master' of https://github.com/XrayR-project/XrayR 2022-09-09 22:43:06 +10:00
pocketW
21847fee7d remove dockerhub support 2022-09-09 22:43:00 +10:00
pocketW
f42e30cc61 Merge pull request #44 from XrayR-project/dependabot/go_modules/github.com/spf13/viper-1.13.0
build(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0
2022-09-07 11:03:44 +10:00
pocketW
e3c7cf2a5f Merge pull request #43 from XrayR-project/dependabot/go_modules/github.com/urfave/cli-1.22.10
build(deps): bump github.com/urfave/cli from 1.22.9 to 1.22.10
2022-09-07 11:03:31 +10:00
dependabot[bot]
82731d9d9c build(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-06 15:39:39 +00:00
dependabot[bot]
45f4d80a49 build(deps): bump github.com/urfave/cli from 1.22.9 to 1.22.10
Bumps [github.com/urfave/cli](https://github.com/urfave/cli) from 1.22.9 to 1.22.10.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v1.22.9...v1.22.10)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-05 16:49:36 +00:00
pocketW
10691e4cf3 panic #42 2022-09-03 20:34:22 +10:00
pocketW
9dbb231a12 Merge pull request #41 from XrayR-project/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.22.8
build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.7 to 3.22.8
2022-09-03 20:32:45 +10:00
dependabot[bot]
8724ece88a build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.7 to 3.22.8
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.7 to 3.22.8.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.22.7...v3.22.8)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-01 15:46:31 +00:00
pocketW
f0610d09fd feat: not reset traffic when report failed 2022-08-30 00:51:21 +10:00
pocketW
8991284e19 chore: update Xray-core to 1.5.10 2022-08-28 14:15:57 +10:00
pocketW
7a12a806a8 Update README.md 2022-08-27 21:05:18 +10:00
pocketW
fda14882a4 Create stale.yml 2022-08-26 22:29:38 +10:00
pocketW
710f081025 fix: #37 2022-08-26 13:00:07 +10:00
pocketW
c280cca7b3 add github docker package 2022-08-25 20:32:07 +10:00
pocketW
fa51cb5309 fix: fix bug when parsing transport protocol 2022-08-24 09:52:45 +10:00
pocketW
529eb46ea0 chore: add hints for config file
doc: add some badges
2022-08-23 11:20:33 +10:00
pocketW
2a528070bf doc: update doc 2022-08-22 22:41:28 +10:00
88 changed files with 3100 additions and 4629 deletions

View File

@@ -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"
}
}

View File

@@ -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@v3
# 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

View File

@@ -20,25 +20,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
-
name: Docker meta
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- 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
id: meta
uses: docker/metadata-action@v4
with:
images: mengxin239/xrayr
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
uses: docker/build-push-action@v2
images: ghcr.io/${{ github.repository }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/arm/v7,linux/arm64,linux/amd64,linux/s390x

View File

@@ -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 }}
@@ -101,19 +101,19 @@ jobs:
CGO_ENABLED: 0
steps:
- name: Checkout codebase
uses: actions/checkout@v2
- name: Show workflow information
uses: actions/checkout@v3
- 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_OUTPUT
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ^1.18
go-version: ^1.20
- 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: |
@@ -135,27 +135,32 @@ jobs:
mv XrayR XrayR.exe
- name: Prepare to release
run: |
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
cp ${GITHUB_WORKSPACE}/main/dns.json ./build_assets/dns.json
cp ${GITHUB_WORKSPACE}/main/route.json ./build_assets/route.json
cp ${GITHUB_WORKSPACE}/main/custom_outbound.json ./build_assets/custom_outbound.json
cp ${GITHUB_WORKSPACE}/main/custom_inbound.json ./build_assets/custom_inbound.json
cp ${GITHUB_WORKSPACE}/main/rulelist ./build_assets/rulelist
cp ${GITHUB_WORKSPACE}/main/config.yml.example ./build_assets/config.yml
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
for i in "${LIST[@]}"
do
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
LASTEST_TAG="$(curl -sL "https://api.github.com/repos/v2fly/${INFO[0]}/releases" | jq -r ".[0].tag_name" || echo "latest")"
FILE_NAME="${INFO[2]}.dat"
echo -e "Downloading ${FILE_NAME}..."
curl -L "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat" -o ./build_assets/${FILE_NAME}
echo -e "Verifying HASH key..."
HASH="$(curl -sL "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
done
uses: nick-fields/retry@v2
with:
timeout_minutes: 60
retry_wait_seconds: 60
max_attempts: 5
command: |
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
cp ${GITHUB_WORKSPACE}/main/dns.json ./build_assets/dns.json
cp ${GITHUB_WORKSPACE}/main/route.json ./build_assets/route.json
cp ${GITHUB_WORKSPACE}/main/custom_outbound.json ./build_assets/custom_outbound.json
cp ${GITHUB_WORKSPACE}/main/custom_inbound.json ./build_assets/custom_inbound.json
cp ${GITHUB_WORKSPACE}/main/rulelist ./build_assets/rulelist
cp ${GITHUB_WORKSPACE}/main/config.yml.example ./build_assets/config.yml
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
for i in "${LIST[@]}"
do
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
DOWNLOAD_URL="https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat"
FILE_NAME="${INFO[2]}.dat"
echo -e "Downloading ${DOWNLOAD_URL}..."
curl -L "${DOWNLOAD_URL}" -o ./build_assets/${FILE_NAME}
echo -e "Verifying HASH key..."
HASH="$(curl -sL "${DOWNLOAD_URL}.sha256sum" | awk -F ' ' '{print $1}')"
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
done
- name: Create ZIP archive
shell: bash
run: |
@@ -173,7 +178,7 @@ jobs:
run: |
mv build_assets XrayR-$ASSET_NAME
- name: Upload files to Artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: XrayR-${{ steps.get_filename.outputs.ASSET_NAME }}
path: |

22
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -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 }}

4
.gitignore vendored
View File

@@ -13,4 +13,6 @@ main/cert
main/config.yml
./vscode
.idea/*
.DS_Store
.DS_Store
*.bak
go.work*

View File

@@ -1,5 +1,5 @@
# Build go
FROM golang:1.18-alpine AS builder
FROM golang:1.20-alpine AS builder
WORKDIR /app
COPY . .
ENV CGO_ENABLED=0

109
README-en.md Normal file
View File

@@ -0,0 +1,109 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[Iranian(farsi) README](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md), [Vietnamese(vi) README](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md), [English(en) README](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)
A Xray backend framework that can easily support many panels.
A back -end framework based on XRAY supports V2ay, Trojan, Shadowsocks protocols, which are easy to expand and support multi -panel docker.
If you like this project, you can click STAR+WATCH in the upper right corner to continue to pay attention to the progress of this project.
## Guide for use
Tutorial[Detailed tutorial](https://xrayr-project.github.io/XrayR-doc/)
## Disclaimer
This project is just my personal learning and development and maintenance. I do not guarantee any availability and is not responsible for any consequences caused by the use of this software.
## Features
* Permanent open source and free.
* Support V2Ray, Trojan, Shadowsocks multiple protocols.
* Support new features such as Vless and XTLS.
* Support single instance docking multi -panel and multi -node, no need to start repeatedly.
* Support restriction online IP
* Support node port level and user level speed limit.
* The configuration is simple and clear.
* Modify the automatic restart instance.
* Easy to compile and upgrade, you can quickly update the core version and support the new features of XRAY-CORE.
## Function
| Function | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| Get node information | √ | √ | √ |
| Get user information | √ | √ | √ |
| User traffic statistics | √ | √ | √ |
| Server information report | √ | √ | √ |
| Automatically apply for a TLS certificate | √ | √ | √ |
| Automatic renewal TLS certificate | √ | √ | √ |
| Number of online people | √ | √ | √ |
| Online user restrictions | √ | √ | √ |
| Audit rules | √ | √ | √ |
| Node port speed limit | √ | √ | √ |
| According to user speed limit | √ | √ | √ |
| Custom DNS | √ | √ | √ |
## Support for panels
| Panel | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (Single-ended multi-user and V2Ray-Plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
## Software Installation
### 1-Click installation
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### Docker
[Docker deployment tutorial](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### Manual installation
[Manual installation tutorial](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## Configuration file and detailed use tutorial
[Detailed tutorial](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
## Licence
[Mozilla Public License Version 2.0](https://github.com/XrayR-project/XrayR/blob/master/LICENSE)
## Telgram
[Xrayr back-end discussion](https://t.me/XrayR_project)
[Xrayr notification](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

104
README-vi.md Normal file
View File

@@ -0,0 +1,104 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[Iranian(farsi) README](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md), [Vietnamese(vi) README](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md), [English(en) README](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)
A Xray backend framework that can easily support many panels.
Khung trở lại dựa trên XRay hỗ trợ các giao thức V2ay, Trojan, Shadowsocks, dễ dàng mở rộng và hỗ trợ kết nối nhiều người.
Nếu bạn thích dự án này, bạn có thể nhấp vào Star+Watch ở góc trên bên phải để tiếp tục chú ý đến tiến trình của dự án này.
## Tài liệu
Sử dụng hướng dẫn: [Hướng dẫn chi tiết](https://xrayr-project.github.io/XrayR-doc/) ( Tiếng Trung )
## Tuyên bố miễn trừ
Dự án này chỉ là học tập và phát triển và bảo trì cá nhân của tôi. Tôi không đảm bảo bất kỳ sự sẵn có nào và không chịu trách nhiệm cho bất kỳ hậu quả nào do việc sử dụng phần mềm này.
## Đặt điểm nổi bật
* Nguồn mở vĩnh viễn và miễn phí.
* Hỗ trợ V2Ray, Trojan, Shadowsocks nhiều giao thức.
* Hỗ trợ các tính năng mới như Vless và XTL.
* Hỗ trợ trường hợp đơn lẻ kết nối Multi -Panel và Multi -Node, không cần phải bắt đầu nhiều lần.
* Hỗ trợ hạn chế IP trực tuyến
* Hỗ trợ cấp cổng nút và giới hạn tốc độ cấp người dùng.
* Cấu hình đơn giản và rõ ràng.
* Sửa đổi phiên bản khởi động lại tự động.
* Dễ dàng biên dịch và nâng cấp, bạn có thể nhanh chóng cập nhật phiên bản cốt lõi và hỗ trợ các tính năng mới của Xray-Core.
## Chức năng
| Chức năng | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| Nhận thông tin Node | √ | √ | √ |
| Nhận thông tin người dùng | √ | √ | √ |
| Thống kê lưu lượng người dùng | √ | √ | √ |
| Báo cáo thông tin máy chủ | √ | √ | √ |
| Tự động đăng ký chứng chỉ TLS | √ | √ | √ |
| Chứng chỉ TLS gia hạn tự động | √ | √ | √ |
| Số người trực tuyến | √ | √ | √ |
| Hạn chế người dùng trực tuyến | √ | √ | √ |
| Quy tắc kiểm toán | √ | √ | √ |
| Giới hạn tốc độ cổng nút | √ | √ | √ |
| Theo giới hạn tốc độ người dùng | √ | √ | √ |
| DNS tùy chỉnh | √ | √ | √ |
## Hỗ trợ Panel
| Panel | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (Nhiều người dùng cuối và v2ray-plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
## Cài đặt phần mềm
### Một cài đặt chính
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### Sử dụng phần mềm triển khai Docker
[Hướng dẫn cài đặt thông qua Docker](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### Hướng dẫn cài đặt
[Hướng dẫn cài đặt thủ công](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## Tệp cấu hình và hướng dẫn sử dụng chi tiết
[Hướng dẫn chi tiết](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
## Licence
[Mozilla Public License Version 2.0](https://github.com/XrayR-project/XrayR/blob/master/LICENSE)
## Telgram
[Xrayr Back-end Thảo luận](https://t.me/XrayR_project)
[Thông báo Xrayr](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

View File

@@ -1,6 +1,15 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[English](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)|[Iranian](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md)|[Vietnamese](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md)
A Xray backend framework that can easily support many panels.
@@ -9,11 +18,14 @@ A Xray backend framework that can easily support many panels.
如果您喜欢本项目可以右上角点个star+watch持续关注本项目的进展。
使用教程:[详细使用教程](https://xrayr-project.github.io/XrayR-doc/)
## 免责声明
本项目只是本人个人学习开发并维护,本人不保证任何可用性,也不对使用本软件造成的任何后果负责。
## 特点
* 永久开源且免费。
* 支持V2rayTrojan Shadowsocks多种协议。
* 支持Vless和XTLS等新特性。
@@ -26,39 +38,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)
## 配置文件及详细使用教程

104
README_Fa.md Normal file
View File

@@ -0,0 +1,104 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[Iranian(farsi) README](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md), [Vietnamese(vi) README](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md), [English(en) README](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)
یک فریمورک بک اند مبتنی بر xray که از چند از پنل پشتیبانی می کند
یک چارچوب بک‌اند مبتنی بر Xray که از پروتکل‌های V2ay، Trojan و Shadowsocks پشتیبانی می‌کند، به راحتی قابل گسترش است و از اتصال چند پنل پشتیبانی می‌کند.
اگر این پروژه را دوست دارید، می توانید با کلیک بر روی ستاره+ساعت در گوشه بالا سمت راست به ادامه روند پیشرفت این پروژه توجه کنید.
آموزش:[اموزش با جزئیات](https://xrayr-project.github.io/XrayR-doc/)
## سلب مسئولیت
این پروژه فقط مطالعه، توسعه و نگهداری شخصی من است. من هیچ گونه قابلیت استفاده را تضمین نمی کنم و مسئولیتی در قبال عواقب ناشی از استفاده از این نرم افزار ندارم.
## امکانات
* منبع باز دائمی و رایگان
* پشتیبانی از چندین پروتکل V2ray، Trojan، Shadowsocks.
* پشتیبانی از ویژگی های جدید مانند Vless و XTLS.
* پشتیبانی از اتصال یک نمونه چند پانل، چند گره، بدون نیاز به شروع مکرر.
* پشتیبانی محدود IP آنلاین
* پشتیبانی از سطح پورت گره، محدودیت سرعت سطح کاربر.
* پیکربندی ساده و سرراست است.
* پیکربندی را تغییر دهید تا نمونه به طور خودکار راه اندازی مجدد شود.
* کامپایل و ارتقاء آن آسان است و می تواند به سرعت نسخه اصلی را به روز کند و از ویژگی های جدید Xray-core پشتیبانی می کند.
## امکانات
| امکانات | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| اطلاعات گره را دریافت کنید | √ | √ | √ |
| دریافت اطلاعات کاربر | √ | √ | √ |
| آمار ترافیک کاربران | √ | √ | √ |
| گزارش اطلاعات سرور | √ | √ | √ |
| به طور خودکار برای گواهی tls درخواست دهید | √ | √ | √ |
| تمدید خودکار گواهی tls | √ | √ | √ |
| آمار آنلاین | √ | √ | √ |
| محدودیت کاربر آنلاین | √ | √ | √ |
| قوانین حسابرسی | √ | √ | √ |
| محدودیت سرعت پورت گره | √ | √ | √ |
| محدودیت سرعت بر اساس کاربر | √ | √ | √ |
| DNS سفارشی | √ | √ | √ |
## پشتیبانی از قسمت فرانت
| قسمت فرانت | 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/) | √ | √ | √ |
## نصب نرم افزار
### نصب بصورت یکپارچه
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### استقرار نرم افزار با استفاده از 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)
## فایل های پیکربندی و آموزش های با جرئیات
[آموزش مفصل](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
## Licence
[Mozilla Public License Version 2.0](https://github.com/XrayR-project/XrayR/blob/master/LICENSE)
## Telgram
[بحث در مورد XrayR Backend](https://t.me/XrayR_project)
[کانال اعلان در مورد XrayR](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

View File

@@ -3,9 +3,11 @@ package api
import (
"encoding/json"
"regexp"
"github.com/xtls/xray-core/infra/conf"
)
// API config
// Config API config
type Config struct {
APIHost string `mapstructure:"ApiHost"`
NodeID int `mapstructure:"NodeID"`
@@ -20,7 +22,7 @@ type Config struct {
DisableCustomConfig bool `mapstructure:"DisableCustomConfig"`
}
// Node status
// NodeStatus Node status
type NodeStatus struct {
CPU float64
Mem float64
@@ -42,8 +44,10 @@ type NodeInfo struct {
TLSType string
EnableVless bool
CypherMethod string
ServerKey string
ServiceName string
Header json.RawMessage
NameServerConfig []*conf.NameServerConfig
}
type UserInfo struct {

57
api/newV2board/model.go Normal file
View File

@@ -0,0 +1,57 @@
package newV2board
import (
"encoding/json"
)
type serverConfig struct {
shadowsocks
v2ray
trojan
ServerPort int `json:"server_port"`
BaseConfig struct {
PushInterval int `json:"push_interval"`
PullInterval int `json:"pull_interval"`
} `json:"base_config"`
Routes []route `json:"routes"`
}
type shadowsocks struct {
Cipher string `json:"cipher"`
Obfs string `json:"obfs"`
ObfsSettings struct {
Path string `json:"path"`
Host string `json:"host"`
} `json:"obfs_settings"`
ServerKey string `json:"server_key"`
}
type v2ray struct {
Network string `json:"network"`
NetworkSettings struct {
Path string `json:"path"`
Headers *json.RawMessage `json:"headers"`
ServiceName string `json:"serviceName"`
Header *json.RawMessage `json:"header"`
} `json:"networkSettings"`
Tls int `json:"tls"`
}
type trojan struct {
Host string `json:"host"`
ServerName string `json:"server_name"`
}
type route struct {
Id int `json:"id"`
Match []string `json:"match"`
Action string `json:"action"`
ActionValue string `json:"action_value"`
}
type user struct {
Id int `json:"id"`
Uuid string `json:"uuid"`
SpeedLimit int `json:"speed_limit"`
}

413
api/newV2board/v2board.go Normal file
View File

@@ -0,0 +1,413 @@
package newV2board
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"regexp"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/bitly/go-simplejson"
"github.com/go-resty/resty/v2"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/infra/conf"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create an api client to the panel.
type APIClient struct {
client *resty.Client
APIHost string
NodeID int
Key string
NodeType string
EnableVless bool
EnableXTLS bool
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
resp atomic.Value
eTag string
}
// New create an api instance
func New(apiConfig *api.Config) *APIClient {
client := resty.New()
client.SetRetryCount(3)
if apiConfig.Timeout > 0 {
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
} else {
client.SetTimeout(5 * time.Second)
}
client.OnError(func(req *resty.Request, err error) {
if v, ok := err.(*resty.ResponseError); ok {
// v.Response contains the last response from the server
// v.Err contains the original error
log.Print(v.Err)
}
})
client.SetBaseURL(apiConfig.APIHost)
// Create Key for each requests
client.SetQueryParams(map[string]string{
"node_id": strconv.Itoa(apiConfig.NodeID),
"node_type": strings.ToLower(apiConfig.NodeType),
"token": apiConfig.Key,
})
// Read local rule list
localRuleList := readLocalRuleList(apiConfig.RuleListPath)
apiClient := &APIClient{
client: client,
NodeID: apiConfig.NodeID,
Key: apiConfig.Key,
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
EnableXTLS: apiConfig.EnableXTLS,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
}
return apiClient
}
// readLocalRuleList reads the local rule list file
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
LocalRuleList = make([]api.DetectRule, 0)
if path != "" {
// open the file
file, err := os.Open(path)
defer file.Close()
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
}
fileScanner := bufio.NewScanner(file)
// read line by line
for fileScanner.Scan() {
LocalRuleList = append(LocalRuleList, api.DetectRule{
ID: -1,
Pattern: regexp.MustCompile(fileScanner.Text()),
})
}
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return
}
}
return LocalRuleList
}
// Describe return a description of the client
func (c *APIClient) Describe() api.ClientInfo {
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
}
// Debug set the client debug for client
func (c *APIClient) Debug() {
c.client.SetDebug(true)
}
func (c *APIClient) assembleURL(path string) string {
return c.APIHost + path
}
func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (*simplejson.Json, error) {
if err != nil {
return nil, fmt.Errorf("request %s failed: %v", c.assembleURL(path), err)
}
if res.StatusCode() > 399 {
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), res.String(), err)
}
rtn, err := simplejson.NewJson(res.Body())
if err != nil {
return nil, fmt.Errorf("ret %s invalid", res.String())
}
return rtn, nil
}
// GetNodeInfo will pull NodeInfo Config from panel
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
server := new(serverConfig)
path := "/api/v1/server/UniProxy/config"
res, err := c.client.R().
ForceContentType("application/json").
Get(path)
nodeInfoResp, err := c.parseResponse(res, path, err)
if err != nil {
return nil, err
}
b, _ := nodeInfoResp.Encode()
json.Unmarshal(b, server)
if server.ServerPort == 0 {
return nil, errors.New("server port must > 0")
}
c.resp.Store(server)
switch c.NodeType {
case "V2ray":
nodeInfo, err = c.parseV2rayNodeResponse(server)
case "Trojan":
nodeInfo, err = c.parseTrojanNodeResponse(server)
case "Shadowsocks":
nodeInfo, err = c.parseSSNodeResponse(server)
default:
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
}
if err != nil {
return nil, fmt.Errorf("parse node info failed: %s, \nError: %v", res.String(), err)
}
return nodeInfo, nil
}
// GetUserList will pull user form panel
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var users []*user
path := "/api/v1/server/UniProxy/user"
switch c.NodeType {
case "V2ray", "Trojan", "Shadowsocks":
break
default:
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
}
res, err := c.client.R().
SetHeader("If-None-Match", c.eTag).
ForceContentType("application/json").
Get(path)
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
if res.StatusCode() == 304 {
return nil, errors.New("users no change")
}
// update etag
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTag {
c.eTag = res.Header().Get("Etag")
}
usersResp, err := c.parseResponse(res, path, err)
if err != nil {
return nil, err
}
b, _ := usersResp.Get("users").Encode()
json.Unmarshal(b, &users)
userList := make([]api.UserInfo, len(users))
for i := 0; i < len(users); i++ {
u := api.UserInfo{
UID: users[i].Id,
UUID: users[i].Uuid,
}
// Support 1.7.1 speed limit
if c.SpeedLimit > 0 {
u.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8)
} else {
u.SpeedLimit = uint64(users[i].SpeedLimit * 1000000 / 8)
}
u.DeviceLimit = c.DeviceLimit // todo waiting v2board send configuration
u.Email = u.UUID + "@v2board.user"
if c.NodeType == "Shadowsocks" {
u.Passwd = u.UUID
}
userList[i] = u
}
return &userList, nil
}
// ReportUserTraffic reports the user traffic
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
path := "/api/v1/server/UniProxy/push"
// json structure: {uid1: [u, d], uid2: [u, d], uid1: [u, d], uid3: [u, d]}
data := make(map[int][]int64, len(*userTraffic))
for _, traffic := range *userTraffic {
data[traffic.UID] = []int64{traffic.Upload, traffic.Download}
}
res, err := c.client.R().SetBody(data).ForceContentType("application/json").Post(path)
_, err = c.parseResponse(res, path, err)
if err != nil {
return err
}
return nil
}
// GetNodeRule implements the API interface
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
routes := c.resp.Load().(*serverConfig).Routes
ruleList := c.LocalRuleList
for i := range routes {
if routes[i].Action == "block" {
ruleList = append(ruleList, api.DetectRule{
ID: i,
Pattern: regexp.MustCompile(strings.Join(routes[i].Match, "|")),
})
}
}
return &ruleList, nil
}
// ReportNodeStatus implements the API interface
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
return nil
}
// ReportNodeOnlineUsers implements the API interface
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
return nil
}
// ReportIllegal implements the API interface
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
return nil
}
// parseTrojanNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
var TLSType = "tls"
if c.EnableXTLS {
TLSType = "xtls"
}
// Create GeneralNodeInfo
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: uint32(s.ServerPort),
TransportProtocol: "tcp",
EnableTLS: true,
TLSType: TLSType,
Host: s.Host,
ServiceName: s.ServerName,
NameServerConfig: s.parseDNSConfig(),
}
return nodeInfo, nil
}
// parseSSNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseSSNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
var header json.RawMessage
if s.Obfs == "http" {
path := "/"
if p := s.ObfsSettings.Path; p != "" {
if strings.HasPrefix(p, "/") {
path = p
} else {
path += p
}
}
h := simplejson.New()
h.Set("type", "http")
h.SetPath([]string{"request", "path"}, path)
header, _ = h.Encode()
}
// Create GeneralNodeInfo
return &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: uint32(s.ServerPort),
TransportProtocol: "tcp",
CypherMethod: s.Cipher,
ServerKey: s.ServerKey, // shadowsocks2022 share key
NameServerConfig: s.parseDNSConfig(),
Header: header,
}, nil
}
// parseV2rayNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
var (
TLSType = "tls"
host string
header json.RawMessage
enableTLS bool
)
if c.EnableXTLS {
TLSType = "xtls"
}
switch s.Network {
case "ws":
if s.NetworkSettings.Headers != nil {
if httpHeader, err := s.NetworkSettings.Headers.MarshalJSON(); err != nil {
return nil, err
} else {
b, _ := simplejson.NewJson(httpHeader)
host = b.Get("Host").MustString()
}
}
case "tcp":
if s.NetworkSettings.Header != nil {
if httpHeader, err := s.NetworkSettings.Header.MarshalJSON(); err != nil {
return nil, err
} else {
header = httpHeader
}
}
}
if s.Tls == 1 {
enableTLS = true
}
// Create GeneralNodeInfo
return &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: uint32(s.ServerPort),
AlterID: 0,
TransportProtocol: s.Network,
EnableTLS: enableTLS,
TLSType: TLSType,
Path: s.NetworkSettings.Path,
Host: host,
EnableVless: c.EnableVless,
ServiceName: s.NetworkSettings.ServiceName,
Header: header,
NameServerConfig: s.parseDNSConfig(),
}, nil
}
func (s *serverConfig) parseDNSConfig() (nameServerList []*conf.NameServerConfig) {
for i := range s.Routes {
if s.Routes[i].Action == "dns" {
nameServerList = append(nameServerList, &conf.NameServerConfig{
Address: &conf.Address{Address: net.ParseAddress(s.Routes[i].ActionValue)},
Domains: s.Routes[i].Match,
})
}
}
return
}

View File

@@ -0,0 +1,101 @@
package newV2board_test
import (
"testing"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/newV2board"
)
func CreateClient() api.API {
apiConfig := &api.Config{
APIHost: "http://localhost:9897",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "V2ray",
}
client := newV2board.New(apiConfig)
return client
}
func TestGetV2rayNodeInfo(t *testing.T) {
client := CreateClient()
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetSSNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Shadowsocks",
}
client := newV2board.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetTrojanNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Trojan",
}
client := newV2board.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetUserList(t *testing.T) {
client := CreateClient()
userList, err := client.GetUserList()
if err != nil {
t.Error(err)
}
t.Log(userList)
}
func TestReportReportUserTraffic(t *testing.T) {
client := CreateClient()
userList, err := client.GetUserList()
if err != nil {
t.Error(err)
}
generalUserTraffic := make([]api.UserTraffic, len(*userList))
for i, userInfo := range *userList {
generalUserTraffic[i] = api.UserTraffic{
UID: userInfo.UID,
Upload: 114514,
Download: 114514,
}
}
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)
}
}
func TestGetNodeRule(t *testing.T) {
client := CreateClient()
client.Debug()
ruleList, err := client.GetNodeRule()
if err != nil {
t.Error(err)
}
t.Log(ruleList)
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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"`
@@ -98,7 +98,6 @@ type NodeRuleItem struct {
Pattern string `json:"pattern"`
}
// IllegalReport
type IllegalReport struct {
UID int `json:"uid"`
RuleID int `json:"rule_id"`

View File

@@ -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))
@@ -591,7 +592,7 @@ func (c *APIClient) ParseSSUserListResponse(userInfoResponse *json.RawMessage) (
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedlimit = uint64(user.SpeedLimit * 1000000 / 8)
}
userList[i] = api.UserInfo{
UID: user.UID,

View File

@@ -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)

View File

@@ -13,14 +13,15 @@ import (
"sync"
"time"
"github.com/XrayR-project/XrayR/api"
"github.com/go-resty/resty/v2"
"github.com/XrayR-project/XrayR/api"
)
var (
firstPortRe = regexp.MustCompile(`(?m)port=(?P<outport>\d+)#?`) // First Port
secondPortRe = regexp.MustCompile(`(?m)port=\d+#(\d+)`) // Second Port
hostRe = regexp.MustCompile(`(?m)host=([\w\.]+)\|?`) // Host
hostRe = regexp.MustCompile(`(?m)host=([\w.]+)\|?`) // Host
)
// APIClient create a api client to the panel.
@@ -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,12 +165,12 @@ 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
disableCustomConfig := c.DisableCustomConfig
if nodeInfoResponse.Version == "2021.11" && !disableCustomConfig {
if nodeInfoResponse.Version != "" && !disableCustomConfig {
// Check if custom_config is empty
if configString, err := json.Marshal(nodeInfoResponse.CustomConfig); err != nil || string(configString) == "[]" {
log.Printf("custom_config is empty! take config from address now.")
@@ -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
}
@@ -240,7 +241,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
systemload := SystemLoad{
Uptime: strconv.FormatUint(nodeStatus.Uptime, 10),
Load: fmt.Sprintf("%.2f %.2f %.2f", nodeStatus.CPU/100, nodeStatus.CPU/100, nodeStatus.CPU/100),
Load: fmt.Sprintf("%.2f %.2f %.2f", nodeStatus.CPU/100, nodeStatus.Mem/100, nodeStatus.Disk/100),
}
res, err := c.client.R().
@@ -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
@@ -775,21 +776,21 @@ func (c *APIClient) ParseSSPanelNodeInfo(nodeInfoResponse *NodeInfoResponse) (*a
if c.NodeType == "Trojan" {
EnableTLS = true
TLSType = nodeConfig.Security // try to read security from config
TLSType = "tls"
transportProtocol = "tcp"
// Select security type
if nodeConfig.EnableXtls == "1" {
TLSType = "xtls"
}
if TLSType == "" {
TLSType = "tls" // default
} else if nodeConfig.Security != "" {
TLSType = nodeConfig.Security // try to read security from config
}
// Select transport protocol
transportProtocol = nodeConfig.Network // try to read transport protocol from config
if nodeConfig.Grpc == "1" {
transportProtocol = "grpc"
}
if nodeConfig.Network != "" {
transportProtocol = "tcp" // default
} else if nodeConfig.Network != "" {
transportProtocol = nodeConfig.Network // try to read transport protocol from config
}
}

View File

@@ -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)

View File

@@ -1,7 +1,8 @@
// Deprecated: after 2023.6.1
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"`
}

View File

@@ -3,6 +3,7 @@ package v2board
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"log"
"os"
@@ -12,9 +13,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 +83,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 +101,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 +135,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 +296,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
}
@@ -337,6 +339,8 @@ func (c *APIClient) ParseSSNodeResponse() (*api.NodeInfo, error) {
if len(*userInfo) > 0 {
port = (*userInfo)[0].Port
method = (*userInfo)[0].Method
} else {
return nil, errors.New("the number of node users is 0")
}
// Create GeneralNodeInfo
@@ -371,7 +375,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())

View File

@@ -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)

View File

@@ -12,9 +12,12 @@ import (
"sync"
"time"
"github.com/XrayR-project/XrayR/api"
"github.com/bitly/go-simplejson"
"github.com/go-resty/resty/v2"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
C "github.com/sagernet/sing/common"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create an api client to the panel.
@@ -80,7 +83,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 +101,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,12 +135,12 @@ 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
}
// GetNodeInfo will pull NodeInfo Config from sspanel
// GetNodeInfo will pull NodeInfo Config from panel
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
var nodeType string
switch c.NodeType {
@@ -181,7 +184,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
return nodeInfo, nil
}
// GetUserList will pull user form sspanel
// GetUserList will pull user form panel
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var nodeType string
switch c.NodeType {
@@ -212,16 +215,16 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
user.Email = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString()
user.Passwd = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString()
user.Method = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("cipher").MustString()
user.SpeedLimit = uint64(response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("speed_limit").MustUint64() * 1000000 / 8)
user.SpeedLimit = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("speed_limit").MustUint64() * 1000000 / 8
case "Trojan":
user.UUID = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
user.Email = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
user.SpeedLimit = uint64(response.Get("data").GetIndex(i).Get("trojan_user").Get("speed_limit").MustUint64() * 1000000 / 8)
user.SpeedLimit = response.Get("data").GetIndex(i).Get("trojan_user").Get("speed_limit").MustUint64() * 1000000 / 8
case "V2ray":
user.UUID = response.Get("data").GetIndex(i).Get("v2ray_user").Get("uuid").MustString()
user.Email = response.Get("data").GetIndex(i).Get("v2ray_user").Get("email").MustString()
user.AlterID = uint16(response.Get("data").GetIndex(i).Get("v2ray_user").Get("alter_id").MustUint64())
user.SpeedLimit = uint64(response.Get("data").GetIndex(i).Get("v2ray_user").Get("speed_limit").MustUint64() * 1000000 / 8)
user.SpeedLimit = response.Get("data").GetIndex(i).Get("v2ray_user").Get("speed_limit").MustUint64() * 1000000 / 8
}
if c.SpeedLimit > 0 {
user.SpeedLimit = uint64((c.SpeedLimit * 1000000) / 8)
@@ -266,7 +269,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
return &ruleList, nil
}
// V2board only support the rule for v2ray
// Only support the rule for v2ray
// fix: reuse config response
c.access.Lock()
defer c.access.Unlock()
@@ -287,7 +290,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
}
@@ -297,7 +300,7 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
return nil
}
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
// ParseTrojanNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
var TLSType = "tls"
if c.EnableXTLS {
@@ -312,7 +315,7 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (
host := inboundInfo.Get("streamSettings").Get("tlsSettings").Get("serverName").MustString()
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
@@ -321,39 +324,47 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (
TLSType: TLSType,
Host: host,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseSSNodeResponse parse the response for the given nodeinfor format
// ParseSSNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
var method string
var method, serverPsk string
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
inboundInfo, _ := simplejson.NewJson(marshalByte)
port := uint32(inboundInfo.Get("port").MustUint64())
userInfo, err := c.GetUserList()
if err != nil {
return nil, err
}
if len(*userInfo) > 0 {
method = (*userInfo)[0].Method
method = inboundInfo.Get("settings").Get("method").MustString()
// Shadowsocks 2022
if C.Contains(shadowaead_2022.List, method) {
serverPsk = inboundInfo.Get("settings").Get("password").MustString()
} else {
userInfo, err := c.GetUserList()
if err != nil {
return nil, err
}
if len(*userInfo) > 0 {
method = (*userInfo)[0].Method
}
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
TransportProtocol: "tcp",
CypherMethod: method,
ServerKey: serverPsk,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
// ParseV2rayNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
var TLSType string = "tls"
var TLSType = "tls"
var path, host, serviceName string
var header json.RawMessage
var enableTLS bool

View File

@@ -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)

View File

@@ -1,7 +1,7 @@
syntax = "proto3";
package xrayr.app.mydispatcher;
option csharp_namespace = "XrayR.App.Myispatcher";
option csharp_namespace = "XrayR.App.Mydispatcher";
option go_package = "github.com/XrayR-project/XrayR/app/mydispatcher";
option java_package = "com.xrayr.app.mydispatcher";
option java_multiple_files = true;

View File

@@ -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() {
@@ -139,7 +140,9 @@ func (*DefaultDispatcher) Start() error {
}
// Close implements common.Closable.
func (*DefaultDispatcher) Close() error { return nil }
func (*DefaultDispatcher) Close() error {
return nil
}
func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link, error) {
downOpt := pipe.OptionsFromContext(ctx)
@@ -172,7 +175,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)
@@ -233,7 +236,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sn
// Speed Limit and Device Limit
bucket, ok, reject := d.Limiter.GetUserBucket(sessionInbound.Tag, user.Email, sessionInbound.Source.Address.IP().String())
if reject {
newError("Devices reach the limit: ", user.Email).AtError().WriteToLog()
newError("Devices reach the limit: ", user.Email).AtWarning().WriteToLog()
common.Close(outboundLink.Writer)
common.Close(inboundLink.Writer)
common.Interrupt(outboundLink.Reader)
@@ -318,33 +321,15 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
if err != nil {
return nil, err
}
switch {
case !sniffingRequest.Enabled:
if !sniffingRequest.Enabled {
go d.routedDispatch(ctx, outbound, destination)
case destination.Network != net.Network_TCP:
// Only metadata sniff will be used for non tcp connection
result, err := sniffer(ctx, nil, true)
if err == nil {
content.Protocol = result.Protocol()
if d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain)
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
}
}
go d.routedDispatch(ctx, outbound, destination)
default:
} else {
go func() {
cReader := &cachedReader{
reader: outbound.Reader.(*pipe.Reader),
}
outbound.Reader = cReader
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
if err == nil {
content.Protocol = result.Protocol()
}
@@ -379,33 +364,15 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
ctx = session.ContextWithContent(ctx, content)
}
sniffingRequest := content.SniffingRequest
switch {
case !sniffingRequest.Enabled:
if !sniffingRequest.Enabled {
go d.routedDispatch(ctx, outbound, destination)
case destination.Network != net.Network_TCP:
// Only metadata sniff will be used for non tcp connection
result, err := sniffer(ctx, nil, true)
if err == nil {
content.Protocol = result.Protocol()
if d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain)
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
}
}
go d.routedDispatch(ctx, outbound, destination)
default:
} else {
go func() {
cReader := &cachedReader{
reader: outbound.Reader.(*pipe.Reader),
}
outbound.Reader = cReader
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
if err == nil {
content.Protocol = result.Protocol()
}
@@ -422,10 +389,11 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
d.routedDispatch(ctx, outbound, destination)
}()
}
return nil
}
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (SniffResult, error) {
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
payload := buf.New()
defer payload.Release()
@@ -451,7 +419,7 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
cReader.Cache(payload)
if !payload.IsEmpty() {
result, err := sniffer.Sniff(ctx, payload.Bytes())
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
if err != common.ErrNoClue {
return result, err
}
@@ -501,7 +469,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 != "" {
@@ -532,7 +500,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
}
if handler == nil {
handler = d.ohm.GetHandler(inTag) // Default outbound hander tag should be as same as the inbound tag
handler = d.ohm.GetHandler(inTag) // Default outbound handler tag should be as same as the inbound tag
}
// If there is no outbound with tag as same as the inbound tag

View File

@@ -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

View File

@@ -2,8 +2,6 @@ package mydispatcher
import "github.com/xtls/xray-core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
return errors.New(values...)
}

View File

@@ -4,8 +4,10 @@ import (
"context"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/bittorrent"
"github.com/xtls/xray-core/common/protocol/http"
"github.com/xtls/xray-core/common/protocol/quic"
"github.com/xtls/xray-core/common/protocol/tls"
)
@@ -22,6 +24,7 @@ type protocolSnifferWithMetadata struct {
// for both TCP and UDP connections
// It will not be shown as a traffic type for routing unless there is no other successful sniffing.
metadataSniffer bool
network net.Network
}
type Sniffer struct {
@@ -31,9 +34,11 @@ type Sniffer struct {
func NewSniffer(ctx context.Context) *Sniffer {
ret := &Sniffer{
sniffer: []protocolSnifferWithMetadata{
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false},
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false},
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false},
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP},
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP},
},
}
if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
@@ -49,11 +54,11 @@ func NewSniffer(ctx context.Context) *Sniffer {
var errUnknownContent = newError("unknown content")
func (s *Sniffer) Sniff(c context.Context, payload []byte) (SniffResult, error) {
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
var pendingSniffer []protocolSnifferWithMetadata
for _, si := range s.sniffer {
s := si.protocolSniffer
if si.metadataSniffer {
if si.metadataSniffer || si.network != network {
continue
}
result, err := s(c, payload)

View File

@@ -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(),
}
}

View File

@@ -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
}

View File

@@ -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...)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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"),
})
}

View File

@@ -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,
},
}
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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...)
}

View File

@@ -2,8 +2,6 @@ package limiter
import "github.com/xtls/xray-core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
return errors.New(values...)
}

View File

@@ -1,13 +1,24 @@
// Package limiter is to control the links that go into the dispather
// Package limiter is to control the links that go into the dispatcher
package limiter
import (
"context"
"fmt"
"strconv"
"strings"
"sync"
"time"
"github.com/eko/gocache/lib/v4/cache"
"github.com/eko/gocache/lib/v4/marshaler"
"github.com/eko/gocache/lib/v4/store"
goCacheStore "github.com/eko/gocache/store/go_cache/v4"
redisStore "github.com/eko/gocache/store/redis/v4"
"github.com/go-redis/redis/v8"
goCache "github.com/patrickmn/go-cache"
"golang.org/x/time/rate"
"github.com/XrayR-project/XrayR/api"
"github.com/juju/ratelimit"
)
type UserInfo struct {
@@ -20,8 +31,12 @@ type InboundInfo struct {
Tag string
NodeSpeedLimit uint64
UserInfo *sync.Map // Key: Email value: UserInfo
BucketHub *sync.Map // key: Email, value: *ratelimit.Bucket
UserOnlineIP *sync.Map // Key: Email Value: *sync.Map: Key: IP, Value: UID
BucketHub *sync.Map // key: Email, value: *rate.Limiter
UserOnlineIP *sync.Map // Key: Email, value: {Key: IP, value: UID}
GlobalLimit struct {
config *GlobalDeviceLimitConfig
globalOnlineIP *marshaler.Marshaler
}
}
type Limiter struct {
@@ -34,13 +49,37 @@ 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, globalLimit *GlobalDeviceLimitConfig) error {
inboundInfo := &InboundInfo{
Tag: tag,
NodeSpeedLimit: nodeSpeedLimit,
BucketHub: new(sync.Map),
UserOnlineIP: new(sync.Map),
}
if globalLimit != nil && globalLimit.Enable {
inboundInfo.GlobalLimit.config = globalLimit
// init local store
gs := goCacheStore.NewGoCache(goCache.New(time.Duration(globalLimit.Expiry)*time.Second, 1*time.Minute))
// init redis store
rs := redisStore.NewRedis(redis.NewClient(
&redis.Options{
Addr: globalLimit.RedisAddr,
Password: globalLimit.RedisPassword,
DB: globalLimit.RedisDB,
}),
store.WithExpiration(time.Duration(globalLimit.Expiry)*time.Second))
// init chained cache. First use local go-cache, if go-cache is nil, then use redis cache
cacheManager := cache.NewChain[any](
cache.New[any](gs), // go-cache is priority
cache.New[any](rs),
)
inboundInfo.GlobalLimit.globalOnlineIP = marshaler.New(cacheManager)
}
userMap := new(sync.Map)
for _, u := range *userList {
userMap.Store(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID), UserInfo{
@@ -55,7 +94,6 @@ func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList
}
func (l *Limiter) UpdateInboundLimiter(tag string, updatedUserList *[]api.UserInfo) error {
if value, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := value.(*InboundInfo)
// Update User info
@@ -65,7 +103,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)
@@ -79,7 +127,8 @@ func (l *Limiter) DeleteInboundLimiter(tag string) error {
}
func (l *Limiter) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
onlineUser := make([]api.OnlineUser, 0)
var onlineUser []api.OnlineUser
if value, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := value.(*InboundInfo)
// Clear Speed Limiter bucket for users who are not online
@@ -91,43 +140,48 @@ func (l *Limiter) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
return true
})
inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool {
email := key.(string)
ipMap := value.(*sync.Map)
ipMap.Range(func(key, value interface{}) bool {
ip := key.(string)
uid := value.(int)
ip := key.(string)
onlineUser = append(onlineUser, api.OnlineUser{UID: uid, IP: ip})
return true
})
email := key.(string)
inboundInfo.UserOnlineIP.Delete(email) // Reset online device
return true
})
} else {
return nil, fmt.Errorf("no such inbound in limiter: %s", tag)
}
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 {
var (
userLimit uint64 = 0
deviceLimit, uid int
)
inboundInfo := value.(*InboundInfo)
nodeLimit := inboundInfo.NodeSpeedLimit
var userLimit uint64 = 0
var deviceLimit int = 0
var uid int = 0
if v, ok := inboundInfo.UserInfo.Load(email); ok {
u := v.(UserInfo)
uid = u.UID
userLimit = u.SpeedLimit
deviceLimit = u.DeviceLimit
}
// Report online device
// Local device limit
ipMap := new(sync.Map)
ipMap.Store(ip, uid)
// If any device is online
if v, ok := inboundInfo.UserOnlineIP.LoadOrStore(email, ipMap); ok {
ipMap := v.(*sync.Map)
// If this ip is a new device
// If this is a new ip
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
counter := 0
ipMap.Range(func(key, value interface{}) bool {
@@ -140,11 +194,20 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
}
}
}
limit := determineRate(nodeLimit, userLimit) // If need the Speed limit
// GlobalLimit
if inboundInfo.GlobalLimit.config != nil && inboundInfo.GlobalLimit.config.Enable {
if reject := globalLimit(inboundInfo, email, uid, ip, deviceLimit); reject {
return nil, false, true
}
}
// Speed limit
limit := determineRate(nodeLimit, userLimit) // Determine the speed limit rate
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
@@ -158,6 +221,51 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
}
}
// Global device limit
func globalLimit(inboundInfo *InboundInfo, email string, uid int, ip string, deviceLimit int) bool {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(inboundInfo.GlobalLimit.config.Timeout)*time.Second)
defer cancel()
// reformat email for unique key
uniqueKey := strings.Replace(email, inboundInfo.Tag, strconv.Itoa(deviceLimit), 1)
v, err := inboundInfo.GlobalLimit.globalOnlineIP.Get(ctx, uniqueKey, new(map[string]int))
if err != nil {
if _, ok := err.(*store.NotFound); ok {
// If the email is a new device
go pushIP(inboundInfo, uniqueKey, &map[string]int{ip: uid})
} else {
newError("cache service").Base(err).AtError().WriteToLog()
}
return false
}
ipMap := v.(*map[string]int)
// Reject device reach limit directly
if deviceLimit > 0 && len(*ipMap) > deviceLimit {
return true
}
// If the ip is not in cache
if _, ok := (*ipMap)[ip]; !ok {
(*ipMap)[ip] = uid
go pushIP(inboundInfo, uniqueKey, ipMap)
}
return false
}
// push the ip to cache
func pushIP(inboundInfo *InboundInfo, uniqueKey string, ipMap *map[string]int) {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(inboundInfo.GlobalLimit.config.Timeout)*time.Second)
defer cancel()
if err := inboundInfo.GlobalLimit.globalOnlineIP.Set(ctx, uniqueKey, ipMap); err != nil {
newError("cache service").Base(err).AtError().WriteToLog()
}
}
// determineRate returns the minimum non-zero rate
func determineRate(nodeLimit, userLimit uint64) (limit uint64) {
if nodeLimit == 0 || userLimit == 0 {

10
common/limiter/model.go Normal file
View File

@@ -0,0 +1,10 @@
package limiter
type GlobalDeviceLimitConfig struct {
Enable bool `mapstructure:"Enable"`
RedisAddr string `mapstructure:"RedisAddr"` // host:port
RedisPassword string `mapstructure:"RedisPassword"`
RedisDB int `mapstructure:"RedisDB"`
Timeout int `mapstructure:"Timeout"`
Expiry int `mapstructure:"Expiry"` // second
}

View File

@@ -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)
}

View File

@@ -1,4 +1,4 @@
package cmd
package mylego
import (
"crypto"

View File

@@ -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 {

View File

@@ -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 ;)).

View 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
View 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
View 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
}

77
common/mylego/renew.go Normal file
View 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
}

View File

@@ -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/run.go Normal file
View 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")
}

95
common/mylego/setup.go Normal file
View 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("", ""))
if err != nil {
log.Panic(err)
}
case "tls":
err := client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", ""))
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)
}
}

View File

@@ -2,8 +2,6 @@ package rule
import "github.com/xtls/xray-core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
return errors.New(values...)
}

View File

@@ -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)

View File

@@ -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

183
go.mod
View File

@@ -1,96 +1,110 @@
module github.com/XrayR-project/XrayR
go 1.18
go 1.20
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/eko/gocache/lib/v4 v4.1.2
github.com/eko/gocache/store/go_cache/v4 v4.1.2
github.com/eko/gocache/store/redis/v4 v4.1.2
github.com/fsnotify/fsnotify v1.6.0
github.com/go-acme/lego/v4 v4.9.2-0.20221210101705-6695fcc87344
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/patrickmn/go-cache v2.1.0+incompatible
github.com/r3labs/diff/v2 v2.15.1
github.com/shirou/gopsutil/v3 v3.22.7
github.com/spf13/viper v1.12.0
github.com/stretchr/testify v1.8.0
github.com/urfave/cli v1.22.9
github.com/xtls/xray-core v1.5.9
golang.org/x/net v0.0.0-20220812174116-3211cb980234
github.com/sagernet/sing v0.1.7
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7
github.com/shirou/gopsutil/v3 v3.23.1
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.1
github.com/xtls/xray-core v1.7.5
golang.org/x/crypto v0.6.0
golang.org/x/net v0.7.0
golang.org/x/time v0.3.0
google.golang.org/protobuf v1.28.1
)
require (
cloud.google.com/go/compute v1.6.1 // indirect
cloud.google.com/go/compute v1.15.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // 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/beorn7/perks v1.0.1 // 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/cheekybits/genny v1.0.0 // 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.2.0 // 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.93.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/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // 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/gofrs/uuid v4.3.1+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/btree v1.1.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // 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.1 // indirect
github.com/googleapis/gax-go/v2 v2.7.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/cpuid/v2 v2.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.2.3 // 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.28.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-beta.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // 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
@@ -98,79 +112,82 @@ 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.8.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
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pires/go-proxyproto v0.6.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
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.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
github.com/quic-go/quic-go v0.32.0 // indirect
github.com/refraction-networking/utls v1.2.2-0.20230207151345-a75a4b484849 // 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/sagernet/sing v0.0.0-20220714145306-09b55ce4b6d0 // indirect
github.com/sagernet/sing-shadowsocks v0.0.0-20220716012931-952ae62e05d7 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // 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/wireguard-go v0.0.0-20221116151939-c99467f53f2c // 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.3 // 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/subosito/gotenv v1.3.0 // 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/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/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.4.2 // 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.11 // indirect
github.com/tklauser/numcpus v0.6.0 // 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-20230107031059-4610f88d00f3 // 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-20220714194419-4cadf0a12139 // indirect
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // 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-20220728004956-3c1f35247d10 // 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.11 // indirect
google.golang.org/api v0.81.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/ratelimit v0.2.0 // indirect
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.5.0 // indirect
google.golang.org/api v0.107.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9 // indirect
google.golang.org/grpc v1.48.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/ns1/ns1-go.v2 v2.6.2 // indirect
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect
google.golang.org/grpc v1.53.0 // indirect
gopkg.in/ini.v1 v1.67.0 // 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
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // 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

631
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -6,15 +6,15 @@ 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
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
BufferSize: 64 # The internal cache size of each connection, kB
BufferSize: 64 # The internal cache size of each connection, kB
Nodes:
-
PanelType: "SSpanel" # Panel type: SSpanel, V2board, PMpanel, Proxypanel, V2RaySocks
PanelType: "SSpanel" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks
ApiConfig:
ApiHost: "http://127.0.0.1:667"
ApiKey: "123"
@@ -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:
Enable: false # Enable the global device limit of a user
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
-
@@ -42,7 +54,7 @@ Nodes:
Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable
CertConfig:
CertMode: dns # Option about how to get certificate: none, file, http, dns. Choose "none" will forcedly disable the tls config.
CertMode: dns # Option about how to get certificate: none, file, http, tls, dns. Choose "none" will forcedly disable the tls config.
CertDomain: "node1.test.com" # Domain to cert
CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
KeyFile: /etc/XrayR/cert/node1.test.com.key
@@ -52,7 +64,7 @@ Nodes:
ALICLOUD_ACCESS_KEY: aaa
ALICLOUD_SECRET_KEY: bbb
# -
# PanelType: "V2board" # Panel type: SSpanel, V2board
# PanelType: "NewV2board" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks
# ApiConfig:
# ApiHost: "http://127.0.0.1:668"
# ApiKey: "123"
@@ -77,4 +89,3 @@ Nodes:
# DNSEnv: # DNS ENV option used by DNS provider
# ALICLOUD_ACCESS_KEY: aaa
# ALICLOUD_SECRET_KEY: bbb

View File

@@ -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
}
}
]

View File

@@ -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"
}
]

View File

@@ -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"

View File

@@ -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"
}

View File

@@ -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.2"
version = "0.9.0"
codename = "XrayR"
intro = "A Xray backend that supports many panels"
)
@@ -56,7 +57,7 @@ func getConfig() *viper.Viper {
}
if err := config.ReadInConfig(); err != nil {
log.Panicf("Fatal error config file: %s \n", err)
log.Panicf("Config file error: %s \n", err)
}
config.WatchConfig() // Watch the config
@@ -73,7 +74,9 @@ func main() {
config := getConfig()
panelConfig := &panel.Config{}
config.Unmarshal(panelConfig)
if err := config.Unmarshal(panelConfig); err != nil {
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
}
p := panel.New(panelConfig)
lastTime := time.Now()
config.OnConfigChange(func(e fsnotify.Event) {
@@ -84,7 +87,9 @@ func main() {
p.Close()
// Delete old instance and trigger GC
runtime.GC()
config.Unmarshal(panelConfig)
if err := config.Unmarshal(panelConfig); err != nil {
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
}
p.Start()
lastTime = time.Now()
}
@@ -92,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
{

View File

@@ -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"
}
]
}

View File

@@ -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"`

View File

@@ -10,8 +10,8 @@ func getDefaultLogConfig() *LogConfig {
}
}
func getDefaultConnetionConfig() *ConnetionConfig {
return &ConnetionConfig{
func getDefaultConnectionConfig() *ConnectionConfig {
return &ConnectionConfig{
Handshake: 4,
ConnIdle: 30,
UplinkOnly: 2,

View File

@@ -2,12 +2,21 @@ package panel
import (
"encoding/json"
io "io/ioutil"
"log"
"os"
"sync"
"github.com/XrayR-project/XrayR/api/newV2board"
"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 +26,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 +58,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 {
@@ -64,14 +66,21 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
}
}
}
// init controller's DNS config
// for _, config := range p.panelConfig.NodesConfig {
// config.ControllerConfig.DNSConfig = coreDnsConfig
// }
dnsConfig, err := coreDnsConfig.Build()
if err != nil {
log.Panicf("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help: %s", err)
}
// 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 +93,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 +103,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 +112,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 +122,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 +131,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()
@@ -150,7 +159,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
return server
}
// Start Start the panel
// Start the panel
func (p *Panel) Start() {
p.access.Lock()
defer p.access.Unlock()
@@ -161,14 +170,18 @@ func (p *Panel) Start() {
log.Panicf("Failed to start instance: %s", err)
}
p.Server = server
// Load Nodes config
for _, nodeConfig := range p.panelConfig.NodesConfig {
var apiClient api.API
switch nodeConfig.PanelType {
case "SSpanel":
apiClient = sspanel.New(nodeConfig.ApiConfig)
// todo Deprecated after 2023.6.1
case "V2board":
apiClient = v2board.New(nodeConfig.ApiConfig)
case "NewV2board":
apiClient = newV2board.New(nodeConfig.ApiConfig)
case "PMpanel":
apiClient = pmpanel.New(nodeConfig.ApiConfig)
case "Proxypanel":
@@ -202,7 +215,7 @@ func (p *Panel) Start() {
return
}
// Close Close the panel
// Close the panel
func (p *Panel) Close() {
p.access.Lock()
defer p.access.Unlock()
@@ -218,21 +231,21 @@ func (p *Panel) Close() {
return
}
func parseConnectionConfig(c *ConnetionConfig) (policy *conf.Policy) {
connetionConfig := getDefaultConnetionConfig()
func parseConnectionConfig(c *ConnectionConfig) (policy *conf.Policy) {
connectionConfig := getDefaultConnectionConfig()
if c != nil {
if _, err := diff.Merge(connetionConfig, c, connetionConfig); err != nil {
log.Panicf("Read ConnetionConfig failed: %s", err)
if _, err := diff.Merge(connectionConfig, c, connectionConfig); err != nil {
log.Panicf("Read ConnectionConfig failed: %s", err)
}
}
policy = &conf.Policy{
StatsUserUplink: true,
StatsUserDownlink: true,
Handshake: &connetionConfig.Handshake,
ConnectionIdle: &connetionConfig.ConnIdle,
UplinkOnly: &connetionConfig.UplinkOnly,
DownlinkOnly: &connetionConfig.DownlinkOnly,
BufferSize: &connetionConfig.BufferSize,
Handshake: &connectionConfig.Handshake,
ConnectionIdle: &connectionConfig.ConnIdle,
UplinkOnly: &connectionConfig.UplinkOnly,
DownlinkOnly: &connectionConfig.DownlinkOnly,
BufferSize: &connectionConfig.BufferSize,
}
return

View File

@@ -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 {

View File

@@ -4,21 +4,24 @@ 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 {
err := c.ihm.RemoveHandler(context.Background(), tag)
err := c.ibm.RemoveHandler(context.Background(), tag)
return err
}
func (c *Controller) removeOutbound(tag string) error {
err := c.ohm.RemoveHandler(context.Background(), tag)
err := c.obm.RemoveHandler(context.Background(), tag)
return err
}
@@ -31,7 +34,7 @@ func (c *Controller) addInbound(config *core.InboundHandlerConfig) error {
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := c.ihm.AddHandler(context.Background(), handler); err != nil {
if err := c.ibm.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
@@ -46,16 +49,16 @@ func (c *Controller) addOutbound(config *core.OutboundHandlerConfig) error {
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := c.ohm.AddHandler(context.Background(), handler); err != nil {
if err := c.obm.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
}
func (c *Controller) addUsers(users []*protocol.User, tag string) error {
handler, err := c.ihm.GetHandler(context.Background(), tag)
handler, err := c.ibm.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 {
@@ -80,9 +83,9 @@ 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)
handler, err := c.ibm.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 {
@@ -102,25 +105,35 @@ func (c *Controller) removeUsers(users []string, tag string) error {
return nil
}
func (c *Controller) getTraffic(email string) (up int64, down int64) {
func (c *Controller) getTraffic(email string) (up int64, down int64, upCounter stats.Counter, downCounter stats.Counter) {
upName := "user>>>" + email + ">>>traffic>>>uplink"
downName := "user>>>" + email + ">>>traffic>>>downlink"
upCounter := c.stm.GetCounter(upName)
downCounter := c.stm.GetCounter(downName)
if upCounter != nil {
upCounter = c.stm.GetCounter(upName)
downCounter = c.stm.GetCounter(downName)
if upCounter != nil && upCounter.Value() != 0 {
up = upCounter.Value()
upCounter.Set(0)
} else {
upCounter = nil
}
if downCounter != nil {
if downCounter != nil && downCounter.Value() != 0 {
down = downCounter.Value()
downCounter.Set(0)
} else {
downCounter = nil
}
return up, down
return up, down, upCounter, downCounter
}
func (c *Controller) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo) error {
err := c.dispatcher.Limiter.AddInboundLimiter(tag, nodeSpeedLimit, userList)
func (c *Controller) resetTraffic(upCounterList *[]stats.Counter, downCounterList *[]stats.Counter) {
for _, upCounter := range *upCounterList {
upCounter.Set(0)
}
for _, downCounter := range *downCounterList {
downCounter.Set(0)
}
}
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
}

View File

@@ -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,23 +13,41 @@ 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/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
clientInfo api.ClientInfo
apiClient api.API
nodeInfo *api.NodeInfo
Tag string
userList *[]api.UserInfo
nodeInfoMonitorPeriodic *task.Periodic
userReportPeriodic *task.Periodic
panelType string
ihm inbound.Manager
ohm outbound.Manager
stm stats.Manager
dispatcher *mydispatcher.DefaultDispatcher
server *core.Instance
config *Config
clientInfo api.ClientInfo
apiClient api.API
nodeInfo *api.NodeInfo
Tag string
userList *[]api.UserInfo
tasks []periodicTask
limitedUsers map[api.UserInfo]LimitInfo
warnedUsers map[api.UserInfo]int
panelType string
ibm inbound.Manager
obm outbound.Manager
stm stats.Manager
dispatcher *mydispatcher.DefaultDispatcher
startAt time.Time
}
type periodicTask struct {
tag string
*task.Periodic
}
// New return a Controller service with default parameters.
@@ -43,11 +57,13 @@ func New(server *core.Instance, api api.API, config *Config, panelType string) *
config: config,
apiClient: api,
panelType: panelType,
ihm: server.GetFeature(inbound.ManagerType()).(inbound.Manager),
ohm: server.GetFeature(outbound.ManagerType()).(outbound.Manager),
ibm: server.GetFeature(inbound.ManagerType()).(inbound.Manager),
obm: 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
}
@@ -61,6 +77,7 @@ func (c *Controller) Start() error {
}
c.nodeInfo = newNodeInfo
c.Tag = c.buildNodeTag()
// Add new tag
err = c.addNewTag(newNodeInfo)
if err != nil {
@@ -73,17 +90,19 @@ func (c *Controller) Start() error {
return err
}
// sync controller userList
c.userList = userInfo
err = c.addNewUser(userInfo, newNodeInfo)
if err != nil {
return err
}
//sync controller userList
c.userList = userInfo
// 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
if !c.config.DisableGetRule {
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
@@ -94,49 +113,70 @@ func (c *Controller) Start() error {
}
}
}
c.nodeInfoMonitorPeriodic = &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.nodeInfoMonitor,
}
c.userReportPeriodic = &task.Periodic{
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()
}()
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()
}()
// Init AutoSpeedLimitConfig
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)
}
// Add periodic tasks
c.tasks = append(c.tasks,
periodicTask{
tag: "node monitor",
Periodic: &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.nodeInfoMonitor,
}},
periodicTask{
tag: "user monitor",
Periodic: &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.userInfoMonitor,
}},
)
// Check cert service in need
if c.nodeInfo.EnableTLS {
c.tasks = append(c.tasks, periodicTask{
tag: "cert monitor",
Periodic: &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second * 60,
Execute: c.certMonitor,
}})
}
// Start periodic tasks
for i := range c.tasks {
log.Printf("%s Start %s periodic task", c.logPrefix(), c.tasks[i].tag)
go c.tasks[i].Start()
}
return nil
}
// Close implement the Close() function of the service interface
func (c *Controller) Close() error {
if c.nodeInfoMonitorPeriodic != nil {
err := c.nodeInfoMonitorPeriodic.Close()
if err != nil {
log.Panicf("node info periodic close failed: %s", err)
for i := range c.tasks {
if c.tasks[i].Periodic != nil {
if err := c.tasks[i].Periodic.Close(); err != nil {
log.Panicf("%s %s periodic task close failed: %s", c.logPrefix(), c.tasks[i].tag, err)
}
}
}
if c.nodeInfoMonitorPeriodic != nil {
err := c.userReportPeriodic.Close()
if err != nil {
log.Panicf("user report periodic close failed: %s", err)
}
}
return nil
}
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 {
@@ -145,18 +185,24 @@ func (c *Controller) nodeInfoMonitor() (err error) {
}
// Update User
var usersChanged = true
newUserInfo, err := c.apiClient.GetUserList()
if err != nil {
log.Print(err)
return nil
if err.Error() == "users no change" {
usersChanged = false
newUserInfo = c.userList
} else {
log.Print(err)
return nil
}
}
var nodeInfoChanged = false
// 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 +224,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
}
@@ -195,64 +241,56 @@ 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()
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)
if err != nil {
log.Print(err)
}
}
if nodeInfoChanged {
err = c.addNewUser(newUserInfo, newNodeInfo)
if err != nil {
log.Print(err)
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
}
} else {
deleted, added := compareUserList(c.userList, newUserInfo)
if len(deleted) > 0 {
deletedEmail := make([]string, len(deleted))
for i, u := range deleted {
deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag, u.Email, u.UID)
var deleted, added []api.UserInfo
if usersChanged {
deleted, added = compareUserList(c.userList, newUserInfo)
if len(deleted) > 0 {
deletedEmail := make([]string, len(deleted))
for i, u := range deleted {
deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag, u.Email, u.UID)
}
err := c.removeUsers(deletedEmail, c.Tag)
if err != nil {
log.Print(err)
}
}
err := c.removeUsers(deletedEmail, c.Tag)
if err != nil {
log.Print(err)
if len(added) > 0 {
err = c.addNewUser(&added, c.nodeInfo)
if err != nil {
log.Print(err)
}
// Update Limiter
if err := c.UpdateInboundLimiter(c.Tag, &added); err != nil {
log.Print(err)
}
}
}
if len(added) > 0 {
err = c.addNewUser(&added, c.nodeInfo)
if err != nil {
log.Print(err)
}
// Update Limiter
if err := c.UpdateInboundLimiter(c.Tag, &added); err != nil {
log.Print(err)
}
}
log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(deleted), len(added))
log.Printf("%s %d user deleted, %d user added", c.logPrefix(), len(deleted), len(added))
}
c.userList = newUserInfo
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 +326,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
@@ -341,7 +379,8 @@ func (c *Controller) addInboundForSSPlugin(newNodeInfo api.NodeInfo) (err error)
func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo) (err error) {
users := make([]*protocol.User, 0)
if nodeInfo.NodeType == "V2ray" {
switch nodeInfo.NodeType {
case "V2ray":
if nodeInfo.EnableVless {
users = c.buildVlessUser(userInfo)
} else {
@@ -354,51 +393,52 @@ func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo
}
users = c.buildVmessUser(userInfo, alterID)
}
} else if nodeInfo.NodeType == "Trojan" {
case "Trojan":
users = c.buildTrojanUser(userInfo)
} else if nodeInfo.NodeType == "Shadowsocks" {
case "Shadowsocks":
users = c.buildSSUser(userInfo, nodeInfo.CypherMethod)
} else if nodeInfo.NodeType == "Shadowsocks-Plugin" {
case "Shadowsocks-Plugin":
users = c.buildSSPluginUser(userInfo)
} else {
default:
return fmt.Errorf("unsupported node type: %s", nodeInfo.NodeType)
}
err = c.addUsers(users, c.Tag)
if err != nil {
return err
}
log.Printf("[%s: %d] Added %d new users", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(*userInfo))
log.Printf("%s Added %d new users", c.logPrefix(), len(*userInfo))
return nil
}
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 +449,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,23 +481,86 @@ 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("%s Limited users:", c.logPrefix())
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
userTraffic := make([]api.UserTraffic, 0)
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 := c.getTraffic(c.buildUserTag(&user))
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,
Upload: up,
Download: down})
if upCounter != nil {
upCounterList = append(upCounterList, upCounter)
}
if downCounter != nil {
downCounterList = append(downCounterList, downCounter)
}
} else {
delete(c.warnedUsers, user)
}
}
if len(userTraffic) > 0 && !c.config.DisableUploadTraffic {
err = c.apiClient.ReportUserTraffic(&userTraffic)
if len(limitedUsers) > 0 {
if err := c.UpdateInboundLimiter(c.Tag, &limitedUsers); err != nil {
log.Print(err)
}
}
if len(userTraffic) > 0 {
var err error // Define an empty error
if !c.config.DisableUploadTraffic {
err = c.apiClient.ReportUserTraffic(&userTraffic)
}
// If report traffic error, not clear the traffic
if err != nil {
log.Print(err)
} else {
c.resetTraffic(&upCounterList, &downCounterList)
}
}
@@ -452,9 +571,10 @@ func (c *Controller) userInfoMonitor() (err error) {
if err = c.apiClient.ReportNodeOnlineUsers(onlineDevice); err != nil {
log.Print(err)
} else {
log.Printf("[%s: %d] Report %d online users", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(*onlineDevice))
log.Printf("%s Report %d online users", c.logPrefix(), len(*onlineDevice))
}
}
// Report Illegal user
if detectResult, err := c.GetDetectResult(c.Tag); err != nil {
log.Print(err)
@@ -462,7 +582,7 @@ func (c *Controller) userInfoMonitor() (err error) {
if err = c.apiClient.ReportIllegal(detectResult); err != nil {
log.Print(err)
} else {
log.Printf("[%s: %d] Report %d illegal behaviors", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(*detectResult))
log.Printf("%s Report %d illegal behaviors", c.logPrefix(), len(*detectResult))
}
}
@@ -472,3 +592,26 @@ func (c *Controller) userInfoMonitor() (err error) {
func (c *Controller) buildNodeTag() string {
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.config.ListenIP, c.nodeInfo.Port)
}
func (c *Controller) logPrefix() string {
return fmt.Sprintf("[%s] %s(ID=%d)", c.clientInfo.APIHost, c.nodeInfo.NodeType, c.nodeInfo.NodeID)
}
// Check Cert
func (c *Controller) certMonitor() error {
if c.nodeInfo.EnableTLS {
switch c.config.CertConfig.CertMode {
case "dns", "http", "tls":
lego, err := mylego.New(c.config.CertConfig)
if err != nil {
log.Print(err)
}
// Xray-core supports the OcspStapling certification hot renew
_, _, _, err = lego.RenewCert()
if err != nil {
log.Print(err)
}
}
}
return nil
}

View File

@@ -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()
{

View File

@@ -0,0 +1,7 @@
package controller
import "github.com/xtls/xray-core/common/errors"
func newError(values ...interface{}) *errors.Error {
return errors.New(values...)
}

View File

@@ -1,28 +1,34 @@
//Package generate the InbounderConfig used by add inbound
// Package controller Package generate the InboundConfig used by add inbound
package controller
import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/common/legocmd"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
C "github.com/sagernet/sing/common"
"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
@@ -48,9 +54,10 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
setting json.RawMessage
)
var proxySetting interface{}
var proxySetting any
// Build Protocol and Protocol setting
if nodeInfo.NodeType == "V2ray" {
switch nodeInfo.NodeType {
case "V2ray":
if nodeInfo.EnableVless {
protocol = "vless"
// Enable fallback
@@ -73,7 +80,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
protocol = "vmess"
proxySetting = &conf.VMessInboundConfig{}
}
} else if nodeInfo.NodeType == "Trojan" {
case "Trojan":
protocol = "trojan"
// Enable fallback
if config.EnableFallback {
@@ -88,23 +95,36 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
} else {
proxySetting = &conf.TrojanServerConfig{}
}
} else if nodeInfo.NodeType == "Shadowsocks" || nodeInfo.NodeType == "Shadowsocks-Plugin" {
case "Shadowsocks", "Shadowsocks-Plugin":
protocol = "shadowsocks"
proxySetting = &conf.ShadowsocksServerConfig{}
randomPasswd := uuid.New()
defaultSSuser := &conf.ShadowsocksUserConfig{
Cipher: "aes-128-gcm",
Password: randomPasswd.String(),
cipher := strings.ToLower(nodeInfo.CypherMethod)
proxySetting = &conf.ShadowsocksServerConfig{
Cipher: cipher,
Password: nodeInfo.ServerKey, // shadowsocks2022 shareKey
}
proxySetting, _ := proxySetting.(*conf.ShadowsocksServerConfig)
proxySetting.Users = append(proxySetting.Users, defaultSSuser)
// shadowsocks must have a random password
// shadowsocks2022's password == user PSK, thus should a length of string >= 32 and base64 encoder
b := make([]byte, 32)
rand.Read(b)
randPasswd := hex.EncodeToString(b)
if C.Contains(shadowaead_2022.List, cipher) {
proxySetting.Users = append(proxySetting.Users, &conf.ShadowsocksUserConfig{
Password: base64.StdEncoding.EncodeToString(b),
})
} else {
proxySetting.Password = randPasswd
}
proxySetting.NetworkList = &conf.NetworkList{"tcp", "udp"}
proxySetting.IVCheck = true
if config.DisableIVCheck {
proxySetting.IVCheck = false
}
} else if nodeInfo.NodeType == "dokodemo-door" {
case "dokodemo-door":
protocol = "dokodemo-door"
proxySetting = struct {
Host string `json:"address"`
@@ -113,14 +133,16 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
Host: "v1.mux.cool",
NetworkList: []string{"tcp", "udp"},
}
} else {
return nil, fmt.Errorf("Unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks, and Shadowsocks-Plugin", nodeInfo.NodeType)
default:
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)
}
inboundDetourConfig.Protocol = protocol
inboundDetourConfig.Settings = &setting
// Build streamSettings
streamSetting = new(conf.StreamConfig)
@@ -129,13 +151,15 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
if err != nil {
return nil, fmt.Errorf("convert TransportProtocol failed: %s", err)
}
if networkType == "tcp" {
switch networkType {
case "tcp":
tcpSetting := &conf.TCPConfig{
AcceptProxyProtocol: config.EnableProxyProtocol,
HeaderConfig: nodeInfo.Header,
}
streamSetting.TCPSettings = tcpSetting
} else if networkType == "websocket" {
case "websocket":
headers := make(map[string]string)
headers["Host"] = nodeInfo.Host
wsSettings := &conf.WebSocketConfig{
@@ -144,14 +168,14 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
Headers: headers,
}
streamSetting.WSSettings = wsSettings
} else if networkType == "http" {
case "http":
hosts := conf.StringList{nodeInfo.Host}
httpSettings := &conf.HTTPConfig{
Host: &hosts,
Path: nodeInfo.Path,
}
streamSetting.HTTPSettings = httpSettings
} else if networkType == "grpc" {
case "grpc":
grpcSettings := &conf.GRPCConfig{
ServiceName: nodeInfo.ServiceName,
}
@@ -159,6 +183,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
}
streamSetting.Network = &transportProtocol
// Build TLS and XTLS settings
if nodeInfo.EnableTLS && config.CertConfig.CertMode != "none" {
streamSetting.Security = nodeInfo.TLSType
@@ -181,6 +206,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
streamSetting.XTLSSettings = xtlsSettings
}
}
// Support ProxyProtocol for any transport protocol
if networkType != "tcp" && networkType != "ws" && config.EnableProxyProtocol {
sockoptConfig := &conf.SocketConfig{
@@ -188,60 +214,59 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
}
streamSetting.SocketSettings = sockoptConfig
}
inboundDetourConfig.Protocol = protocol
inboundDetourConfig.StreamSetting = streamSetting
inboundDetourConfig.Settings = &setting
return inboundDetourConfig.Build()
}
func getCertFile(certConfig *CertConfig) (certFile string, keyFile string, err error) {
if certConfig.CertMode == "file" {
func getCertFile(certConfig *mylego.CertConfig) (certFile string, keyFile string, err error) {
switch certConfig.CertMode {
case "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()
case "dns":
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()
case "http", "tls":
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
default:
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 +281,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,

View File

@@ -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",

View File

@@ -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()

View File

@@ -1,19 +1,29 @@
package controller
import (
"encoding/base64"
"fmt"
"strings"
"github.com/XrayR-project/XrayR/api"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
C "github.com/sagernet/sing/common"
"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/shadowsocks_2022"
"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}
var AEADMethod = map[shadowsocks.CipherType]uint8{
shadowsocks.CipherType_AES_128_GCM: 0,
shadowsocks.CipherType_AES_256_GCM: 0,
shadowsocks.CipherType_CHACHA20_POLY1305: 0,
shadowsocks.CipherType_XCHACHA20_POLY1305: 0,
}
func (c *Controller) buildVmessUser(userInfo *[]api.UserInfo, serverAlterID uint16) (users []*protocol.User) {
users = make([]*protocol.User, len(*userInfo))
@@ -37,7 +47,7 @@ func (c *Controller) buildVlessUser(userInfo *[]api.UserInfo) (users []*protocol
for i, user := range *userInfo {
vlessAccount := &vless.Account{
Id: user.UUID,
Flow: "xtls-rprx-direct",
Flow: "xtls-rprx-vision,none",
}
users[i] = &protocol.User{
Level: 0,
@@ -65,43 +75,75 @@ func (c *Controller) buildTrojanUser(userInfo *[]api.UserInfo) (users []*protoco
}
func (c *Controller) buildSSUser(userInfo *[]api.UserInfo, method string) (users []*protocol.User) {
users = make([]*protocol.User, 0)
users = make([]*protocol.User, len(*userInfo))
cypherMethod := cipherFromString(method)
for _, user := range *userInfo {
ssAccount := &shadowsocks.Account{
Password: user.Passwd,
CipherType: cypherMethod,
for i, user := range *userInfo {
// shadowsocks2022 Key = "openssl rand -base64 32" and multi users needn't cipher method
if C.Contains(shadowaead_2022.List, strings.ToLower(method)) {
e := c.buildUserTag(&user)
userKey, err := c.checkShadowsocksPassword(user.Passwd, method)
if err != nil {
newError(fmt.Errorf("[UID: %d] %s", user.UID, err)).AtError().WriteToLog()
continue
}
users[i] = &protocol.User{
Level: 0,
Email: e,
Account: serial.ToTypedMessage(&shadowsocks_2022.User{
Key: userKey,
Email: e,
Level: 0,
}),
}
} else {
users[i] = &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(&shadowsocks.Account{
Password: user.Passwd,
CipherType: cipherFromString(method),
}),
}
}
users = append(users, &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(ssAccount),
})
}
return users
}
func (c *Controller) buildSSPluginUser(userInfo *[]api.UserInfo) (users []*protocol.User) {
users = make([]*protocol.User, 0)
users = make([]*protocol.User, len(*userInfo))
for _, user := range *userInfo {
// Check if the cypher method is AEAD
cypherMethod := cipherFromString(user.Method)
for _, aeadMethod := range AEADMethod {
if aeadMethod == cypherMethod {
ssAccount := &shadowsocks.Account{
Password: user.Passwd,
CipherType: cypherMethod,
for i, user := range *userInfo {
// shadowsocks2022 Key = openssl rand -base64 32 and multi users needn't cipher method
if C.Contains(shadowaead_2022.List, strings.ToLower(user.Method)) {
e := c.buildUserTag(&user)
userKey, err := c.checkShadowsocksPassword(user.Passwd, user.Method)
if err != nil {
newError(fmt.Errorf("[UID: %d] %s", user.UID, err)).AtError().WriteToLog()
continue
}
users[i] = &protocol.User{
Level: 0,
Email: e,
Account: serial.ToTypedMessage(&shadowsocks_2022.User{
Key: userKey,
Email: e,
Level: 0,
}),
}
} else {
// Check if the cypher method is AEAD
cypherMethod := cipherFromString(user.Method)
if _, ok := AEADMethod[cypherMethod]; ok {
users[i] = &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(&shadowsocks.Account{
Password: user.Passwd,
CipherType: cypherMethod,
}),
}
users = append(users, &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(ssAccount),
})
}
}
}
return users
}
@@ -124,3 +166,23 @@ func cipherFromString(c string) shadowsocks.CipherType {
func (c *Controller) buildUserTag(user *api.UserInfo) string {
return fmt.Sprintf("%s|%s|%d", c.Tag, user.Email, user.UID)
}
func (c *Controller) checkShadowsocksPassword(password string, method string) (string, error) {
if strings.Contains(c.panelType, "V2board") {
var userKey string
if len(password) < 16 {
return "", newError("shadowsocks2022 key's length must be greater than 16").AtWarning()
}
if method == "2022-blake3-aes-128-gcm" {
userKey = password[:16]
} else {
if len(password) < 32 {
return "", newError("shadowsocks2022 key's length must be greater than 32").AtWarning()
}
userKey = password[:32]
}
return base64.StdEncoding.EncodeToString([]byte(userKey)), nil
} else {
return password, nil
}
}

View File

@@ -1,5 +1,5 @@
// Package service contains all the services used by XrayR
// To implement an service, one needs to implement the interface below.
// To implement a service, one needs to implement the interface below.
package service
// Service is the interface of all the services running in the panel