mirror of
https://github.com/OutlineFoundation/outline-server.git
synced 2026-05-13 13:58:57 +00:00
Compare commits
26 commits
server-v1.
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de566bfc9f | ||
|
|
38eca18e93 | ||
|
|
847d66f8e4 | ||
|
|
eb1ae915a8 | ||
|
|
1f1c03cfc4 | ||
|
|
23e468b434 | ||
|
|
fa185b0f90 | ||
|
|
326b527d0f | ||
|
|
f9ea8edbb5 | ||
|
|
db19f85194 | ||
|
|
48d336d95b | ||
|
|
3370399012 | ||
|
|
3d2ce1f93e | ||
|
|
846c0685c3 | ||
|
|
9b44fe58b1 | ||
|
|
26803710c9 | ||
|
|
4f59eea8a9 | ||
|
|
0bfa80262c | ||
|
|
9dbfc44a06 | ||
|
|
9013feafa7 | ||
|
|
d9aa8560ce | ||
|
|
067d0d47ae | ||
|
|
d262f5242f | ||
|
|
15a9e54e5e | ||
|
|
47f24520d3 | ||
|
|
34b5e7a2e8 |
25 changed files with 348 additions and 298 deletions
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
|
@ -1,4 +1,4 @@
|
|||
* @Jigsaw-Code/outline-dev
|
||||
* @OutlineFoundation/outline-dev
|
||||
|
||||
/src/server_manager/model/ @fortuna
|
||||
/src/shadowbox/ @fortuna
|
||||
|
|
|
|||
45
.github/workflows/build_and_test_debug.yml
vendored
45
.github/workflows/build_and_test_debug.yml
vendored
|
|
@ -43,8 +43,16 @@ jobs:
|
|||
node-version: 18
|
||||
cache: npm
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: false
|
||||
|
||||
- name: Install NPM Dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 'true'
|
||||
|
||||
- name: Lint
|
||||
run: ./task lint
|
||||
|
|
@ -81,6 +89,40 @@ jobs:
|
|||
files: |
|
||||
src/shadowbox/server/api.yml
|
||||
|
||||
shadowbox-arm64:
|
||||
name: Shadowbox (arm64)
|
||||
runs-on: ubuntu-22.04-arm
|
||||
needs: lint
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.4
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: npm
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: false
|
||||
|
||||
- name: Install NPM Dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 'true'
|
||||
|
||||
- name: Shadowbox Debug Build (arm64)
|
||||
run: TARGET_ARCH=aarch64 ./task shadowbox:build
|
||||
|
||||
- name: Shadowbox Unit Test
|
||||
run: ./task shadowbox:test
|
||||
|
||||
- name: Shadowbox Docker Build (arm64)
|
||||
run: TARGET_ARCH=aarch64 ./task shadowbox:docker:build
|
||||
|
||||
manual-install-script:
|
||||
name: Manual Install Script
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -119,7 +161,8 @@ jobs:
|
|||
|
||||
sentry-webhook:
|
||||
name: Sentry Webhook
|
||||
runs-on: ubuntu-latest
|
||||
# TODO(puppeteer/puppeteer#12818): Update when chromium bug is resolved.
|
||||
runs-on: ubuntu-22.04
|
||||
needs: lint
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
|||
6
.github/workflows/pull_request_checks.yml
vendored
6
.github/workflows/pull_request_checks.yml
vendored
|
|
@ -37,10 +37,10 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v2.2.0
|
||||
uses: actions/setup-node@v6.1.0
|
||||
with:
|
||||
node-version: 18
|
||||
cache: npm
|
||||
|
|
@ -49,7 +49,7 @@ jobs:
|
|||
run: npm ci
|
||||
|
||||
- name: Ensure Commitizen Format
|
||||
uses: JulienKode/pull-request-name-linter-action@98794a8b815ec05560813c42e55fd8d32d3fd248
|
||||
uses: JulienKode/pull-request-name-linter-action@v20.1.0
|
||||
|
||||
size_label:
|
||||
name: Change Size Label
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# Outline Server
|
||||
|
||||
 [](https://community.internetfreedomfestival.org/community/channels/outline-community) [](https://www.reddit.com/r/outlinevpn/)
|
||||
 [](https://community.internetfreedomfestival.org/community/channels/outline-community) [](https://www.reddit.com/r/outlinevpn/)
|
||||
|
||||
Outline Server is the component that provides the Shadowsocks service (via [outline-ss-server](https://github.com/Jigsaw-Code/outline-ss-server/)) and a service management API. You can deploy this server directly following simple instructions in this repository, or if you prefer a ready-to-use graphical interface you can use the [Outline Manager](https://github.com/Jigsaw-Code/outline-apps/).
|
||||
Outline Server is the component that provides the Shadowsocks service (via [outline-ss-server](https://github.com/OutlineFoundation/tunnel-server/)) and a service management API. You can deploy this server directly following simple instructions in this repository, or if you prefer a ready-to-use graphical interface you can use the [Outline Manager](https://github.com/OutlineFoundation/outline-apps/).
|
||||
|
||||
**Components:**
|
||||
|
||||
- **Outline Server** ([`src/shadowbox`](src/shadowbox)): The core proxy server that runs and manages [outline-ss-server](https://github.com/Jigsaw-Code/outline-ss-server/), a Shadowsocks backend. It provides a REST API for access key management.
|
||||
- **Outline Server** ([`src/shadowbox`](src/shadowbox)): The core proxy server that runs and manages [outline-ss-server](https://github.com/OutlineFoundation/tunnel-server/), a Shadowsocks backend. It provides a REST API for access key management.
|
||||
|
||||
- **Metrics Server** ([`src/metrics_server`](src/metrics_server)): A REST service for optional, anonymous metrics sharing.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,6 @@ The censors used to block Shadowsocks, but Shadowsocks has evolved, and in 2021,
|
|||
|
||||
In 2022 China started blocking seemingly random traffic ([report](https://www.opentech.fund/news/exposing-the-great-firewalls-dynamic-blocking-of-fully-encrypted-traffic/)). While there is no evidence they could detect Shadowsocks, the protocol ended up blocked.
|
||||
|
||||
As a reponse, we [added a feature to the Outline Client](https://github.com/Jigsaw-Code/outline-apps/pull/1454) that allows service managers to specify a **[connection prefix disguise](https://www.reddit.com/r/outlinevpn/wiki/index/prefixing/)** to be used in the Shadowsocks initialization, which can be used to bypass the blocking in China by making it look like a protocol that is allowed.
|
||||
As a reponse, we [added a feature to the Outline Client](https://github.com/OutlineFoundation/outline-apps/pull/1454) that allows service managers to specify a **[connection prefix disguise](https://www.reddit.com/r/outlinevpn/wiki/index/prefixing/)** to be used in the Shadowsocks initialization, which can be used to bypass the blocking in China by making it look like a protocol that is allowed.
|
||||
|
||||
Shadowsocks remains our protocol of choice because it's simple, well understood and very performant. Furthermore, it has an enthusiastic community of very smart people behind it.
|
||||
|
|
|
|||
8
go.mod
8
go.mod
|
|
@ -1,6 +1,6 @@
|
|||
module localhost
|
||||
|
||||
go 1.21
|
||||
go 1.26.3
|
||||
|
||||
require (
|
||||
github.com/Jigsaw-Code/outline-ss-server v1.7.3
|
||||
|
|
@ -37,10 +37,10 @@ require (
|
|||
github.com/shadowsocks/go-shadowsocks2 v0.1.5 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/zeebo/xxh3 v1.0.2 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/term v0.19.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
mvdan.cc/sh/v3 v3.8.0 // indirect
|
||||
|
|
|
|||
12
go.sum
12
go.sum
|
|
@ -91,8 +91,8 @@ github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
|||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -102,11 +102,11 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
|
|
|
|||
239
package-lock.json
generated
239
package-lock.json
generated
|
|
@ -1891,6 +1891,19 @@
|
|||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
|
||||
|
|
@ -3819,6 +3832,19 @@
|
|||
"node": "^10.12.0 || >=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.1.2",
|
||||
"dev": true,
|
||||
|
|
@ -4774,6 +4800,16 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number-object": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
|
||||
|
|
@ -5210,17 +5246,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/chokidar": {
|
||||
"version": "3.5.3",
|
||||
"dev": true,
|
||||
|
|
@ -5247,17 +5272,6 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"dev": true,
|
||||
|
|
@ -5292,14 +5306,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"dev": true,
|
||||
|
|
@ -5325,17 +5331,6 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/levn": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||
|
|
@ -5614,51 +5609,6 @@
|
|||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch/node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch/node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch/node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch/node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "2.6.0",
|
||||
"dev": true,
|
||||
|
|
@ -6094,8 +6044,7 @@
|
|||
},
|
||||
"node_modules/outline-shadowsocksconfig": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "git+ssh://git@github.com/Jigsaw-Code/outline-shadowsocksconfig.git#add590ed57277653d02dd2031ae301500ae881e1",
|
||||
"license": "Apache-2.0",
|
||||
"resolved": "git+ssh://git@github.com/OutlineFoundation/shadowsocksconfig.git#add590ed57277653d02dd2031ae301500ae881e1",
|
||||
"dependencies": {
|
||||
"ipaddr.js": "^2.0.0",
|
||||
"js-base64": "^3.5.2",
|
||||
|
|
@ -8131,6 +8080,19 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"license": "MIT",
|
||||
|
|
@ -9056,7 +9018,7 @@
|
|||
"dependencies": {
|
||||
"ip-regex": "^4.1.0",
|
||||
"js-yaml": "^3.12.0",
|
||||
"outline-shadowsocksconfig": "github:Jigsaw-Code/outline-shadowsocksconfig#v0.2.0",
|
||||
"outline-shadowsocksconfig": "github:OutlineFoundation/shadowsocksconfig#v0.2.0",
|
||||
"prom-client": "^11.1.3",
|
||||
"randomstring": "^1.1.5",
|
||||
"restify": "^11.1.0",
|
||||
|
|
@ -10423,6 +10385,15 @@
|
|||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.1.1"
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
|
||||
|
|
@ -11731,6 +11702,15 @@
|
|||
"flat-cache": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"finalhandler": {
|
||||
"version": "1.1.2",
|
||||
"dev": true,
|
||||
|
|
@ -12352,6 +12332,12 @@
|
|||
"integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
|
||||
"dev": true
|
||||
},
|
||||
"is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"is-number-object": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
|
||||
|
|
@ -12588,13 +12574,6 @@
|
|||
"version": "2.2.0",
|
||||
"dev": true
|
||||
},
|
||||
"braces": {
|
||||
"version": "3.0.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "3.5.3",
|
||||
"dev": true,
|
||||
|
|
@ -12609,13 +12588,6 @@
|
|||
"readdirp": "~3.6.0"
|
||||
}
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"dev": true,
|
||||
|
|
@ -12635,10 +12607,6 @@
|
|||
"binary-extensions": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"is-number": {
|
||||
"version": "7.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "3.6.0",
|
||||
"dev": true,
|
||||
|
|
@ -12652,13 +12620,6 @@
|
|||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -12926,41 +12887,6 @@
|
|||
"requires": {
|
||||
"braces": "^3.0.2",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"mime": {
|
||||
|
|
@ -13264,7 +13190,7 @@
|
|||
"@types/tmp": "^0.2.1",
|
||||
"ip-regex": "^4.1.0",
|
||||
"js-yaml": "^3.12.0",
|
||||
"outline-shadowsocksconfig": "github:Jigsaw-Code/outline-shadowsocksconfig#v0.2.0",
|
||||
"outline-shadowsocksconfig": "github:OutlineFoundation/shadowsocksconfig#v0.2.0",
|
||||
"prom-client": "^11.1.3",
|
||||
"randomstring": "^1.1.5",
|
||||
"restify": "^11.1.0",
|
||||
|
|
@ -13284,8 +13210,8 @@
|
|||
}
|
||||
},
|
||||
"outline-shadowsocksconfig": {
|
||||
"version": "git+ssh://git@github.com/Jigsaw-Code/outline-shadowsocksconfig.git#add590ed57277653d02dd2031ae301500ae881e1",
|
||||
"from": "outline-shadowsocksconfig@github:Jigsaw-Code/outline-shadowsocksconfig#v0.2.0",
|
||||
"version": "git+ssh://git@github.com/OutlineFoundation/shadowsocksconfig.git#add590ed57277653d02dd2031ae301500ae881e1",
|
||||
"from": "outline-shadowsocksconfig@github:OutlineFoundation/shadowsocksconfig#v0.2.0",
|
||||
"requires": {
|
||||
"ipaddr.js": "^2.0.0",
|
||||
"js-base64": "^3.5.2",
|
||||
|
|
@ -14741,6 +14667,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"toidentifier": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ const VALID_USER_REPORT2: HourlyUserConnectionMetricsReport = {
|
|||
|
||||
/*
|
||||
* Legacy access key user reports to ensure backwards compatibility with servers not
|
||||
* synced past https://github.com/Jigsaw-Code/outline-server/pull/1529).
|
||||
* synced past https://github.com/OutlineFoundation/outline-server/pull/1529).
|
||||
*/
|
||||
const LEGACY_PER_KEY_USER_REPORT: HourlyUserConnectionMetricsReport = {
|
||||
userId: 'foo',
|
||||
|
|
@ -43,7 +43,7 @@ const LEGACY_PER_KEY_USER_REPORT: HourlyUserConnectionMetricsReport = {
|
|||
|
||||
/*
|
||||
* Legacy multiple countries user reports to ensure backwards compatibility with servers
|
||||
* not synced past https://github.com/Jigsaw-Code/outline-server/pull/1242.
|
||||
* not synced past https://github.com/OutlineFoundation/outline-server/pull/1242.
|
||||
*/
|
||||
const LEGACY_PER_LOCATION_USER_REPORT: HourlyUserConnectionMetricsReport = {
|
||||
userId: 'foobar',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Outline Manager
|
||||
|
||||
> THIS PROJECT HAS MOVED TO A [NEW LOCATION](https://github.com/Jigsaw-Code/outline-apps/tree/master/): Outline Manager is now part of the [Outline Apps repository](https://github.com/Jigsaw-Code/outline-apps).
|
||||
> THIS PROJECT HAS MOVED TO A [NEW LOCATION](https://github.com/OutlineFoundation/outline-apps/tree/master/server_manager): Outline Manager is now part of the [Outline Apps repository](https://github.com/OutlineFoundation/outline-apps).
|
||||
|
||||
We are keeping this folder to support legacy versions of the app that point to the old [server install script](https://github.com/Jigsaw-Code/outline-server/blob/master/src/server_manager/install_scripts/install_server.sh).
|
||||
We are keeping this folder to support legacy versions of the app that point to the old [server install script](https://github.com/OutlineFoundation/outline-server/blob/master/src/server_manager/install_scripts/install_server.sh).
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ function fetch() {
|
|||
function install_docker() {
|
||||
(
|
||||
# Change umask so that /usr/share/keyrings/docker-archive-keyring.gpg has the right permissions.
|
||||
# See https://github.com/Jigsaw-Code/outline-server/issues/951.
|
||||
# See https://github.com/OutlineFoundation/outline-server/issues/951.
|
||||
# We do this in a subprocess so the umask for the calling process is unaffected.
|
||||
umask 0022
|
||||
fetch https://get.docker.com/ | sh
|
||||
|
|
@ -376,7 +376,7 @@ function start_watchtower() {
|
|||
-v /var/run/docker.sock:/var/run/docker.sock)
|
||||
# By itself, local messes up the return code.
|
||||
local STDERR_OUTPUT
|
||||
STDERR_OUTPUT="$(docker run -d "${docker_watchtower_flags[@]}" containrrr/watchtower --cleanup --label-enable --scope=outline --tlsverify --interval "${WATCHTOWER_REFRESH_SECONDS}" 2>&1 >/dev/null)" && return
|
||||
STDERR_OUTPUT="$(docker run -d "${docker_watchtower_flags[@]}" nickfedor/watchtower --cleanup --label-enable --scope=outline --tlsverify --interval "${WATCHTOWER_REFRESH_SECONDS}" 2>&1 >/dev/null)" && return
|
||||
readonly STDERR_OUTPUT
|
||||
log_error "FAILED"
|
||||
if docker_container_exists watchtower; then
|
||||
|
|
@ -440,7 +440,7 @@ Make sure to open the following ports on your firewall, router or cloud provider
|
|||
function set_hostname() {
|
||||
# These are URLs that return the client's apparent IP address.
|
||||
# We have more than one to try in case one starts failing
|
||||
# (e.g. https://github.com/Jigsaw-Code/outline-server/issues/776).
|
||||
# (e.g. https://github.com/OutlineFoundation/outline-server/issues/776).
|
||||
local -ar urls=(
|
||||
'https://icanhazip.com/'
|
||||
'https://ipinfo.io/ip'
|
||||
|
|
@ -456,8 +456,8 @@ function set_hostname() {
|
|||
install_shadowbox() {
|
||||
local MACHINE_TYPE
|
||||
MACHINE_TYPE="$(uname -m)"
|
||||
if [[ "${MACHINE_TYPE}" != "x86_64" ]]; then
|
||||
log_error "Unsupported machine type: ${MACHINE_TYPE}. Please run this script on a x86_64 machine"
|
||||
if [[ "${MACHINE_TYPE}" != "x86_64" && "${MACHINE_TYPE}" != "aarch64" && "${MACHINE_TYPE}" != "arm64" ]]; then
|
||||
log_error "Unsupported machine type: ${MACHINE_TYPE}. Supported architectures: x86_64, aarch64/arm64."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
# 1.7.2
|
||||
|
||||
- Fixes
|
||||
- Fix reporting of country metrics and improve logging output (https://github.com/Jigsaw-Code/outline-server/pull/1242)
|
||||
- Fix reporting of country metrics and improve logging output (https://github.com/OutlineFoundation/outline-server/pull/1242)
|
||||
|
||||
# 1.7.1
|
||||
|
||||
- Fixes
|
||||
- Corner case of isPortUsed that could result in infinite restart loop (https://github.com/Jigsaw-Code/outline-server/pull/1238)
|
||||
- Prevent excessive logging (https://github.com/Jigsaw-Code/outline-server/pull/1232)
|
||||
- Corner case of isPortUsed that could result in infinite restart loop (https://github.com/OutlineFoundation/outline-server/pull/1238)
|
||||
- Prevent excessive logging (https://github.com/OutlineFoundation/outline-server/pull/1232)
|
||||
|
||||
# 1.7.0
|
||||
|
||||
- Features
|
||||
- Add encryption cipher selection to create access key API (https://github.com/Jigsaw-Code/outline-server/pull/1002)
|
||||
- Make access key secrets longer (https://github.com/Jigsaw-Code/outline-server/pull/1098)
|
||||
- Add encryption cipher selection to create access key API (https://github.com/OutlineFoundation/outline-server/pull/1002)
|
||||
- Make access key secrets longer (https://github.com/OutlineFoundation/outline-server/pull/1098)
|
||||
- Fixes
|
||||
- Race condition on concurrent API calls (https://github.com/Jigsaw-Code/outline-server/pull/995)
|
||||
- Upgrades (https://github.com/Jigsaw-Code/outline-server/pull/1211)
|
||||
- Race condition on concurrent API calls (https://github.com/OutlineFoundation/outline-server/pull/995)
|
||||
- Upgrades (https://github.com/OutlineFoundation/outline-server/pull/1211)
|
||||
- Base image to `node:16.18.0-alpine3.16`
|
||||
- outline-ss-server from 1.3.5 to [1.4.0](https://github.com/Jigsaw-Code/outline-ss-server/releases/tag/v1.4.0)
|
||||
- outline-ss-server from 1.3.5 to [1.4.0](https://github.com/OutlineFoundation/outline-ss-server/releases/tag/v1.4.0)
|
||||
- Prometheus from 2.33.5 to [2.37.1](https://github.com/prometheus/prometheus/releases/tag/v2.37.1)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Outline Server (Shadowbox)
|
||||
|
||||
The Outline Server, internal name "Shadowbox," is designed to streamline the setup and sharing of Shadowsocks servers. It includes a user management API and creates Shadowsocks instances when needed. It's managed by the [Outline Manager](https://github.com/Jigsaw-Code/outline-apps/) and used as proxy by the [Outline Client](https://github.com/Jigsaw-Code/outline-apps/) apps. Shadowbox is also compatible with standard Shadowsocks clients.
|
||||
The Outline Server, internal name "Shadowbox," is designed to streamline the setup and sharing of Shadowsocks servers. It includes a user management API and creates Shadowsocks instances when needed. It's managed by the [Outline Manager](https://github.com/OutlineFoundation/outline-apps/) and used as proxy by the [Outline Client](https://github.com/OutlineFoundation/outline-apps/) apps. Shadowbox is also compatible with standard Shadowsocks clients.
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ The Outline Server, internal name "Shadowbox," is designed to streamline the set
|
|||
1. **Run the Installation Script**
|
||||
|
||||
```sh
|
||||
sudo bash -c "$(wget -qO- https://raw.githubusercontent.com/Jigsaw-Code/outline-apps/master/server_manager/install_scripts/install_server.sh)"
|
||||
sudo bash -c "$(wget -qO- https://raw.githubusercontent.com/OutlineFoundation/outline-apps/master/server_manager/install_scripts/install_server.sh)"
|
||||
```
|
||||
|
||||
1. **Customize (Optional)**
|
||||
|
|
@ -17,7 +17,7 @@ The Outline Server, internal name "Shadowbox," is designed to streamline the set
|
|||
Add flags for hostname, port, etc. Example:
|
||||
|
||||
```sh
|
||||
sudo bash -c "$(wget -qO- https://raw.githubusercontent.com/Jigsaw-Code/outline-apps/master/server_manager/install_scripts/install_server.sh)" install_server.sh \
|
||||
sudo bash -c "$(wget -qO- https://raw.githubusercontent.com/OutlineFoundation/outline-apps/master/server_manager/install_scripts/install_server.sh)" install_server.sh \
|
||||
--hostname=myserver.com \
|
||||
--keys-port=443
|
||||
```
|
||||
|
|
@ -113,7 +113,7 @@ The Outline Server provides a REST API for access key management. If you know th
|
|||
|
||||
1. **Further Options:**
|
||||
|
||||
Consult the [OpenAPI spec](./server/api.yml) and [documentation](https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/shadowbox/server/api.yml) for more options.
|
||||
Consult the [OpenAPI spec](./server/api.yml) and [documentation](https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/OutlineFoundation/outline-server/master/src/shadowbox/server/api.yml) for more options.
|
||||
|
||||
## Testing
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ tasks:
|
|||
vars:
|
||||
TARGET_OS: '{{.TARGET_OS | default "linux"}}'
|
||||
TARGET_ARCH: '{{.TARGET_ARCH | default "x86_64"}}'
|
||||
GOARCH: '{{get (dict "x86_64" "amd64") .TARGET_ARCH | default .TARGET_ARCH}}'
|
||||
GOARCH: '{{get (dict "x86_64" "amd64" "aarch64" "arm64") .TARGET_ARCH | default .TARGET_ARCH}}'
|
||||
TARGET_DIR: '{{.TARGET_DIR | default (joinPath .OUTPUT_BASE .TARGET_OS .TARGET_ARCH)}}'
|
||||
NODE_DIR: '{{joinPath .TARGET_DIR "app"}}'
|
||||
BIN_DIR: '{{joinPath .TARGET_DIR "bin"}}'
|
||||
|
|
@ -76,13 +76,21 @@ tasks:
|
|||
IMAGE_NAME: '{{.IMAGE_NAME | default "localhost/outline/shadowbox"}}'
|
||||
TARGET_ARCH: '{{.TARGET_ARCH | default "x86_64"}}'
|
||||
IMAGE_ROOT: '{{joinPath .OUTPUT_BASE "image_root" .TARGET_ARCH}}'
|
||||
# Newer node images have no valid content trust data.
|
||||
# Pin the image node:16.18.0-alpine3.16 by hash.
|
||||
# See image at https://hub.docker.com/_/node/tags?page=1&name=18.18.0-alpine3.18
|
||||
# Pin the image node:18.20.8-alpine3.21 by hash.
|
||||
# See image at https://hub.docker.com/_/node/tags?page=1&name=18.20.8-alpine3.21
|
||||
# Note: "aarch64" is an alias for "arm64" — Linux ARM64 hosts report "aarch64" via uname -m.
|
||||
NODE_IMAGE: '{{get
|
||||
(dict
|
||||
"x86_64" "node@sha256:a0b787b0d53feacfa6d606fb555e0dbfebab30573277f1fe25148b05b66fa097"
|
||||
"arm64" "node@sha256:b4b7a1dd149c65ee6025956ac065a843b4409a62068bd2b0cbafbb30ca2fab3b"
|
||||
"x86_64" "node@sha256:929b04d7c782f04f615cf785488fed452b6569f87c73ff666ad553a7554f0006"
|
||||
"arm64" "node@sha256:c2281c62c4aadf92ea71a6c05e6c8e640634b6a99dc52a6e54575f9cb298a037"
|
||||
"aarch64" "node@sha256:c2281c62c4aadf92ea71a6c05e6c8e640634b6a99dc52a6e54575f9cb298a037"
|
||||
) .TARGET_ARCH
|
||||
}}'
|
||||
DOCKER_PLATFORM: '{{get
|
||||
(dict
|
||||
"x86_64" "linux/amd64"
|
||||
"arm64" "linux/arm64"
|
||||
"aarch64" "linux/arm64"
|
||||
) .TARGET_ARCH
|
||||
}}'
|
||||
env:
|
||||
|
|
@ -103,6 +111,7 @@ tasks:
|
|||
# Build image with given root
|
||||
- |
|
||||
"${DOCKER:-docker}" build --force-rm \
|
||||
--platform '{{.DOCKER_PLATFORM}}' \
|
||||
--build-arg NODE_IMAGE='{{.NODE_IMAGE}}' \
|
||||
--build-arg VERSION='{{.VERSION}}' \
|
||||
-f '{{joinPath .TASKFILE_DIR "docker" "Dockerfile"}}' \
|
||||
|
|
|
|||
|
|
@ -18,16 +18,17 @@ FROM ${NODE_IMAGE}
|
|||
ARG VERSION
|
||||
|
||||
# Save metadata on the software versions we are using.
|
||||
LABEL shadowbox.node_version=16.18.0
|
||||
LABEL shadowbox.node_version=18.20.8
|
||||
|
||||
LABEL shadowbox.github.release=${VERSION}
|
||||
|
||||
# The user management service doesn't quit with SIGTERM.
|
||||
STOPSIGNAL SIGKILL
|
||||
|
||||
# Upgrade installed Alpine packages to pick up security fixes (musl-utils, busybox, ssl_client, etc.).
|
||||
# We use curl to detect the server's public IP. We need to use the --date option in `date` to
|
||||
# safely grab the ip-to-country database.
|
||||
RUN apk add --no-cache --upgrade coreutils curl
|
||||
RUN apk upgrade --no-cache && apk add --no-cache --upgrade coreutils curl
|
||||
|
||||
COPY . /
|
||||
|
||||
|
|
|
|||
|
|
@ -102,12 +102,17 @@ export interface PrometheusClient {
|
|||
}
|
||||
|
||||
export class ApiPrometheusClient implements PrometheusClient {
|
||||
constructor(private address: string) {}
|
||||
private readonly agent: http.Agent;
|
||||
|
||||
constructor(private address: string) {
|
||||
this.agent = new http.Agent({ keepAlive: true });
|
||||
}
|
||||
|
||||
private request(url: string): Promise<QueryResultData> {
|
||||
return new Promise<QueryResultData>((fulfill, reject) => {
|
||||
const options = {agent: this.agent};
|
||||
http
|
||||
.get(url, (response) => {
|
||||
.get(url, options, (response) => {
|
||||
if (response.statusCode < 200 || response.statusCode > 299) {
|
||||
reject(new Error(`Got error ${response.statusCode}`));
|
||||
response.resume();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
# Alpine 3.19 curl is using the c-ares resolver instead of the system resolver,
|
||||
# which caused DNS issues. Upgrade once the Alpine image includes the fix. See
|
||||
# https://github.com/Jigsaw-Code/outline-server/pull/1566.
|
||||
# https://github.com/OutlineFoundation/outline-server/pull/1566.
|
||||
FROM docker.io/golang:1-alpine3.18
|
||||
|
||||
# curl for fetching pages using the local proxy
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
"dependencies": {
|
||||
"ip-regex": "^4.1.0",
|
||||
"js-yaml": "^3.12.0",
|
||||
"outline-shadowsocksconfig": "github:Jigsaw-Code/outline-shadowsocksconfig#v0.2.0",
|
||||
"outline-shadowsocksconfig": "github:OutlineFoundation/shadowsocksconfig#v0.2.0",
|
||||
"prom-client": "^11.1.3",
|
||||
"randomstring": "^1.1.5",
|
||||
"restify": "^11.1.0",
|
||||
|
|
|
|||
|
|
@ -146,46 +146,94 @@ paths:
|
|||
type: object
|
||||
properties:
|
||||
server:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
location:
|
||||
type: string
|
||||
asn:
|
||||
type: number
|
||||
asOrg:
|
||||
type: string
|
||||
tunnelTime:
|
||||
type: object
|
||||
properties:
|
||||
tunnelTime:
|
||||
type: object
|
||||
properties:
|
||||
seconds:
|
||||
type: number
|
||||
dataTransferred:
|
||||
type: object
|
||||
properties:
|
||||
bytes:
|
||||
type: number
|
||||
bandwidth:
|
||||
type: object
|
||||
properties:
|
||||
current:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
bytes:
|
||||
type: number
|
||||
timestamp:
|
||||
type: integer
|
||||
peak:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
bytes:
|
||||
type: number
|
||||
timestamp:
|
||||
type: integer
|
||||
locations:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
seconds:
|
||||
type: number
|
||||
dataTransferred:
|
||||
type: object
|
||||
properties:
|
||||
bytes:
|
||||
location:
|
||||
type: string
|
||||
asn:
|
||||
type: number
|
||||
asOrg:
|
||||
type: string
|
||||
tunnelTime:
|
||||
type: object
|
||||
properties:
|
||||
seconds:
|
||||
type: number
|
||||
dataTransferred:
|
||||
type: object
|
||||
properties:
|
||||
bytes:
|
||||
type: number
|
||||
accessKeys:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
accessKeyId:
|
||||
type: string
|
||||
type: integer
|
||||
tunnelTime:
|
||||
type: object
|
||||
properties:
|
||||
seconds:
|
||||
seconds:
|
||||
type: number
|
||||
dataTransferred:
|
||||
type: object
|
||||
properties:
|
||||
bytes:
|
||||
bytes:
|
||||
type: number
|
||||
connection:
|
||||
type: object
|
||||
properties:
|
||||
lastTrafficSeen:
|
||||
type: number
|
||||
peakDeviceCount:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: integer
|
||||
timestamp:
|
||||
type: integer
|
||||
examples:
|
||||
'0':
|
||||
value: '{"server":[{"location":"US","asn":null,"asOrg":null,"tunnelTime":{"seconds":100},"dataTransferred":{"bytes":100}}],"accessKeys":[{"accessKeyId":"0","tunnelTime":{"seconds":100},"dataTransferred":{"bytes":100}}]}'
|
||||
value: '{"server":{"tunnelTime":{"seconds":100},"dataTransferred":{"bytes":100},"bandwidth": {"current": {"data": {"bytes": 10},"timestamp": 1739284734},"peak": {"data": {"bytes": 80},"timestamp": 1738959398}},"locations": [{"location": "US","asn": null,"asOrg": null,"dataTransferred": {"bytes": 100},"tunnelTime": {"seconds": 100}}]},"accessKeys":[{"accessKeyId":0,"tunnelTime":{"seconds":100},"dataTransferred":{"bytes":100},"connection": {"lastTrafficSeen": 1739284734,"peakDeviceCount": {"data": 4,"timestamp": 1738959398}}}]}'
|
||||
/name:
|
||||
put:
|
||||
description: Renames the server
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ const MMDB_LOCATION_ASN = '/var/lib/libmaxminddb/ip-asn.mmdb';
|
|||
async function exportPrometheusMetrics(registry: prometheus.Registry, port): Promise<http.Server> {
|
||||
return new Promise<http.Server>((resolve, _) => {
|
||||
const server = http.createServer((_, res) => {
|
||||
res.setHeader('Content-Type', registry.contentType);
|
||||
res.write(registry.metrics());
|
||||
res.end();
|
||||
});
|
||||
|
|
@ -122,7 +123,7 @@ async function main() {
|
|||
|
||||
const prometheusPort = await portProvider.reserveFirstFreePort(9090);
|
||||
// Use 127.0.0.1 instead of localhost for Prometheus because it's resolving incorrectly for some users.
|
||||
// See https://github.com/Jigsaw-Code/outline-server/issues/341
|
||||
// See https://github.com/OutlineFoundation/outline-server/issues/341
|
||||
const prometheusLocation = `127.0.0.1:${prometheusPort}`;
|
||||
|
||||
const nodeMetricsPort = await portProvider.reserveFirstFreePort(prometheusPort + 1);
|
||||
|
|
|
|||
|
|
@ -202,7 +202,6 @@ describe('PrometheusManagerMetrics', () => {
|
|||
"seconds": 1000
|
||||
},
|
||||
"connection": {
|
||||
"lastConnected": 1738959398,
|
||||
"lastTrafficSeen": 1739284734,
|
||||
"peakDeviceCount": {
|
||||
"data": 4,
|
||||
|
|
@ -389,7 +388,6 @@ describe('PrometheusManagerMetrics', () => {
|
|||
"seconds": 1000
|
||||
},
|
||||
"connection": {
|
||||
"lastConnected": null,
|
||||
"lastTrafficSeen": null,
|
||||
"peakDeviceCount": {
|
||||
"data": 0,
|
||||
|
|
@ -406,7 +404,6 @@ describe('PrometheusManagerMetrics', () => {
|
|||
"seconds": 0
|
||||
},
|
||||
"connection": {
|
||||
"lastConnected": 1738959398,
|
||||
"lastTrafficSeen": 1738959398,
|
||||
"peakDeviceCount": {
|
||||
"data": 4,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
PrometheusClient,
|
||||
PrometheusMetric,
|
||||
PrometheusValue,
|
||||
QueryResultData,
|
||||
} from '../infrastructure/prometheus_scraper';
|
||||
import {DataUsageByUser, DataUsageTimeframe} from '../model/metrics';
|
||||
|
||||
|
|
@ -35,7 +36,6 @@ interface TimedData<T> {
|
|||
}
|
||||
|
||||
interface ConnectionStats {
|
||||
lastConnected: number | null;
|
||||
lastTrafficSeen: number | null;
|
||||
peakDeviceCount: TimedData<number>;
|
||||
}
|
||||
|
|
@ -112,8 +112,9 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
|
|||
Math.ceil(now / PROMETHEUS_RANGE_QUERY_STEP_SECONDS) * PROMETHEUS_RANGE_QUERY_STEP_SECONDS;
|
||||
const start = end - timeframe.seconds;
|
||||
|
||||
this.prunePrometheusCache();
|
||||
|
||||
const [
|
||||
bandwidth,
|
||||
bandwidthRange,
|
||||
dataTransferredByLocation,
|
||||
tunnelTimeByLocation,
|
||||
|
|
@ -122,34 +123,31 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
|
|||
dataTransferredByAccessKeyRange,
|
||||
tunnelTimeByAccessKeyRange,
|
||||
] = await Promise.all([
|
||||
this.prometheusClient.query(
|
||||
`sum(rate(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`
|
||||
),
|
||||
this.prometheusClient.queryRange(
|
||||
this.cachedPrometheusClient.queryRange(
|
||||
`sum(rate(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`,
|
||||
start,
|
||||
end,
|
||||
`${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s`
|
||||
),
|
||||
this.prometheusClient.query(
|
||||
this.cachedPrometheusClient.query(
|
||||
`sum(increase(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[${timeframe.seconds}s])) by (location, asn, asorg)`
|
||||
),
|
||||
this.prometheusClient.query(
|
||||
this.cachedPrometheusClient.query(
|
||||
`sum(increase(shadowsocks_tunnel_time_seconds_per_location[${timeframe.seconds}s])) by (location, asn, asorg)`
|
||||
),
|
||||
this.prometheusClient.query(
|
||||
this.cachedPrometheusClient.query(
|
||||
`sum(increase(shadowsocks_data_bytes{dir=~"c<p|p>t"}[${timeframe.seconds}s])) by (access_key)`
|
||||
),
|
||||
this.prometheusClient.query(
|
||||
this.cachedPrometheusClient.query(
|
||||
`sum(increase(shadowsocks_tunnel_time_seconds[${timeframe.seconds}s])) by (access_key)`
|
||||
),
|
||||
this.prometheusClient.queryRange(
|
||||
this.cachedPrometheusClient.queryRange(
|
||||
`sum(increase(shadowsocks_data_bytes{dir=~"c<p|p>t"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`,
|
||||
start,
|
||||
end,
|
||||
`${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s`
|
||||
),
|
||||
this.prometheusClient.queryRange(
|
||||
this.cachedPrometheusClient.queryRange(
|
||||
`sum(increase(shadowsocks_tunnel_time_seconds[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`,
|
||||
start,
|
||||
end,
|
||||
|
|
@ -166,13 +164,15 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
|
|||
},
|
||||
locations: [],
|
||||
};
|
||||
for (const result of bandwidth.result) {
|
||||
if (result.value) {
|
||||
serverMetrics.bandwidth.current.data.bytes = parseFloat(result.value[1]);
|
||||
serverMetrics.bandwidth.current.timestamp = result.value[0];
|
||||
}
|
||||
break; // There should only be one result.
|
||||
|
||||
const bandwidthRangeValues = bandwidthRange.result[0].values ?? [];
|
||||
const currentBandwidth = bandwidthRangeValues[bandwidthRangeValues.length - 1];
|
||||
|
||||
if (currentBandwidth) {
|
||||
serverMetrics.bandwidth.current.data.bytes = parseFloat(currentBandwidth[1]);
|
||||
serverMetrics.bandwidth.current.timestamp = currentBandwidth[0];
|
||||
}
|
||||
|
||||
for (const result of bandwidthRange.result) {
|
||||
const peakDataTransferred = findPeak(result.values ?? []);
|
||||
if (peakDataTransferred !== null) {
|
||||
|
|
@ -211,8 +211,6 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
|
|||
}
|
||||
for (const result of tunnelTimeByAccessKeyRange.result) {
|
||||
const entry = getServerMetricsAccessKeyEntry(accessKeyMap, result.metric);
|
||||
const lastConnected = findLastNonZero(result.values ?? []);
|
||||
entry.connection.lastConnected = lastConnected ? Math.min(now, lastConnected[0]) : null;
|
||||
const peakTunnelTimeSec = findPeak(result.values ?? []);
|
||||
if (peakTunnelTimeSec !== null) {
|
||||
const peakValue = parseFloat(peakTunnelTimeSec[1]);
|
||||
|
|
@ -234,6 +232,41 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
|
|||
accessKeys: Array.from(accessKeyMap.values()),
|
||||
};
|
||||
}
|
||||
|
||||
private prometheusCache = new Map<string, {timestamp: number; result: QueryResultData}>();
|
||||
|
||||
private get cachedPrometheusClient() {
|
||||
return new Proxy(this.prometheusClient, {
|
||||
get: (target, prop) => {
|
||||
if (typeof target[prop] !== 'function') {
|
||||
return target[prop];
|
||||
}
|
||||
|
||||
return async (query, ...args) => {
|
||||
const cacheId = `${String(prop)}: ${query} (args: ${args.join(', ')}))`;
|
||||
|
||||
if (this.prometheusCache.has(cacheId)) {
|
||||
return this.prometheusCache.get(cacheId).result;
|
||||
}
|
||||
|
||||
const result = await (target[prop] as Function)(query, ...args);
|
||||
|
||||
this.prometheusCache.set(cacheId, {timestamp: Date.now(), result});
|
||||
|
||||
return result;
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private prunePrometheusCache() {
|
||||
const now = Date.now();
|
||||
for (const [key, value] of this.prometheusCache) {
|
||||
if (now - value.timestamp > PROMETHEUS_RANGE_QUERY_STEP_SECONDS * 1000) {
|
||||
this.prometheusCache.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getServerMetricsLocationEntry(
|
||||
|
|
@ -268,7 +301,6 @@ function getServerMetricsAccessKeyEntry(
|
|||
dataTransferred: {bytes: 0},
|
||||
tunnelTime: {seconds: 0},
|
||||
connection: {
|
||||
lastConnected: null,
|
||||
lastTrafficSeen: null,
|
||||
peakDeviceCount: {
|
||||
data: 0,
|
||||
|
|
|
|||
|
|
@ -21,26 +21,12 @@ import * as file from '../infrastructure/file';
|
|||
import * as logging from '../infrastructure/logging';
|
||||
import {ShadowsocksAccessKey, ShadowsocksServer} from '../model/shadowsocks_server';
|
||||
|
||||
/** Represents an outline-ss-server configuration with multiple services. */
|
||||
export interface OutlineSSServerConfig {
|
||||
services: {
|
||||
listeners: {
|
||||
type: string;
|
||||
address: string;
|
||||
}[];
|
||||
keys: {
|
||||
id: string;
|
||||
cipher: string;
|
||||
secret: string;
|
||||
}[];
|
||||
}[];
|
||||
}
|
||||
|
||||
// Runs outline-ss-server.
|
||||
export class OutlineShadowsocksServer implements ShadowsocksServer {
|
||||
private ssProcess: child_process.ChildProcess;
|
||||
private ipCountryFilename?: string;
|
||||
private ipAsnFilename?: string;
|
||||
private isAsnMetricsEnabled = false;
|
||||
private isReplayProtectionEnabled = false;
|
||||
|
||||
/**
|
||||
|
|
@ -95,39 +81,22 @@ export class OutlineShadowsocksServer implements ShadowsocksServer {
|
|||
|
||||
private writeConfigFile(keys: ShadowsocksAccessKey[]): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const validKeys: ShadowsocksAccessKey[] = keys.filter((key) => {
|
||||
const keysJson = {keys: [] as ShadowsocksAccessKey[]};
|
||||
for (const key of keys) {
|
||||
if (!isAeadCipher(key.cipher)) {
|
||||
logging.error(
|
||||
`Cipher ${key.cipher} for access key ${key.id} is not supported: use an AEAD cipher instead.`
|
||||
);
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const config: OutlineSSServerConfig = {services: []};
|
||||
const keysByPort: Record<number, ShadowsocksAccessKey[]> = {};
|
||||
for (const key of validKeys) {
|
||||
(keysByPort[key.port] ??= []).push(key);
|
||||
}
|
||||
for (const port in keysByPort) {
|
||||
const service = {
|
||||
listeners: [
|
||||
{type: 'tcp', address: `[::]:${port}`},
|
||||
{type: 'udp', address: `[::]:${port}`},
|
||||
],
|
||||
keys: keysByPort[port].map((key) => ({
|
||||
id: key.id,
|
||||
cipher: key.cipher,
|
||||
secret: key.secret,
|
||||
})),
|
||||
};
|
||||
config.services.push(service);
|
||||
keysJson.keys.push(key);
|
||||
}
|
||||
|
||||
mkdirp.sync(path.dirname(this.configFilename));
|
||||
|
||||
try {
|
||||
file.atomicWriteFileSync(this.configFilename, jsyaml.safeDump(config, {sortKeys: true}));
|
||||
file.atomicWriteFileSync(this.configFilename, jsyaml.safeDump(keysJson, {sortKeys: true}));
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
|
|
|
|||
12
third_party/Taskfile.yml
vendored
12
third_party/Taskfile.yml
vendored
|
|
@ -11,17 +11,17 @@ tasks:
|
|||
prometheus:download-*-*:
|
||||
desc: Download and extract prometheus binary
|
||||
vars:
|
||||
VERSION: '2.37.1'
|
||||
VERSION: '2.53.4'
|
||||
GOOS: '{{index .MATCH 0}}'
|
||||
GOARCH: '{{index .MATCH 1}}'
|
||||
TEMPFILE: {sh: mktemp}
|
||||
SHA256: '{{printf "%v/%v" .GOOS .GOARCH | get
|
||||
(dict
|
||||
"linux/amd64" "753f66437597cf52ada98c2f459aa8c03745475c249c9f2b40ac7b3919131ba6"
|
||||
"linux/arm64" "b59a66fb5c7ec5acf6bf426793528a5789a1478a0dad8c64edc2843caf31b1b8"
|
||||
"darwin/amd64" "e03a43d98955ac3500f57353ea74b5df829074205f195ea6b3b88f55c4575c79"
|
||||
"darwin/arm64" "eb8a174c82a0fb6c84e81d9a73214318fb4a605115ad61505d7883d02e5a6f52"
|
||||
)
|
||||
"linux/amd64" "b8b497c4610d1b93208252b60c8f20f6b2e78596ae8df43397a2e805aa53d475"
|
||||
"linux/arm64" "ec7236ecea7154d0bfe142921708b1ae7b5e921e100e0ee85ab92b7c444357e0"
|
||||
"darwin/amd64" "10066a1aa21c4ddb8d5e61c31b52e898d8ac42c7e99fd757e2fc4b6c20b8075f"
|
||||
"darwin/arm64" "cb3e638d8e9b4b27a6aa1f45a4faa3741b548aac67d4649aea7a2fad3c09f0a1"
|
||||
)
|
||||
}}'
|
||||
TARGET_DIR: '{{joinPath .OUTPUT_BASE "prometheus" .GOOS .GOARCH}}'
|
||||
TARGET: '{{joinPath .TARGET_DIR "prometheus"}}'
|
||||
|
|
|
|||
16
third_party/prometheus/METADATA
vendored
16
third_party/prometheus/METADATA
vendored
|
|
@ -10,13 +10,21 @@ third_party {
|
|||
}
|
||||
url {
|
||||
type: ARCHIVE
|
||||
value: "https://github.com/prometheus/prometheus/releases/download/2.37.1/prometheus-2.37.1.linux-amd64.tar.gz"
|
||||
value: "https://github.com/prometheus/prometheus/releases/download/v2.53.4/prometheus-2.53.4.linux-amd64.tar.gz"
|
||||
}
|
||||
url {
|
||||
type: ARCHIVE
|
||||
value: "https://github.com/prometheus/prometheus/releases/download/2.37.1/prometheus-2.37.1.darwin-amd64.tar.gz"
|
||||
value: "https://github.com/prometheus/prometheus/releases/download/v2.53.4/prometheus-2.53.4.linux-arm64.tar.gz"
|
||||
}
|
||||
version: "2.37.1"
|
||||
last_upgrade_date { year: 2022 month: 10 day: 24 }
|
||||
url {
|
||||
type: ARCHIVE
|
||||
value: "https://github.com/prometheus/prometheus/releases/download/v2.53.4/prometheus-2.53.4.darwin-amd64.tar.gz"
|
||||
}
|
||||
url {
|
||||
type: ARCHIVE
|
||||
value: "https://github.com/prometheus/prometheus/releases/download/v2.53.4/prometheus-2.53.4.darwin-arm64.tar.gz"
|
||||
}
|
||||
version: "2.53.4"
|
||||
last_upgrade_date { year: 2025 month: 3 day: 20 }
|
||||
license_type: PERMISSIVE
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue