diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3b66b7db..34aad372 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -* @Jigsaw-Code/outline-dev +* @OutlineFoundation/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 39bd1af0..441bc05e 100644 --- a/.github/workflows/build_and_test_debug.yml +++ b/.github/workflows/build_and_test_debug.yml @@ -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 diff --git a/.github/workflows/pull_request_checks.yml b/.github/workflows/pull_request_checks.yml index f10203bc..3b27ae42 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@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 diff --git a/README.md b/README.md index 9ab5ac45..dfb518e4 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Outline Server -![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/) +![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/) -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. diff --git a/docs/shadowsocks.md b/docs/shadowsocks.md index e8f426e9..35b899f4 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/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. diff --git a/go.mod b/go.mod index 49855d17..f5572f2c 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,15 @@ module localhost -go 1.22 +go 1.26.3 require ( - github.com/Jigsaw-Code/outline-ss-server v1.9.2-0.20250218165321-888b9dcbdf1c + github.com/Jigsaw-Code/outline-ss-server v1.7.3 github.com/go-task/task/v3 v3.36.0 github.com/google/addlicense v1.1.1 ) require ( - github.com/Jigsaw-Code/outline-sdk v0.0.18-0.20241106233708-faffebb12629 // indirect - github.com/Jigsaw-Code/outline-sdk/x v0.0.0-20250130222646-80b6430a1fc8 // indirect + github.com/Jigsaw-Code/outline-sdk v0.0.14 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmatcuk/doublestar/v4 v4.0.2 // indirect @@ -18,10 +17,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 // indirect - github.com/gorilla/websocket v1.5.3 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/lmittmann/tint v1.0.5 // indirect @@ -41,11 +37,11 @@ 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.26.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/term v0.23.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/crypto v0.45.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 + 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 a7475566..f86ef755 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,7 @@ -github.com/Jigsaw-Code/outline-sdk v0.0.18-0.20241106233708-faffebb12629 h1:sHi1X4vwtNNBUDCbxynGXe7cM/inwTbavowHziaxlbk= -github.com/Jigsaw-Code/outline-sdk v0.0.18-0.20241106233708-faffebb12629/go.mod h1:CFDKyGZA4zatKE4vMLe8TyQpZCyINOeRFbMAmYHxodw= -github.com/Jigsaw-Code/outline-sdk/x v0.0.0-20250130222646-80b6430a1fc8 h1:TsWEVm5XU/SalHlZ6VztE9YwHmN0akCgbWFgQ0j7Mys= -github.com/Jigsaw-Code/outline-sdk/x v0.0.0-20250130222646-80b6430a1fc8/go.mod h1:aFUEz6Z/eD0NS3c3fEIX+JO2D9aIrXCmWTb1zJFlItw= -github.com/Jigsaw-Code/outline-ss-server v1.9.2-0.20250218165321-888b9dcbdf1c h1:e8G2DgRGZmhJCgrJw//+IQYkBckmGA4ZeOP4GjkQEDw= -github.com/Jigsaw-Code/outline-ss-server v1.9.2-0.20250218165321-888b9dcbdf1c/go.mod h1:ArtQJ43aUTB5r1gr27E6aMj4nDOZz5vG4WY4NNKJ6ZM= +github.com/Jigsaw-Code/outline-sdk v0.0.14 h1:uJLvIne7YJNolbX7KDacd8gLidrUzRuweBO2APmQEmI= +github.com/Jigsaw-Code/outline-sdk v0.0.14/go.mod h1:9cEaF6sWWMzY8orcUI9pV5D0oFp2FZArTSyJiYtMQQs= +github.com/Jigsaw-Code/outline-ss-server v1.7.3 h1:UF8AaOV2agRb6edF0U0CtTcwpyIxm6NVDa5QLkQh28E= +github.com/Jigsaw-Code/outline-ss-server v1.7.3/go.mod h1:cKPicPWlLWZKJfkQ3CBpQm8a3gXrA2+dpQvsECqBVz8= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -24,8 +22,6 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/task/v3 v3.36.0 h1:XVJ5hQ5hdzTAulHpAGzbUMUuYr9MUOEQFOFazI3hUsY= github.com/go-task/task/v3 v3.36.0/go.mod h1:XBCIAzuyG/mgZVHMUm3cCznz4+IpsBQRlW1gw7OA5sA= -github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= -github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -36,10 +32,6 @@ github.com/google/addlicense v1.1.1/go.mod h1:Sm/DHu7Jk+T5miFHHehdIjbi4M5+dJDRS3 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= @@ -99,28 +91,28 @@ 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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +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= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.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.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +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= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/package-lock.json b/package-lock.json index b4d82a70..30073280 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" }, diff --git a/src/metrics_server/connection_metrics.spec.ts b/src/metrics_server/connection_metrics.spec.ts index 15fb97ca..41aa3af4 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/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', diff --git a/src/server_manager/README.md b/src/server_manager/README.md index 2672de2d..c0a8a86d 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/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). diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index 39ba2b0d..9bf72a09 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/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 diff --git a/src/shadowbox/CHANGELOG.md b/src/shadowbox/CHANGELOG.md index af5a38eb..8074d6af 100644 --- a/src/shadowbox/CHANGELOG.md +++ b/src/shadowbox/CHANGELOG.md @@ -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) diff --git a/src/shadowbox/README.md b/src/shadowbox/README.md index c0519aa2..8462adf7 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/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 diff --git a/src/shadowbox/Taskfile.yml b/src/shadowbox/Taskfile.yml index fb72a9b8..adec26d4 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") .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"}}' \ diff --git a/src/shadowbox/docker/Dockerfile b/src/shadowbox/docker/Dockerfile index c9d27db7..671c421a 100644 --- a/src/shadowbox/docker/Dockerfile +++ b/src/shadowbox/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 . / diff --git a/src/shadowbox/infrastructure/prometheus_scraper.ts b/src/shadowbox/infrastructure/prometheus_scraper.ts index 5cfe4064..af9ebad5 100644 --- a/src/shadowbox/infrastructure/prometheus_scraper.ts +++ b/src/shadowbox/infrastructure/prometheus_scraper.ts @@ -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 { return new Promise((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(); diff --git a/src/shadowbox/integration_test/client/Dockerfile b/src/shadowbox/integration_test/client/Dockerfile index 31a51bb6..f2915cdf 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/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 diff --git a/src/shadowbox/package.json b/src/shadowbox/package.json index 89a2ef5d..62e4ddd4 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: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", diff --git a/src/shadowbox/server/main.ts b/src/shadowbox/server/main.ts index 8f52c18d..5d1be776 100644 --- a/src/shadowbox/server/main.ts +++ b/src/shadowbox/server/main.ts @@ -48,6 +48,7 @@ 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(); }); @@ -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); diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 3969ac39..422f1162 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -16,6 +16,7 @@ import { PrometheusClient, PrometheusMetric, PrometheusValue, + QueryResultData, } from '../infrastructure/prometheus_scraper'; import {DataUsageByUser, DataUsageTimeframe} from '../model/metrics'; @@ -111,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, @@ -121,34 +123,31 @@ export class PrometheusManagerMetrics implements ManagerMetrics { dataTransferredByAccessKeyRange, tunnelTimeByAccessKeyRange, ] = await Promise.all([ - this.prometheusClient.query( - `sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))` - ), - this.prometheusClient.queryRange( + this.cachedPrometheusClient.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.prometheusClient.query( + this.cachedPrometheusClient.query( `sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[${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=~"ct"}[${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=~"ct"}[${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, @@ -165,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) { @@ -231,6 +232,41 @@ 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( diff --git a/src/shadowbox/server/outline_shadowsocks_server.ts b/src/shadowbox/server/outline_shadowsocks_server.ts index ab60e451..e7c40370 100644 --- a/src/shadowbox/server/outline_shadowsocks_server.ts +++ b/src/shadowbox/server/outline_shadowsocks_server.ts @@ -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,42 +81,22 @@ export class OutlineShadowsocksServer implements ShadowsocksServer { private writeConfigFile(keys: ShadowsocksAccessKey[]): Promise { 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 = {}; - for (const key of validKeys) { - (keysByPort[key.port] ??= []).push(key); - } - for (const port in keysByPort) { - const service = { - listeners: [ - // NOTE: We explicitly specify the address string with only the port - // number. This will result in an address that listens on all - // available network interfaces (both IPv4 and IPv6). - {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); diff --git a/third_party/Taskfile.yml b/third_party/Taskfile.yml index a2749545..bdcc6807 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.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"}}' diff --git a/third_party/prometheus/METADATA b/third_party/prometheus/METADATA index 6b603f4f..4712de71 100644 --- a/third_party/prometheus/METADATA +++ b/third_party/prometheus/METADATA @@ -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 }