diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 34aad372..3b66b7db 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -* @OutlineFoundation/outline-dev +* @Jigsaw-Code/outline-dev /src/server_manager/model/ @fortuna /src/shadowbox/ @fortuna diff --git a/.github/workflows/build_and_test_debug.yml b/.github/workflows/build_and_test_debug.yml index 441bc05e..39bd1af0 100644 --- a/.github/workflows/build_and_test_debug.yml +++ b/.github/workflows/build_and_test_debug.yml @@ -43,16 +43,8 @@ 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 @@ -89,40 +81,6 @@ 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 @@ -161,8 +119,7 @@ jobs: sentry-webhook: name: Sentry Webhook - # TODO(puppeteer/puppeteer#12818): Update when chromium bug is resolved. - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest needs: lint steps: - name: Checkout diff --git a/.github/workflows/pull_request_checks.yml b/.github/workflows/pull_request_checks.yml index 3b27ae42..f10203bc 100644 --- a/.github/workflows/pull_request_checks.yml +++ b/.github/workflows/pull_request_checks.yml @@ -37,10 +37,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Clone Repository - uses: actions/checkout@v4 + uses: actions/checkout@v2 - name: Install Node - uses: actions/setup-node@v6.1.0 + uses: actions/setup-node@v2.2.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@v20.1.0 + uses: JulienKode/pull-request-name-linter-action@98794a8b815ec05560813c42e55fd8d32d3fd248 size_label: name: Change Size Label diff --git a/README.md b/README.md index dfb518e4..9ab5ac45 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Outline Server -![Build and Test](https://github.com/OutlineFoundation/outline-server/actions/workflows/build_and_test_debug.yml/badge.svg?branch=master) [![Mattermost](https://badgen.net/badge/Mattermost/Outline%20Community/blue)](https://community.internetfreedomfestival.org/community/channels/outline-community) [![Reddit](https://badgen.net/badge/Reddit/r%2Foutlinevpn/orange)](https://www.reddit.com/r/outlinevpn/) +![Build and Test](https://github.com/Jigsaw-Code/outline-server/actions/workflows/build_and_test_debug.yml/badge.svg?branch=master) [![Mattermost](https://badgen.net/badge/Mattermost/Outline%20Community/blue)](https://community.internetfreedomfestival.org/community/channels/outline-community) [![Reddit](https://badgen.net/badge/Reddit/r%2Foutlinevpn/orange)](https://www.reddit.com/r/outlinevpn/) -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/). +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/). **Components:** -- **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. +- **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. - **Metrics Server** ([`src/metrics_server`](src/metrics_server)): A REST service for optional, anonymous metrics sharing. diff --git a/docs/shadowsocks.md b/docs/shadowsocks.md index 35b899f4..e8f426e9 100644 --- a/docs/shadowsocks.md +++ b/docs/shadowsocks.md @@ -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/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. +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. 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. diff --git a/go.mod b/go.mod index f5572f2c..1228b069 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module localhost -go 1.26.3 +go 1.21 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.45.0 // indirect + golang.org/x/crypto v0.18.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/term v0.37.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.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 diff --git a/go.sum b/go.sum index f86ef755..84be92e3 100644 --- a/go.sum +++ b/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.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +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/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.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +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/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -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/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= 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= diff --git a/package-lock.json b/package-lock.json index 30073280..b4d82a70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1891,19 +1891,6 @@ "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", @@ -3832,19 +3819,6 @@ "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, @@ -4800,16 +4774,6 @@ "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", @@ -5246,6 +5210,17 @@ "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, @@ -5272,6 +5247,17 @@ "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, @@ -5306,6 +5292,14 @@ "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, @@ -5331,6 +5325,17 @@ "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", @@ -5609,6 +5614,51 @@ "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, @@ -6044,7 +6094,8 @@ }, "node_modules/outline-shadowsocksconfig": { "version": "0.2.0", - "resolved": "git+ssh://git@github.com/OutlineFoundation/shadowsocksconfig.git#add590ed57277653d02dd2031ae301500ae881e1", + "resolved": "git+ssh://git@github.com/Jigsaw-Code/outline-shadowsocksconfig.git#add590ed57277653d02dd2031ae301500ae881e1", + "license": "Apache-2.0", "dependencies": { "ipaddr.js": "^2.0.0", "js-base64": "^3.5.2", @@ -8080,19 +8131,6 @@ "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", @@ -9018,7 +9056,7 @@ "dependencies": { "ip-regex": "^4.1.0", "js-yaml": "^3.12.0", - "outline-shadowsocksconfig": "github:OutlineFoundation/shadowsocksconfig#v0.2.0", + "outline-shadowsocksconfig": "github:Jigsaw-Code/outline-shadowsocksconfig#v0.2.0", "prom-client": "^11.1.3", "randomstring": "^1.1.5", "restify": "^11.1.0", @@ -10385,15 +10423,6 @@ "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", @@ -11702,15 +11731,6 @@ "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, @@ -12332,12 +12352,6 @@ "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", @@ -12574,6 +12588,13 @@ "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, @@ -12588,6 +12609,13 @@ "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, @@ -12607,6 +12635,10 @@ "binary-extensions": "^2.0.0" } }, + "is-number": { + "version": "7.0.0", + "dev": true + }, "readdirp": { "version": "3.6.0", "dev": true, @@ -12620,6 +12652,13 @@ "requires": { "glob": "^7.1.3" } + }, + "to-regex-range": { + "version": "5.0.1", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } } } }, @@ -12887,6 +12926,41 @@ "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": { @@ -13190,7 +13264,7 @@ "@types/tmp": "^0.2.1", "ip-regex": "^4.1.0", "js-yaml": "^3.12.0", - "outline-shadowsocksconfig": "github:OutlineFoundation/shadowsocksconfig#v0.2.0", + "outline-shadowsocksconfig": "github:Jigsaw-Code/outline-shadowsocksconfig#v0.2.0", "prom-client": "^11.1.3", "randomstring": "^1.1.5", "restify": "^11.1.0", @@ -13210,8 +13284,8 @@ } }, "outline-shadowsocksconfig": { - "version": "git+ssh://git@github.com/OutlineFoundation/shadowsocksconfig.git#add590ed57277653d02dd2031ae301500ae881e1", - "from": "outline-shadowsocksconfig@github:OutlineFoundation/shadowsocksconfig#v0.2.0", + "version": "git+ssh://git@github.com/Jigsaw-Code/outline-shadowsocksconfig.git#add590ed57277653d02dd2031ae301500ae881e1", + "from": "outline-shadowsocksconfig@github:Jigsaw-Code/outline-shadowsocksconfig#v0.2.0", "requires": { "ipaddr.js": "^2.0.0", "js-base64": "^3.5.2", @@ -14667,15 +14741,6 @@ } } }, - "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" }, diff --git a/src/metrics_server/connection_metrics.spec.ts b/src/metrics_server/connection_metrics.spec.ts index 41aa3af4..15fb97ca 100644 --- a/src/metrics_server/connection_metrics.spec.ts +++ b/src/metrics_server/connection_metrics.spec.ts @@ -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/OutlineFoundation/outline-server/pull/1529). + * synced past https://github.com/Jigsaw-Code/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/OutlineFoundation/outline-server/pull/1242. + * not synced past https://github.com/Jigsaw-Code/outline-server/pull/1242. */ const LEGACY_PER_LOCATION_USER_REPORT: HourlyUserConnectionMetricsReport = { userId: 'foobar', diff --git a/src/server_manager/README.md b/src/server_manager/README.md index c0a8a86d..2672de2d 100644 --- a/src/server_manager/README.md +++ b/src/server_manager/README.md @@ -1,5 +1,5 @@ # Outline Manager -> 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). +> 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). -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). +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). diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index 9bf72a09..39ba2b0d 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/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/OutlineFoundation/outline-server/issues/951. + # See https://github.com/Jigsaw-Code/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[@]}" nickfedor/watchtower --cleanup --label-enable --scope=outline --tlsverify --interval "${WATCHTOWER_REFRESH_SECONDS}" 2>&1 >/dev/null)" && return + 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 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/OutlineFoundation/outline-server/issues/776). + # (e.g. https://github.com/Jigsaw-Code/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" && "${MACHINE_TYPE}" != "aarch64" && "${MACHINE_TYPE}" != "arm64" ]]; then - log_error "Unsupported machine type: ${MACHINE_TYPE}. Supported architectures: x86_64, aarch64/arm64." + if [[ "${MACHINE_TYPE}" != "x86_64" ]]; then + log_error "Unsupported machine type: ${MACHINE_TYPE}. Please run this script on a x86_64 machine" exit 1 fi diff --git a/src/shadowbox/CHANGELOG.md b/src/shadowbox/CHANGELOG.md index 8074d6af..af5a38eb 100644 --- a/src/shadowbox/CHANGELOG.md +++ b/src/shadowbox/CHANGELOG.md @@ -1,22 +1,20 @@ # 1.7.2 - - Fixes - - Fix reporting of country metrics and improve logging output (https://github.com/OutlineFoundation/outline-server/pull/1242) + - Fix reporting of country metrics and improve logging output (https://github.com/Jigsaw-Code/outline-server/pull/1242) # 1.7.1 - - Fixes - - 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) + - 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) # 1.7.0 - Features - - 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) + - 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) - Fixes - - Race condition on concurrent API calls (https://github.com/OutlineFoundation/outline-server/pull/995) -- Upgrades (https://github.com/OutlineFoundation/outline-server/pull/1211) + - 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) - Base image to `node:16.18.0-alpine3.16` - - outline-ss-server from 1.3.5 to [1.4.0](https://github.com/OutlineFoundation/outline-ss-server/releases/tag/v1.4.0) + - outline-ss-server from 1.3.5 to [1.4.0](https://github.com/Jigsaw-Code/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) diff --git a/src/shadowbox/README.md b/src/shadowbox/README.md index 8462adf7..c0519aa2 100644 --- a/src/shadowbox/README.md +++ b/src/shadowbox/README.md @@ -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/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. +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. ## 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/OutlineFoundation/outline-apps/master/server_manager/install_scripts/install_server.sh)" + sudo bash -c "$(wget -qO- https://raw.githubusercontent.com/Jigsaw-Code/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/OutlineFoundation/outline-apps/master/server_manager/install_scripts/install_server.sh)" install_server.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 \ --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/OutlineFoundation/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/Jigsaw-Code/outline-server/master/src/shadowbox/server/api.yml) for more options. ## Testing diff --git a/src/shadowbox/Taskfile.yml b/src/shadowbox/Taskfile.yml index adec26d4..fb72a9b8 100644 --- a/src/shadowbox/Taskfile.yml +++ b/src/shadowbox/Taskfile.yml @@ -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" "aarch64" "arm64") .TARGET_ARCH | default .TARGET_ARCH}}' + GOARCH: '{{get (dict "x86_64" "amd64") .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,21 +76,13 @@ 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}}' - # 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. + # 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 NODE_IMAGE: '{{get (dict - "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" + "x86_64" "node@sha256:a0b787b0d53feacfa6d606fb555e0dbfebab30573277f1fe25148b05b66fa097" + "arm64" "node@sha256:b4b7a1dd149c65ee6025956ac065a843b4409a62068bd2b0cbafbb30ca2fab3b" ) .TARGET_ARCH }}' env: @@ -111,7 +103,6 @@ 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"}}' \ diff --git a/src/shadowbox/docker/Dockerfile b/src/shadowbox/docker/Dockerfile index 671c421a..c9d27db7 100644 --- a/src/shadowbox/docker/Dockerfile +++ b/src/shadowbox/docker/Dockerfile @@ -18,17 +18,16 @@ FROM ${NODE_IMAGE} ARG VERSION # Save metadata on the software versions we are using. -LABEL shadowbox.node_version=18.20.8 +LABEL shadowbox.node_version=16.18.0 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 upgrade --no-cache && apk add --no-cache --upgrade coreutils curl +RUN apk add --no-cache --upgrade coreutils curl COPY . / diff --git a/src/shadowbox/infrastructure/prometheus_scraper.ts b/src/shadowbox/infrastructure/prometheus_scraper.ts index af9ebad5..5cfe4064 100644 --- a/src/shadowbox/infrastructure/prometheus_scraper.ts +++ b/src/shadowbox/infrastructure/prometheus_scraper.ts @@ -102,17 +102,12 @@ export interface PrometheusClient { } export class ApiPrometheusClient implements PrometheusClient { - private readonly agent: http.Agent; - - constructor(private address: string) { - this.agent = new http.Agent({ keepAlive: true }); - } + constructor(private address: string) {} private request(url: string): Promise { return new Promise((fulfill, reject) => { - const options = {agent: this.agent}; http - .get(url, options, (response) => { + .get(url, (response) => { if (response.statusCode < 200 || response.statusCode > 299) { reject(new Error(`Got error ${response.statusCode}`)); response.resume(); diff --git a/src/shadowbox/integration_test/client/Dockerfile b/src/shadowbox/integration_test/client/Dockerfile index f2915cdf..31a51bb6 100644 --- a/src/shadowbox/integration_test/client/Dockerfile +++ b/src/shadowbox/integration_test/client/Dockerfile @@ -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/OutlineFoundation/outline-server/pull/1566. +# https://github.com/Jigsaw-Code/outline-server/pull/1566. FROM docker.io/golang:1-alpine3.18 # curl for fetching pages using the local proxy diff --git a/src/shadowbox/package.json b/src/shadowbox/package.json index 62e4ddd4..89a2ef5d 100644 --- a/src/shadowbox/package.json +++ b/src/shadowbox/package.json @@ -11,7 +11,7 @@ "dependencies": { "ip-regex": "^4.1.0", "js-yaml": "^3.12.0", - "outline-shadowsocksconfig": "github:OutlineFoundation/shadowsocksconfig#v0.2.0", + "outline-shadowsocksconfig": "github:Jigsaw-Code/outline-shadowsocksconfig#v0.2.0", "prom-client": "^11.1.3", "randomstring": "^1.1.5", "restify": "^11.1.0", diff --git a/src/shadowbox/server/api.yml b/src/shadowbox/server/api.yml index 2532d51c..7a7cc025 100644 --- a/src/shadowbox/server/api.yml +++ b/src/shadowbox/server/api.yml @@ -146,94 +146,46 @@ paths: type: object properties: server: - 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: - 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: integer + location: + type: string + asn: + type: number + asOrg: + type: string tunnelTime: type: object properties: - seconds: + seconds: type: number dataTransferred: type: object properties: bytes: type: number - connection: + accessKeys: + type: array + items: + type: object + properties: + accessKeyId: + type: string + tunnelTime: type: object properties: - lastTrafficSeen: + seconds: + type: number + dataTransferred: + type: object + properties: + bytes: type: number - peakDeviceCount: - type: object - properties: - data: - type: integer - timestamp: - type: integer examples: '0': - 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}}}]}' + value: '{"server":[{"location":"US","asn":null,"asOrg":null,"tunnelTime":{"seconds":100},"dataTransferred":{"bytes":100}}],"accessKeys":[{"accessKeyId":"0","tunnelTime":{"seconds":100},"dataTransferred":{"bytes":100}}]}' /name: put: description: Renames the server diff --git a/src/shadowbox/server/main.ts b/src/shadowbox/server/main.ts index 5d1be776..8f52c18d 100644 --- a/src/shadowbox/server/main.ts +++ b/src/shadowbox/server/main.ts @@ -48,7 +48,6 @@ const MMDB_LOCATION_ASN = '/var/lib/libmaxminddb/ip-asn.mmdb'; async function exportPrometheusMetrics(registry: prometheus.Registry, port): Promise { return new Promise((resolve, _) => { const server = http.createServer((_, res) => { - res.setHeader('Content-Type', registry.contentType); res.write(registry.metrics()); res.end(); }); @@ -123,7 +122,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/OutlineFoundation/outline-server/issues/341 + // See https://github.com/Jigsaw-Code/outline-server/issues/341 const prometheusLocation = `127.0.0.1:${prometheusPort}`; const nodeMetricsPort = await portProvider.reserveFirstFreePort(prometheusPort + 1); diff --git a/src/shadowbox/server/manager_metrics.spec.ts b/src/shadowbox/server/manager_metrics.spec.ts index ed3107ff..4532224f 100644 --- a/src/shadowbox/server/manager_metrics.spec.ts +++ b/src/shadowbox/server/manager_metrics.spec.ts @@ -202,6 +202,7 @@ describe('PrometheusManagerMetrics', () => { "seconds": 1000 }, "connection": { + "lastConnected": 1738959398, "lastTrafficSeen": 1739284734, "peakDeviceCount": { "data": 4, @@ -388,6 +389,7 @@ describe('PrometheusManagerMetrics', () => { "seconds": 1000 }, "connection": { + "lastConnected": null, "lastTrafficSeen": null, "peakDeviceCount": { "data": 0, @@ -404,6 +406,7 @@ describe('PrometheusManagerMetrics', () => { "seconds": 0 }, "connection": { + "lastConnected": 1738959398, "lastTrafficSeen": 1738959398, "peakDeviceCount": { "data": 4, diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 422f1162..5c07dbe1 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -16,7 +16,6 @@ import { PrometheusClient, PrometheusMetric, PrometheusValue, - QueryResultData, } from '../infrastructure/prometheus_scraper'; import {DataUsageByUser, DataUsageTimeframe} from '../model/metrics'; @@ -36,6 +35,7 @@ interface TimedData { } interface ConnectionStats { + lastConnected: number | null; lastTrafficSeen: number | null; peakDeviceCount: TimedData; } @@ -112,9 +112,8 @@ 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, @@ -123,31 +122,34 @@ export class PrometheusManagerMetrics implements ManagerMetrics { dataTransferredByAccessKeyRange, tunnelTimeByAccessKeyRange, ] = await Promise.all([ - this.cachedPrometheusClient.queryRange( + this.prometheusClient.query( + `sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))` + ), + this.prometheusClient.queryRange( `sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.cachedPrometheusClient.query( + this.prometheusClient.query( `sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[${timeframe.seconds}s])) by (location, asn, asorg)` ), - this.cachedPrometheusClient.query( + this.prometheusClient.query( `sum(increase(shadowsocks_tunnel_time_seconds_per_location[${timeframe.seconds}s])) by (location, asn, asorg)` ), - this.cachedPrometheusClient.query( + this.prometheusClient.query( `sum(increase(shadowsocks_data_bytes{dir=~"ct"}[${timeframe.seconds}s])) by (access_key)` ), - this.cachedPrometheusClient.query( + this.prometheusClient.query( `sum(increase(shadowsocks_tunnel_time_seconds[${timeframe.seconds}s])) by (access_key)` ), - this.cachedPrometheusClient.queryRange( + this.prometheusClient.queryRange( `sum(increase(shadowsocks_data_bytes{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.cachedPrometheusClient.queryRange( + this.prometheusClient.queryRange( `sum(increase(shadowsocks_tunnel_time_seconds[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`, start, end, @@ -164,15 +166,13 @@ export class PrometheusManagerMetrics implements ManagerMetrics { }, locations: [], }; - - 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 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. } - for (const result of bandwidthRange.result) { const peakDataTransferred = findPeak(result.values ?? []); if (peakDataTransferred !== null) { @@ -211,6 +211,8 @@ 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]); @@ -232,41 +234,6 @@ export class PrometheusManagerMetrics implements ManagerMetrics { accessKeys: Array.from(accessKeyMap.values()), }; } - - private prometheusCache = new Map(); - - 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( @@ -301,6 +268,7 @@ function getServerMetricsAccessKeyEntry( dataTransferred: {bytes: 0}, tunnelTime: {seconds: 0}, connection: { + lastConnected: null, lastTrafficSeen: null, peakDeviceCount: { data: 0, diff --git a/src/shadowbox/server/outline_shadowsocks_server.ts b/src/shadowbox/server/outline_shadowsocks_server.ts index e7c40370..1abe1b2c 100644 --- a/src/shadowbox/server/outline_shadowsocks_server.ts +++ b/src/shadowbox/server/outline_shadowsocks_server.ts @@ -21,12 +21,26 @@ 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; /** @@ -81,22 +95,39 @@ export class OutlineShadowsocksServer implements ShadowsocksServer { private writeConfigFile(keys: ShadowsocksAccessKey[]): Promise { return new Promise((resolve, reject) => { - const keysJson = {keys: [] as ShadowsocksAccessKey[]}; - for (const key of keys) { + const validKeys: ShadowsocksAccessKey[] = keys.filter((key) => { if (!isAeadCipher(key.cipher)) { logging.error( `Cipher ${key.cipher} for access key ${key.id} is not supported: use an AEAD cipher instead.` ); - continue; + return false; } + return true; + }); - keysJson.keys.push(key); + const config: OutlineSSServerConfig = {services: []}; + const keysByPort: Record = {}; + 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); } mkdirp.sync(path.dirname(this.configFilename)); - try { - file.atomicWriteFileSync(this.configFilename, jsyaml.safeDump(keysJson, {sortKeys: true})); + file.atomicWriteFileSync(this.configFilename, jsyaml.safeDump(config, {sortKeys: true})); resolve(); } catch (error) { reject(error); diff --git a/third_party/Taskfile.yml b/third_party/Taskfile.yml index bdcc6807..a2749545 100644 --- a/third_party/Taskfile.yml +++ b/third_party/Taskfile.yml @@ -11,17 +11,17 @@ tasks: prometheus:download-*-*: desc: Download and extract prometheus binary vars: - VERSION: '2.53.4' + VERSION: '2.37.1' GOOS: '{{index .MATCH 0}}' GOARCH: '{{index .MATCH 1}}' TEMPFILE: {sh: mktemp} SHA256: '{{printf "%v/%v" .GOOS .GOARCH | get (dict - "linux/amd64" "b8b497c4610d1b93208252b60c8f20f6b2e78596ae8df43397a2e805aa53d475" - "linux/arm64" "ec7236ecea7154d0bfe142921708b1ae7b5e921e100e0ee85ab92b7c444357e0" - "darwin/amd64" "10066a1aa21c4ddb8d5e61c31b52e898d8ac42c7e99fd757e2fc4b6c20b8075f" - "darwin/arm64" "cb3e638d8e9b4b27a6aa1f45a4faa3741b548aac67d4649aea7a2fad3c09f0a1" - ) + "linux/amd64" "753f66437597cf52ada98c2f459aa8c03745475c249c9f2b40ac7b3919131ba6" + "linux/arm64" "b59a66fb5c7ec5acf6bf426793528a5789a1478a0dad8c64edc2843caf31b1b8" + "darwin/amd64" "e03a43d98955ac3500f57353ea74b5df829074205f195ea6b3b88f55c4575c79" + "darwin/arm64" "eb8a174c82a0fb6c84e81d9a73214318fb4a605115ad61505d7883d02e5a6f52" + ) }}' TARGET_DIR: '{{joinPath .OUTPUT_BASE "prometheus" .GOOS .GOARCH}}' TARGET: '{{joinPath .TARGET_DIR "prometheus"}}' diff --git a/third_party/prometheus/METADATA b/third_party/prometheus/METADATA index 4712de71..6b603f4f 100644 --- a/third_party/prometheus/METADATA +++ b/third_party/prometheus/METADATA @@ -10,21 +10,13 @@ third_party { } url { type: ARCHIVE - value: "https://github.com/prometheus/prometheus/releases/download/v2.53.4/prometheus-2.53.4.linux-amd64.tar.gz" + value: "https://github.com/prometheus/prometheus/releases/download/2.37.1/prometheus-2.37.1.linux-amd64.tar.gz" } url { type: ARCHIVE - value: "https://github.com/prometheus/prometheus/releases/download/v2.53.4/prometheus-2.53.4.linux-arm64.tar.gz" + value: "https://github.com/prometheus/prometheus/releases/download/2.37.1/prometheus-2.37.1.darwin-amd64.tar.gz" } - 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 } + version: "2.37.1" + last_upgrade_date { year: 2022 month: 10 day: 24 } license_type: PERMISSIVE }