mirror of https://github.com/go-gost/gost.git
Compare commits
273 Commits
v3.0.0-rc.
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
e2447ce578 | |
|
|
811420e923 | |
|
|
374b46dfe1 | |
|
|
22edb92084 | |
|
|
78a0a8c734 | |
|
|
41e1878ebc | |
|
|
5639c90e98 | |
|
|
36abf9bcd9 | |
|
|
d584b7ac61 | |
|
|
56e2a1c496 | |
|
|
b0bea19275 | |
|
|
cd64f2edd3 | |
|
|
5f04c84e32 | |
|
|
600a64e611 | |
|
|
3d6b16686b | |
|
|
44684d40c3 | |
|
|
2be36abe75 | |
|
|
8db62785fa | |
|
|
8740e6f258 | |
|
|
d4c9ef5056 | |
|
|
c06eb0d331 | |
|
|
c8b48dc248 | |
|
|
45d94cf391 | |
|
|
bc96fe3918 | |
|
|
646d3f906c | |
|
|
4c69940f1e | |
|
|
3d1f6fcbbb | |
|
|
e388426ec6 | |
|
|
340ba32ef0 | |
|
|
96551d5fa5 | |
|
|
8d05a6ed93 | |
|
|
0348a16aa9 | |
|
|
50934e0978 | |
|
|
c2ed9c6f07 | |
|
|
3b9da4e260 | |
|
|
f0a67a1108 | |
|
|
59c9638ce6 | |
|
|
49fa28882f | |
|
|
08c617b54e | |
|
|
d03b0e2360 | |
|
|
245d610baf | |
|
|
f01f0c9215 | |
|
|
9b6e9d9cae | |
|
|
1a0e2b06ac | |
|
|
de8bb498db | |
|
|
1f735d0649 | |
|
|
d173d167da | |
|
|
b593fdb952 | |
|
|
d448628fea | |
|
|
0bfc7f10cd | |
|
|
a24b29c8c2 | |
|
|
87af3da1b6 | |
|
|
dc6fe595e0 | |
|
|
db4a0a9259 | |
|
|
62a097c5e4 | |
|
|
b51cecbbfd | |
|
|
66a260abd1 | |
|
|
1d3603f9ed | |
|
|
952c61c1a2 | |
|
|
6d61ab82db | |
|
|
f61bb2fd72 | |
|
|
4c2835db04 | |
|
|
d662b1fd46 | |
|
|
015923fa5d | |
|
|
f66fce9d49 | |
|
|
982a93eee4 | |
|
|
758251d3f9 | |
|
|
a1e48e164b | |
|
|
581dd74bbb | |
|
|
3c7a984aa3 | |
|
|
2a9b7e7d9b | |
|
|
ab2b9714ee | |
|
|
6d38c24d88 | |
|
|
f376db2000 | |
|
|
161d96f582 | |
|
|
128c0c3117 | |
|
|
66ce980c36 | |
|
|
829148d3f8 | |
|
|
c45f148a88 | |
|
|
fdc88af4ac | |
|
|
c41c5bf79a | |
|
|
b3eeb338fb | |
|
|
72f185bbf6 | |
|
|
a7599990a8 | |
|
|
17ca5600d3 | |
|
|
32ccfe483c | |
|
|
1d0eb06938 | |
|
|
1fa5067d14 | |
|
|
7ddfef85a5 | |
|
|
fa98777ab1 | |
|
|
e53486efe0 | |
|
|
881ebfad5b | |
|
|
d5bb691b38 | |
|
|
24c13b861a | |
|
|
d6daa8bf56 | |
|
|
d18539ef66 | |
|
|
48b0a690c5 | |
|
|
a7037a27bf | |
|
|
ac9960de6d | |
|
|
404e099907 | |
|
|
94cd590142 | |
|
|
d3809ec634 | |
|
|
efb4da6fa6 | |
|
|
2ec8584e96 | |
|
|
f05f5cf862 | |
|
|
bf3018b5b1 | |
|
|
073af6163e | |
|
|
ef07551e01 | |
|
|
8077c039f3 | |
|
|
33c8f3595a | |
|
|
87bff8cac6 | |
|
|
d52fcccf06 | |
|
|
18610d4608 | |
|
|
c1de9e4e72 | |
|
|
1f7c080043 | |
|
|
b091f72900 | |
|
|
8e50e9c085 | |
|
|
7a35d25c5c | |
|
|
8e311e7891 | |
|
|
17fc806c2d | |
|
|
b21fd4a22b | |
|
|
3e8a025c45 | |
|
|
0d7a6f8091 | |
|
|
4a2a2a00de | |
|
|
c71e128fec | |
|
|
1400b5ff5d | |
|
|
cd4fad8c08 | |
|
|
56a68ae06f | |
|
|
5d7100f749 | |
|
|
12871a4ff3 | |
|
|
6130bbc075 | |
|
|
85d4fcaf7d | |
|
|
7904e571c1 | |
|
|
8253c41de6 | |
|
|
2148d10def | |
|
|
8846c69c05 | |
|
|
2161b97064 | |
|
|
e245526b33 | |
|
|
60389894c0 | |
|
|
9ab142ac99 | |
|
|
be1b0627d1 | |
|
|
f4115b867d | |
|
|
a513417d7e | |
|
|
58bd3d062b | |
|
|
d2b79c13a6 | |
|
|
fedd7a7b07 | |
|
|
27f4b6abeb | |
|
|
d4aadbbc14 | |
|
|
4508747635 | |
|
|
50844821f8 | |
|
|
29ec7c94ad | |
|
|
53d6706b5d | |
|
|
594418db53 | |
|
|
72cd708dfe | |
|
|
b576bb5645 | |
|
|
8c529e3595 | |
|
|
b75e133719 | |
|
|
abfbf23842 | |
|
|
42d62550bf | |
|
|
69ba5864ad | |
|
|
fdd3eb61c3 | |
|
|
f68bfdb149 | |
|
|
9e4bca84f1 | |
|
|
925ff89b78 | |
|
|
ca077d418a | |
|
|
7264fca4f8 | |
|
|
bc37fac037 | |
|
|
dc48c4a3b2 | |
|
|
310614663a | |
|
|
8f094d86b0 | |
|
|
638c780acd | |
|
|
1f20f199c0 | |
|
|
8dc6146f79 | |
|
|
ed29b34c68 | |
|
|
b41aea03da | |
|
|
2c17e4417d | |
|
|
ea8ff5f77a | |
|
|
6762957c65 | |
|
|
4c934cd737 | |
|
|
0d494f5ef2 | |
|
|
bf1bab7d84 | |
|
|
b8383b1076 | |
|
|
345da28f01 | |
|
|
907420d18b | |
|
|
c1437794f8 | |
|
|
c2df5650fb | |
|
|
23506579e7 | |
|
|
969db8691b | |
|
|
3b0a2f1bae | |
|
|
b2ed4ae9fd | |
|
|
b2784011d0 | |
|
|
d4e00683c3 | |
|
|
471513896d | |
|
|
6967278600 | |
|
|
2898732c20 | |
|
|
bb0c0450e8 | |
|
|
6bb7dfea8f | |
|
|
e27d2bc593 | |
|
|
3144d61ecd | |
|
|
e4b68d385a | |
|
|
a91300d078 | |
|
|
b8785eb71e | |
|
|
67fcd929be | |
|
|
48c262bf71 | |
|
|
05c35005e9 | |
|
|
f6112773a1 | |
|
|
61002b7c9c | |
|
|
f8a19e0829 | |
|
|
b1ace73943 | |
|
|
3d0e993498 | |
|
|
9cbc875965 | |
|
|
139b934fc6 | |
|
|
c1d9228eee | |
|
|
2ea7dc250a | |
|
|
be33046c16 | |
|
|
d85b8d487b | |
|
|
ab8d77bb5e | |
|
|
8640596064 | |
|
|
8a699e1906 | |
|
|
0d41f5edc8 | |
|
|
87364d2494 | |
|
|
6982b8af63 | |
|
|
0b1540b23a | |
|
|
1bbeeb7e66 | |
|
|
5d4db6e377 | |
|
|
05d06a2718 | |
|
|
420b36b2f8 | |
|
|
3883a4493a | |
|
|
355aaa7690 | |
|
|
c73c98974c | |
|
|
98c9cc2821 | |
|
|
b48d2c0dbc | |
|
|
b7266f4660 | |
|
|
6883c32157 | |
|
|
f1c5f7598c | |
|
|
c31242efbb | |
|
|
6ee632cee2 | |
|
|
8e2060582e | |
|
|
410b4ddf53 | |
|
|
0b6474d836 | |
|
|
4b6d6e7e7c | |
|
|
b2434dd05b | |
|
|
3d15dedab2 | |
|
|
436864ca92 | |
|
|
4847f9ee9f | |
|
|
a88b44d01d | |
|
|
45f1cc2efc | |
|
|
bfa1530ef6 | |
|
|
f02c9810f2 | |
|
|
fda6214c12 | |
|
|
acb8c5bd53 | |
|
|
3f53858c83 | |
|
|
a379565412 | |
|
|
69089b790f | |
|
|
1c2d9b2a5b | |
|
|
8d852b62cf | |
|
|
a80dec0dc1 | |
|
|
62ca8ff2de | |
|
|
b8504f7f28 | |
|
|
95a2f0d649 | |
|
|
d810224e7b | |
|
|
db36804bfa | |
|
|
ad8af1942d | |
|
|
3d01f74f7c | |
|
|
6b05bcb0ab | |
|
|
799c95bfb4 | |
|
|
de0a584175 | |
|
|
381b1c9bf5 | |
|
|
ed7d72bfd8 | |
|
|
98aef6ba90 | |
|
|
dfde6efdeb | |
|
|
8858598cf0 | |
|
|
c765789076 |
|
|
@ -1,89 +0,0 @@
|
||||||
# ref: https://docs.docker.com/ci-cd/github-actions/
|
|
||||||
# https://blog.oddbit.com/post/2020-09-25-building-multi-architecture-im/
|
|
||||||
|
|
||||||
name: Docker
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Prepare
|
|
||||||
id: prepare
|
|
||||||
run: |
|
|
||||||
DOCKER_IMAGE=${{ secrets.DOCKER_IMAGE }}
|
|
||||||
VERSION=latest
|
|
||||||
|
|
||||||
# If this is git tag, use the tag name as a docker tag
|
|
||||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
|
||||||
VERSION=${GITHUB_REF#refs/tags/v}
|
|
||||||
fi
|
|
||||||
TAGS="${DOCKER_IMAGE}:${VERSION}"
|
|
||||||
|
|
||||||
# If the VERSION looks like a version number, assume that
|
|
||||||
# this is the most recent version of the image and also
|
|
||||||
# tag it 'latest'.
|
|
||||||
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
|
||||||
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set output parameters.
|
|
||||||
echo ::set-output name=tags::${TAGS}
|
|
||||||
echo ::set-output name=docker_image::${DOCKER_IMAGE}
|
|
||||||
echo ::set-output name=docker_platforms::linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/s390x
|
|
||||||
|
|
||||||
# https://github.com/crazy-max/ghaction-docker-buildx
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
id: buildx
|
|
||||||
uses: crazy-max/ghaction-docker-buildx@v1
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
|
|
||||||
- name: Environment
|
|
||||||
run: |
|
|
||||||
echo home=$HOME
|
|
||||||
echo git_ref=$GITHUB_REF
|
|
||||||
echo git_sha=$GITHUB_SHA
|
|
||||||
echo version=${{ steps.prepare.outputs.version }}
|
|
||||||
echo image=${{ steps.prepare.outputs.docker_image }}
|
|
||||||
echo platforms=${{ steps.prepare.outputs.docker_platforms }}
|
|
||||||
echo avail_platforms=${{ steps.buildx.outputs.platforms }}
|
|
||||||
|
|
||||||
# https://github.com/actions/checkout
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Docker Buildx (no push)
|
|
||||||
run: |
|
|
||||||
docker buildx bake \
|
|
||||||
--set ${{ github.event.repository.name }}.platform=${{ steps.prepare.outputs.docker_platforms }} \
|
|
||||||
--set ${{ github.event.repository.name }}.output=type=image,push=false \
|
|
||||||
--set ${{ github.event.repository.name }}.tags="${{ steps.prepare.outputs.tags }}" \
|
|
||||||
--file docker-compose.yaml
|
|
||||||
|
|
||||||
- name: Docker Login
|
|
||||||
if: success()
|
|
||||||
env:
|
|
||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
echo "${DOCKER_PASSWORD}" | docker login --username "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
|
||||||
|
|
||||||
- name: Docker Buildx (push)
|
|
||||||
if: success()
|
|
||||||
run: |
|
|
||||||
docker buildx bake \
|
|
||||||
--set ${{ github.event.repository.name }}.platform=${{ steps.prepare.outputs.docker_platforms }} \
|
|
||||||
--set ${{ github.event.repository.name }}.output=type=image,push=true \
|
|
||||||
--set ${{ github.event.repository.name }}.tags="${{ steps.prepare.outputs.tags }}" \
|
|
||||||
--file docker-compose.yaml
|
|
||||||
|
|
||||||
- name: Clear
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
rm -f ${HOME}/.docker/config.json
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
# ref: https://docs.docker.com/ci-cd/github-actions/
|
||||||
|
# https://blog.oddbit.com/post/2020-09-25-building-multi-architecture-im/
|
||||||
|
|
||||||
|
name: docker
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
id: prepare
|
||||||
|
run: |
|
||||||
|
DOCKER_IMAGE=${{ secrets.DOCKER_IMAGE }}
|
||||||
|
VERSION=latest
|
||||||
|
|
||||||
|
# If this is git tag, use the tag name as a docker tag
|
||||||
|
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/v}
|
||||||
|
fi
|
||||||
|
TAGS="${DOCKER_IMAGE}:${VERSION}"
|
||||||
|
|
||||||
|
# If the VERSION looks like a version number, assume that
|
||||||
|
# this is the most recent version of the image and also
|
||||||
|
# tag it 'latest'.
|
||||||
|
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||||
|
MAJOR_VERSION=`echo $VERSION | awk '{split($0,a,"."); print a[1]}'`
|
||||||
|
MINOR_VERSION=`echo $VERSION | awk '{split($0,a,"."); print a[2]}'`
|
||||||
|
TAGS="$TAGS,${DOCKER_IMAGE}:${MAJOR_VERSION},${DOCKER_IMAGE}:${MAJOR_VERSION}.${MINOR_VERSION},${DOCKER_IMAGE}:latest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set output parameters.
|
||||||
|
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
|
||||||
|
echo "docker_image=${DOCKER_IMAGE}" >> $GITHUB_OUTPUT
|
||||||
|
echo "docker_platforms=linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/s390x,linux/riscv64" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Environment
|
||||||
|
run: |
|
||||||
|
echo home=$HOME
|
||||||
|
echo git_ref=$GITHUB_REF
|
||||||
|
echo git_sha=$GITHUB_SHA
|
||||||
|
echo image=${{ steps.prepare.outputs.docker_image }}
|
||||||
|
echo tags=${{ steps.prepare.outputs.tags }}
|
||||||
|
echo platforms=${{ steps.prepare.outputs.docker_platforms }}
|
||||||
|
echo avail_platforms=${{ steps.buildx.outputs.platforms }}
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Buildx and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
platforms: ${{ steps.prepare.outputs.docker_platforms }}
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.prepare.outputs.tags }}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
name: goreleaser
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# run only against tags
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
# packages: write
|
||||||
|
# issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- run: git fetch --force --tags
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.26'
|
||||||
|
cache: true
|
||||||
|
- name: Install UPX
|
||||||
|
uses: crazy-max/ghaction-upx@v3
|
||||||
|
with:
|
||||||
|
install-only: true
|
||||||
|
# More assembly might be required: Docker logins, GPG, etc. It all depends
|
||||||
|
# on your needs.
|
||||||
|
- uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
# either 'goreleaser' (default) or 'goreleaser-pro':
|
||||||
|
distribution: goreleaser
|
||||||
|
version: '~> v2'
|
||||||
|
args: release --clean
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# Your GoReleaser Pro key, if you are using the 'goreleaser-pro'
|
||||||
|
# distribution:
|
||||||
|
# GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
name: Trigger nightly build
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# * is a special character in YAML, so you have to quote this string
|
||||||
|
- cron: '00 15 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_date:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Check latest commit
|
||||||
|
outputs:
|
||||||
|
should_run: ${{ steps.should_run.outputs.should_run }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: print latest_commit
|
||||||
|
run: echo ${{ github.sha }}
|
||||||
|
- id: should_run
|
||||||
|
continue-on-error: true
|
||||||
|
name: check latest commit is less than a day
|
||||||
|
if: ${{ github.event_name == 'schedule' }}
|
||||||
|
run: test -z $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "should_run=false" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
trigger-nightly:
|
||||||
|
needs: check_date
|
||||||
|
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||||
|
name: Push tag for nightly build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: 'Checkout'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.NIGHTLY_BUILD_GH_TOKEN }}
|
||||||
|
fetch-depth: 0
|
||||||
|
-
|
||||||
|
name: 'Push new tag'
|
||||||
|
run: |
|
||||||
|
git config user.name "${GITHUB_ACTOR}"
|
||||||
|
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
|
||||||
|
|
||||||
|
# A previous release was created using a lightweight tag
|
||||||
|
# git describe by default includes only annotated tags
|
||||||
|
# git describe --tags includes lightweight tags as well
|
||||||
|
DESCRIBE=`git tag -l --sort=-v:refname | grep -v nightly | head -n 1`
|
||||||
|
MAJOR_VERSION=`echo $DESCRIBE | awk '{split($0,a,"."); print a[1]}'`
|
||||||
|
MINOR_VERSION=`echo $DESCRIBE | awk '{split($0,a,"."); print a[2]}'`
|
||||||
|
PATCH_VERSION=`echo $DESCRIBE | awk '{split($0,a,"."); print a[3]}'`
|
||||||
|
PATCH_VERSION="$((${PATCH_VERSION} + 1))"
|
||||||
|
TAG="${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}-nightly.$(date +'%Y%m%d')"
|
||||||
|
git tag -a $TAG -m "$TAG: nightly build"
|
||||||
|
git push origin $TAG
|
||||||
|
- name: 'Clean up nightly releases'
|
||||||
|
uses: dev-drprasad/delete-older-releases@v0.3.3
|
||||||
|
with:
|
||||||
|
keep_latest: 2
|
||||||
|
delete_tags: true
|
||||||
|
delete_tag_pattern: nightly
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.NIGHTLY_BUILD_GH_TOKEN }}
|
||||||
|
|
@ -32,8 +32,10 @@ _testmain.go
|
||||||
*.bak
|
*.bak
|
||||||
|
|
||||||
cmd/gost/gost
|
cmd/gost/gost
|
||||||
|
gost
|
||||||
snap
|
snap
|
||||||
|
|
||||||
*.pem
|
*.pem
|
||||||
*.yaml
|
/*.yaml
|
||||||
*.txt
|
*.txt
|
||||||
|
dist/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||||
|
# Make sure to check the documentation at https://goreleaser.com
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
# You may remove this if you don't use go modules.
|
||||||
|
- go mod tidy
|
||||||
|
# you may remove this if you don't need go generate
|
||||||
|
# - go generate ./...
|
||||||
|
builds:
|
||||||
|
- env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
main: ./cmd/gost
|
||||||
|
targets:
|
||||||
|
- darwin_amd64
|
||||||
|
- darwin_arm64
|
||||||
|
- linux_386
|
||||||
|
- linux_amd64
|
||||||
|
- linux_amd64_v3
|
||||||
|
- linux_arm_5
|
||||||
|
- linux_arm_6
|
||||||
|
- linux_arm_7
|
||||||
|
- linux_arm64
|
||||||
|
- linux_mips_softfloat
|
||||||
|
- linux_mips_hardfloat
|
||||||
|
- linux_mipsle_softfloat
|
||||||
|
- linux_mipsle_hardfloat
|
||||||
|
- linux_mips64
|
||||||
|
- linux_mips64le
|
||||||
|
- linux_s390x
|
||||||
|
- linux_riscv64
|
||||||
|
- linux_loong64
|
||||||
|
- freebsd_386
|
||||||
|
- freebsd_amd64
|
||||||
|
- windows_386
|
||||||
|
- windows_amd64
|
||||||
|
- windows_amd64_v3
|
||||||
|
- windows_arm64
|
||||||
|
- android_arm64
|
||||||
|
ldflags:
|
||||||
|
- "-s -w -X 'main.version={{ .Tag }}'"
|
||||||
|
|
||||||
|
upx:
|
||||||
|
- # UPX compression. Disabled by default (see #863): upx --best/--lzma/--brute
|
||||||
|
# adds ~3s startup time on low-spec systems (linux/arm). Release builds can
|
||||||
|
# opt in via GORELEASER_UPX=true environment variable.
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- format: tar.gz
|
||||||
|
# use zip for windows archives
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ incpatch .Version }}-next"
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- '^docs:'
|
||||||
|
- '^test:'
|
||||||
|
|
||||||
|
release:
|
||||||
|
prerelease: auto
|
||||||
|
mode: keep-existing
|
||||||
|
|
||||||
|
# The lines beneath this are called `modelines`. See `:help modeline`
|
||||||
|
# Feel free to remove those if you don't want/use them.
|
||||||
|
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||||
|
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
# CLAUDE.md — gost/
|
||||||
|
|
||||||
|
CLI binary entry point for GOST (GO Simple Tunnel). This module compiles the `gost` command.
|
||||||
|
|
||||||
|
## Build & Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd gost && go build ./cmd/gost/...
|
||||||
|
|
||||||
|
# Cross-compile
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" ./cmd/gost/...
|
||||||
|
|
||||||
|
# Build all platforms
|
||||||
|
make all
|
||||||
|
|
||||||
|
# Run
|
||||||
|
./gost -L "http://:8080" -F "socks5://:1080"
|
||||||
|
./gost -C gost.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `cmd/gost/main.go` | CLI entry point: flag parsing, multi-worker mode (`--` separator), program launch via `svc.Run` |
|
||||||
|
| `cmd/gost/program.go` | Service lifecycle (`Init`/`Start`/`Stop`), config loading, API/metrics/profiling servers, SIGHUP reload |
|
||||||
|
| `cmd/gost/register.go` | Blank imports for all built-in handlers, listeners, dialers, connectors — triggers `init()` registration |
|
||||||
|
| `cmd/gost/version.go` | Version string (`3.3.0`) |
|
||||||
|
|
||||||
|
## CLI Flags
|
||||||
|
|
||||||
|
| Flag | Usage |
|
||||||
|
|------|-------|
|
||||||
|
| `-L` | Inline service definition (repeatable) |
|
||||||
|
| `-F` | Inline chain node definition (repeatable) |
|
||||||
|
| `-C` | Path to config file (YAML/JSON) |
|
||||||
|
| `-D` / `-DD` | Debug / trace logging |
|
||||||
|
| `-api` | API service address (e.g. `:8080`) |
|
||||||
|
| `-metrics` | Prometheus metrics address |
|
||||||
|
| `-O` | Output merged config as yaml or json, then exit |
|
||||||
|
| `-V` | Print version and exit |
|
||||||
|
|
||||||
|
## Multi-Worker Mode
|
||||||
|
|
||||||
|
Arguments separated by ` -- ` spawn multiple worker processes via `exec.CommandContext`. Each worker runs as a child process with `_GOST_ID` set. Any worker's exit cancels all others. This is triggered in `init()` before flag parsing.
|
||||||
|
|
||||||
|
## Service Lifecycle
|
||||||
|
|
||||||
|
- `Init` → calls `parser.Init()` with all CLI/config inputs
|
||||||
|
- `Start` → `parser.Parse()` → `loader.Load()` → `p.run()` (starts services, API, metrics, profiling)
|
||||||
|
- `Stop` → cancels reload context, closes all services
|
||||||
|
- SIGHUP → `reloadConfig()` re-parses and re-runs without restarting the process
|
||||||
|
|
||||||
|
## Key Dependencies
|
||||||
|
|
||||||
|
- `github.com/go-gost/core` — interface definitions
|
||||||
|
- `github.com/go-gost/x` — all implementations, config, registry
|
||||||
|
- `github.com/judwhite/go-svc` — OS service framework (handles daemon/SIGHUP/SIGTERM)
|
||||||
|
|
||||||
|
## Registration Pattern
|
||||||
|
|
||||||
|
All components register via `init()` side-effects in their packages. `cmd/gost/register.go` triggers them with blank imports. When adding a new built-in handler/listener/dialer/connector, add a blank import here.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Tests are in `tests/e2e/` — integration tests using the built binary. No unit tests. Run with `go test ./tests/e2e/...`.
|
||||||
35
Dockerfile
35
Dockerfile
|
|
@ -1,28 +1,37 @@
|
||||||
FROM --platform=$BUILDPLATFORM golang:1.19-alpine as builder
|
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.6.1 AS xx
|
||||||
|
|
||||||
# Convert TARGETPLATFORM to GOARCH format
|
FROM --platform=$BUILDPLATFORM golang:1.26-alpine3.23 AS builder
|
||||||
# https://github.com/tonistiigi/xx
|
|
||||||
COPY --from=tonistiigi/xx:golang / /
|
# UPX compression disabled by default (see #863): upx --best adds ~3s startup
|
||||||
|
# time on low-spec systems (linux/arm). Builds can opt in by uncommenting the
|
||||||
|
# upx install line and adding upx --best to the build command below.
|
||||||
|
# RUN apk add --no-cache upx || echo "upx not found"
|
||||||
|
|
||||||
|
COPY --from=xx / /
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
RUN apk add --no-cache musl-dev git gcc
|
RUN xx-info env
|
||||||
|
|
||||||
ADD . /src
|
ENV CGO_ENABLED=0
|
||||||
|
|
||||||
WORKDIR /src
|
ENV XX_VERIFY_STATIC=1
|
||||||
|
|
||||||
ENV GO111MODULE=on
|
WORKDIR /app
|
||||||
|
|
||||||
RUN cd cmd/gost && go env && go build
|
COPY . .
|
||||||
|
|
||||||
FROM alpine:latest
|
RUN cd cmd/gost && \
|
||||||
|
xx-go build -ldflags "-s -w" && \
|
||||||
|
xx-verify gost
|
||||||
|
|
||||||
# add iptables for tun/tap
|
FROM alpine:3.23
|
||||||
RUN apk add --no-cache iptables
|
|
||||||
|
# add iptables/nftables for tun/tap
|
||||||
|
RUN apk add --no-cache iptables nftables
|
||||||
|
|
||||||
WORKDIR /bin/
|
WORKDIR /bin/
|
||||||
|
|
||||||
COPY --from=builder /src/cmd/gost/gost .
|
COPY --from=builder /app/cmd/gost/gost .
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/gost"]
|
ENTRYPOINT ["/bin/gost"]
|
||||||
73
README.md
73
README.md
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
### GO语言实现的安全隧道
|
### GO语言实现的安全隧道
|
||||||
|
|
||||||
[English README](README_en.md)
|
[](README.md) [](README_en.md)
|
||||||
|
|
||||||
## 功能特性
|
## 功能特性
|
||||||
|
|
||||||
|
|
@ -10,34 +10,61 @@
|
||||||
- [x] [多级转发链](https://gost.run/concepts/chain/)
|
- [x] [多级转发链](https://gost.run/concepts/chain/)
|
||||||
- [x] [多协议支持](https://gost.run/tutorials/protocols/overview/)
|
- [x] [多协议支持](https://gost.run/tutorials/protocols/overview/)
|
||||||
- [x] [TCP/UDP端口转发](https://gost.run/tutorials/port-forwarding/)
|
- [x] [TCP/UDP端口转发](https://gost.run/tutorials/port-forwarding/)
|
||||||
|
- [x] [反向代理](https://gost.run/tutorials/reverse-proxy/)和[隧道](https://gost.run/tutorials/reverse-proxy-tunnel/)
|
||||||
- [x] [TCP/UDP透明代理](https://gost.run/tutorials/redirect/)
|
- [x] [TCP/UDP透明代理](https://gost.run/tutorials/redirect/)
|
||||||
- [x] DNS[解析](https://gost.run/concepts/resolver/)和[代理](https://gost.run/tutorials/dns/)
|
- [x] DNS[解析](https://gost.run/concepts/resolver/)和[代理](https://gost.run/tutorials/dns/)
|
||||||
- [x] [TUN/TAP设备](https://gost.run/tutorials/tuntap/)
|
- [x] [TUN/TAP设备](https://gost.run/tutorials/tuntap/)与[TUN2SOCKS](https://gost.run/tutorials/tungo/)
|
||||||
- [x] [反向代理](https://gost.run/tutorials/reverse-proxy/)
|
|
||||||
- [x] [负载均衡](https://gost.run/concepts/selector/)
|
- [x] [负载均衡](https://gost.run/concepts/selector/)
|
||||||
- [x] [路由控制](https://gost.run/concepts/bypass/)
|
- [x] [路由控制](https://gost.run/concepts/bypass/)
|
||||||
- [x] [限速限流](https://gost.run/concepts/limiter/)
|
|
||||||
- [x] [准入控制](https://gost.run/concepts/admission/)
|
- [x] [准入控制](https://gost.run/concepts/admission/)
|
||||||
- [x] [动态配置](https://gost.run/tutorials/api/config/)
|
- [x] [限速限流](https://gost.run/concepts/limiter/)
|
||||||
|
- [x] [插件系统](https://gost.run/concepts/plugin/)
|
||||||
- [x] [Prometheus监控指标](https://gost.run/tutorials/metrics/)
|
- [x] [Prometheus监控指标](https://gost.run/tutorials/metrics/)
|
||||||
|
- [x] [动态配置](https://gost.run/tutorials/api/config/)
|
||||||
- [x] [Web API](https://gost.run/tutorials/api/overview/)
|
- [x] [Web API](https://gost.run/tutorials/api/overview/)
|
||||||
- [ ] Web UI
|
- [x] [GUI](https://github.com/go-gost/gostctl)/[WebUI](https://github.com/go-gost/gost-ui)
|
||||||
|
|
||||||
Wiki站点:[https://gost.run](https://gost.run)
|
## 概览
|
||||||
|
|
||||||
Telegram讨论群:[https://t.me/gogost](https://t.me/gogost)
|

|
||||||
|
|
||||||
Google讨论组:[https://groups.google.com/d/forum/go-gost](https://groups.google.com/d/forum/go-gost)
|
GOST作为隧道有三种主要使用方式。
|
||||||
|
|
||||||
旧版入口:[v2.gost.run](https://v2.gost.run)
|
### 正向代理
|
||||||
|
|
||||||
|
作为代理服务访问网络,可以组合使用多种协议组成转发链进行转发。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 端口转发
|
||||||
|
|
||||||
|
将一个服务的端口映射到另外一个服务的端口,同样可以组合使用多种协议组成转发链进行转发。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 反向代理
|
||||||
|
|
||||||
|
利用隧道和内网穿透将内网服务暴露到公网访问。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 下载安装
|
## 下载安装
|
||||||
|
|
||||||
|
|
||||||
### 二进制文件
|
### 二进制文件
|
||||||
|
|
||||||
[https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
[https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
||||||
|
|
||||||
|
### 安装脚本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装最新版本 [https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
||||||
|
bash <(curl -fsSL https://github.com/go-gost/gost/raw/master/install.sh) --install
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
# 选择要安装的版本
|
||||||
|
bash <(curl -fsSL https://github.com/go-gost/gost/raw/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
### 源码编译
|
### 源码编译
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -52,6 +79,28 @@ go build
|
||||||
docker run --rm gogost/gost -V
|
docker run --rm gogost/gost -V
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 工具
|
||||||
|
|
||||||
|
### GUI
|
||||||
|
|
||||||
|
[go-gost/gostctl](https://github.com/go-gost/gostctl)
|
||||||
|
|
||||||
|
### WebUI
|
||||||
|
|
||||||
|
[go-gost/gost-ui](https://github.com/go-gost/gost-ui)
|
||||||
|
|
||||||
### Shadowsocks Android插件
|
### Shadowsocks Android插件
|
||||||
|
|
||||||
[xausky/ShadowsocksGostPlugin](https://github.com/xausky/ShadowsocksGostPlugin)
|
[hamid-nazari/ShadowsocksGostPlugin](https://github.com/hamid-nazari/ShadowsocksGostPlugin)
|
||||||
|
|
||||||
|
## 帮助与支持
|
||||||
|
|
||||||
|
Wiki站点:[https://gost.run](https://gost.run)
|
||||||
|
|
||||||
|
YouTube: [https://www.youtube.com/@gost-tunnel](https://www.youtube.com/@gost-tunnel)
|
||||||
|
|
||||||
|
Telegram:[https://t.me/gogost](https://t.me/gogost)
|
||||||
|
|
||||||
|
Google讨论组:[https://groups.google.com/d/forum/go-gost](https://groups.google.com/d/forum/go-gost)
|
||||||
|
|
||||||
|
旧版入口:[v2.gost.run](https://v2.gost.run)
|
||||||
|
|
|
||||||
73
README_en.md
73
README_en.md
|
|
@ -2,40 +2,69 @@
|
||||||
|
|
||||||
### A simple security tunnel written in golang
|
### A simple security tunnel written in golang
|
||||||
|
|
||||||
|
[](README_en.md) [](README.md)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- [x] [Listening on multiple ports](https://gost.run/en/getting-started/quick-start/)
|
- [x] [Listening on multiple ports](https://gost.run/en/getting-started/quick-start/)
|
||||||
- [x] [Multi-level forwarding chain](https://gost.run/en/concepts/chain/)
|
- [x] [Multi-level forwarding chain](https://gost.run/en/concepts/chain/)
|
||||||
- [x] Rich protocol
|
- [x] Rich protocol
|
||||||
- [x] [TCP/UDP port forwarding](https://gost.run/en/tutorials/port-forwarding/)
|
- [x] [TCP/UDP port forwarding](https://gost.run/en/tutorials/port-forwarding/)
|
||||||
|
- [x] [Reverse Proxy](https://gost.run/en/tutorials/reverse-proxy/) and [Tunnel](https://gost.run/en/tutorials/reverse-proxy-tunnel/)
|
||||||
- [x] [TCP/UDP transparent proxy](https://gost.run/en/tutorials/redirect/)
|
- [x] [TCP/UDP transparent proxy](https://gost.run/en/tutorials/redirect/)
|
||||||
- [x] DNS [resolver](https://gost.run/en/concepts/resolver/) and [proxy](https://gost.run/en/tutorials/dns/)
|
- [x] DNS [resolver](https://gost.run/en/concepts/resolver/) and [proxy](https://gost.run/en/tutorials/dns/)
|
||||||
- [x] [TUN/TAP device](https://gost.run/en/tutorials/tuntap/)
|
- [x] [TUN/TAP device](https://gost.run/en/tutorials/tuntap/) and [TUN2SOCKS](https://gost.run/en/tutorials/tungo/)
|
||||||
- [x] [Reverse Proxy](https://gost.run/en/tutorials/reverse-proxy/)
|
|
||||||
- [x] [Load balancing](https://gost.run/en/concepts/selector/)
|
- [x] [Load balancing](https://gost.run/en/concepts/selector/)
|
||||||
- [x] [Routing control](https://gost.run/en/concepts/bypass/)
|
- [x] [Routing control](https://gost.run/en/concepts/bypass/)
|
||||||
- [x] [Bandwidth/Rate Limiter](https://gost.run/en/concepts/limiter/)
|
|
||||||
- [x] [Admission control](https://gost.run/en/concepts/limiter/)
|
- [x] [Admission control](https://gost.run/en/concepts/limiter/)
|
||||||
- [x] [Dynamic configuration](https://gost.run/en/tutorials/api/config/)
|
- [x] [Bandwidth/Rate Limiter](https://gost.run/en/concepts/limiter/)
|
||||||
|
- [x] [Plugin System](https://gost.run/en/concepts/plugin/)
|
||||||
- [x] [Prometheus metrics](https://gost.run/en/tutorials/metrics/)
|
- [x] [Prometheus metrics](https://gost.run/en/tutorials/metrics/)
|
||||||
|
- [x] [Dynamic configuration](https://gost.run/en/tutorials/api/config/)
|
||||||
- [x] [Web API](https://gost.run/en/tutorials/api/overview/)
|
- [x] [Web API](https://gost.run/en/tutorials/api/overview/)
|
||||||
- [ ] Web UI
|
- [x] [GUI](https://github.com/go-gost/gostctl)/[WebUI](https://github.com/go-gost/gost-ui)
|
||||||
|
|
||||||
Wiki: [https://gost.run](https://gost.run/en/)
|
## Overview
|
||||||
|
|
||||||
Telegram: [https://t.me/gogost](https://t.me/gogost)
|

|
||||||
|
|
||||||
Google group: [https://groups.google.com/d/forum/go-gost](https://groups.google.com/d/forum/go-gost)
|
There are three main ways to use GOST as a tunnel.
|
||||||
|
|
||||||
Legacy version: [v2.gost.run](https://v2.gost.run/en/)
|
### Proxy
|
||||||
|
|
||||||
|
As a proxy service to access the network, multiple protocols can be used in combination to form a forwarding chain for traffic forwarding.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Port Forwarding
|
||||||
|
|
||||||
|
Mapping the port of one service to the port of another service, you can also use a combination of multiple protocols to form a forwarding chain for traffic forwarding.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Reverse Proxy
|
||||||
|
|
||||||
|
Use tunnel and intranet penetration to expose local services behind NAT or firewall to public network for access.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
||||||
### Binary files
|
### Binary files
|
||||||
|
|
||||||
[https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
[https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
||||||
|
|
||||||
|
### install script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# install latest from [https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
||||||
|
bash <(curl -fsSL https://github.com/go-gost/gost/raw/master/install.sh) --install
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
# select version for install
|
||||||
|
bash <(curl -fsSL https://github.com/go-gost/gost/raw/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
### From source
|
### From source
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -50,6 +79,28 @@ go build
|
||||||
docker run --rm gogost/gost -V
|
docker run --rm gogost/gost -V
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
### GUI
|
||||||
|
|
||||||
|
[go-gost/gostctl](https://github.com/go-gost/gostctl)
|
||||||
|
|
||||||
|
### WebUI
|
||||||
|
|
||||||
|
[go-gost/gost-ui](https://github.com/go-gost/gost-ui)
|
||||||
|
|
||||||
### Shadowsocks Android
|
### Shadowsocks Android
|
||||||
|
|
||||||
[xausky/ShadowsocksGostPlugin](https://github.com/xausky/ShadowsocksGostPlugin)
|
[hamid-nazari/ShadowsocksGostPlugin](https://github.com/hamid-nazari/ShadowsocksGostPlugin)
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Wiki: [https://gost.run](https://gost.run/en/)
|
||||||
|
|
||||||
|
YouTube: [https://www.youtube.com/@gost-tunnel](https://www.youtube.com/@gost-tunnel)
|
||||||
|
|
||||||
|
Telegram: [https://t.me/gogost](https://t.me/gogost)
|
||||||
|
|
||||||
|
Google group: [https://groups.google.com/d/forum/go-gost](https://groups.google.com/d/forum/go-gost)
|
||||||
|
|
||||||
|
Legacy version: [v2.gost.run](https://v2.gost.run/en/)
|
||||||
|
|
|
||||||
595
cmd/gost/cmd.go
595
cmd/gost/cmd.go
|
|
@ -1,595 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
mdutil "github.com/go-gost/core/metadata/util"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
"github.com/go-gost/x/limiter/conn"
|
|
||||||
"github.com/go-gost/x/limiter/traffic"
|
|
||||||
mdx "github.com/go-gost/x/metadata"
|
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidCmd = errors.New("invalid cmd")
|
|
||||||
ErrInvalidNode = errors.New("invalid node")
|
|
||||||
)
|
|
||||||
|
|
||||||
type stringList []string
|
|
||||||
|
|
||||||
func (l *stringList) String() string {
|
|
||||||
return fmt.Sprintf("%s", *l)
|
|
||||||
}
|
|
||||||
func (l *stringList) Set(value string) error {
|
|
||||||
*l = append(*l, value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
|
|
||||||
namePrefix := ""
|
|
||||||
cfg := &config.Config{}
|
|
||||||
|
|
||||||
var chain *config.ChainConfig
|
|
||||||
if len(nodes) > 0 {
|
|
||||||
chain = &config.ChainConfig{
|
|
||||||
Name: fmt.Sprintf("%schain-0", namePrefix),
|
|
||||||
}
|
|
||||||
cfg.Chains = append(cfg.Chains, chain)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, node := range nodes {
|
|
||||||
url, err := normCmd(node)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeConfig, err := buildNodeConfig(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
nodeConfig.Name = fmt.Sprintf("%snode-0", namePrefix)
|
|
||||||
|
|
||||||
var nodes []*config.NodeConfig
|
|
||||||
for _, host := range strings.Split(nodeConfig.Addr, ",") {
|
|
||||||
if host == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
nodeCfg := &config.NodeConfig{}
|
|
||||||
*nodeCfg = *nodeConfig
|
|
||||||
nodeCfg.Name = fmt.Sprintf("%snode-%d", namePrefix, len(nodes))
|
|
||||||
nodeCfg.Addr = host
|
|
||||||
nodes = append(nodes, nodeCfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
mc := nodeConfig.Connector.Metadata
|
|
||||||
md := mdx.NewMetadata(mc)
|
|
||||||
|
|
||||||
hopConfig := &config.HopConfig{
|
|
||||||
Name: fmt.Sprintf("%shop-%d", namePrefix, i),
|
|
||||||
Selector: parseSelector(mc),
|
|
||||||
Nodes: nodes,
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := mdutil.GetString(md, "bypass"); v != "" {
|
|
||||||
bypassCfg := &config.BypassConfig{
|
|
||||||
Name: fmt.Sprintf("%sbypass-%d", namePrefix, len(cfg.Bypasses)),
|
|
||||||
}
|
|
||||||
if v[0] == '~' {
|
|
||||||
bypassCfg.Whitelist = true
|
|
||||||
v = v[1:]
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
if s == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
bypassCfg.Matchers = append(bypassCfg.Matchers, s)
|
|
||||||
}
|
|
||||||
hopConfig.Bypass = bypassCfg.Name
|
|
||||||
cfg.Bypasses = append(cfg.Bypasses, bypassCfg)
|
|
||||||
delete(mc, "bypass")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "resolver"); v != "" {
|
|
||||||
resolverCfg := &config.ResolverConfig{
|
|
||||||
Name: fmt.Sprintf("%sresolver-%d", namePrefix, len(cfg.Resolvers)),
|
|
||||||
}
|
|
||||||
for _, rs := range strings.Split(v, ",") {
|
|
||||||
if rs == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
resolverCfg.Nameservers = append(
|
|
||||||
resolverCfg.Nameservers,
|
|
||||||
&config.NameserverConfig{
|
|
||||||
Addr: rs,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
hopConfig.Resolver = resolverCfg.Name
|
|
||||||
cfg.Resolvers = append(cfg.Resolvers, resolverCfg)
|
|
||||||
delete(mc, "resolver")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "hosts"); v != "" {
|
|
||||||
hostsCfg := &config.HostsConfig{
|
|
||||||
Name: fmt.Sprintf("%shosts-%d", namePrefix, len(cfg.Hosts)),
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
ss := strings.SplitN(s, ":", 2)
|
|
||||||
if len(ss) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hostsCfg.Mappings = append(
|
|
||||||
hostsCfg.Mappings,
|
|
||||||
&config.HostMappingConfig{
|
|
||||||
Hostname: ss[0],
|
|
||||||
IP: ss[1],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
hopConfig.Hosts = hostsCfg.Name
|
|
||||||
cfg.Hosts = append(cfg.Hosts, hostsCfg)
|
|
||||||
delete(mc, "hosts")
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := mdutil.GetString(md, "interface"); v != "" {
|
|
||||||
hopConfig.Interface = v
|
|
||||||
delete(mc, "interface")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetInt(md, "so_mark"); v > 0 {
|
|
||||||
hopConfig.SockOpts = &config.SockOptsConfig{
|
|
||||||
Mark: v,
|
|
||||||
}
|
|
||||||
delete(mc, "so_mark")
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.Hops = append(chain.Hops, hopConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, svc := range services {
|
|
||||||
url, err := normCmd(svc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
service, err := buildServiceConfig(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
service.Name = fmt.Sprintf("%sservice-%d", namePrefix, i)
|
|
||||||
if chain != nil {
|
|
||||||
if service.Listener.Type == "rtcp" || service.Listener.Type == "rudp" {
|
|
||||||
service.Listener.Chain = chain.Name
|
|
||||||
} else {
|
|
||||||
service.Handler.Chain = chain.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg.Services = append(cfg.Services, service)
|
|
||||||
|
|
||||||
mh := service.Handler.Metadata
|
|
||||||
md := mdx.NewMetadata(mh)
|
|
||||||
if v := mdutil.GetInt(md, "retries"); v > 0 {
|
|
||||||
service.Handler.Retries = v
|
|
||||||
delete(mh, "retries")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "admission"); v != "" {
|
|
||||||
admCfg := &config.AdmissionConfig{
|
|
||||||
Name: fmt.Sprintf("%sadmission-%d", namePrefix, len(cfg.Admissions)),
|
|
||||||
}
|
|
||||||
if v[0] == '~' {
|
|
||||||
admCfg.Whitelist = true
|
|
||||||
v = v[1:]
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
if s == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
admCfg.Matchers = append(admCfg.Matchers, s)
|
|
||||||
}
|
|
||||||
service.Admission = admCfg.Name
|
|
||||||
cfg.Admissions = append(cfg.Admissions, admCfg)
|
|
||||||
delete(mh, "admission")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "bypass"); v != "" {
|
|
||||||
bypassCfg := &config.BypassConfig{
|
|
||||||
Name: fmt.Sprintf("%sbypass-%d", namePrefix, len(cfg.Bypasses)),
|
|
||||||
}
|
|
||||||
if v[0] == '~' {
|
|
||||||
bypassCfg.Whitelist = true
|
|
||||||
v = v[1:]
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
if s == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
bypassCfg.Matchers = append(bypassCfg.Matchers, s)
|
|
||||||
}
|
|
||||||
service.Bypass = bypassCfg.Name
|
|
||||||
cfg.Bypasses = append(cfg.Bypasses, bypassCfg)
|
|
||||||
delete(mh, "bypass")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "resolver"); v != "" {
|
|
||||||
resolverCfg := &config.ResolverConfig{
|
|
||||||
Name: fmt.Sprintf("%sresolver-%d", namePrefix, len(cfg.Resolvers)),
|
|
||||||
}
|
|
||||||
for _, rs := range strings.Split(v, ",") {
|
|
||||||
if rs == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
resolverCfg.Nameservers = append(
|
|
||||||
resolverCfg.Nameservers,
|
|
||||||
&config.NameserverConfig{
|
|
||||||
Addr: rs,
|
|
||||||
Prefer: mdutil.GetString(md, "prefer"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
service.Resolver = resolverCfg.Name
|
|
||||||
cfg.Resolvers = append(cfg.Resolvers, resolverCfg)
|
|
||||||
delete(mh, "resolver")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "hosts"); v != "" {
|
|
||||||
hostsCfg := &config.HostsConfig{
|
|
||||||
Name: fmt.Sprintf("%shosts-%d", namePrefix, len(cfg.Hosts)),
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
ss := strings.SplitN(s, ":", 2)
|
|
||||||
if len(ss) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hostsCfg.Mappings = append(
|
|
||||||
hostsCfg.Mappings,
|
|
||||||
&config.HostMappingConfig{
|
|
||||||
Hostname: ss[0],
|
|
||||||
IP: ss[1],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
service.Hosts = hostsCfg.Name
|
|
||||||
cfg.Hosts = append(cfg.Hosts, hostsCfg)
|
|
||||||
delete(mh, "hosts")
|
|
||||||
}
|
|
||||||
|
|
||||||
in := mdutil.GetString(md, "limiter.in")
|
|
||||||
out := mdutil.GetString(md, "limiter.out")
|
|
||||||
cin := mdutil.GetString(md, "limiter.conn.in")
|
|
||||||
cout := mdutil.GetString(md, "limiter.conn.out")
|
|
||||||
if in != "" || cin != "" || out != "" || cout != "" {
|
|
||||||
limiter := &config.LimiterConfig{
|
|
||||||
Name: fmt.Sprintf("%slimiter-%d", namePrefix, len(cfg.Limiters)),
|
|
||||||
}
|
|
||||||
if in != "" || out != "" {
|
|
||||||
limiter.Limits = append(limiter.Limits,
|
|
||||||
fmt.Sprintf("%s %s %s", traffic.GlobalLimitKey, in, out))
|
|
||||||
}
|
|
||||||
if cin != "" || cout != "" {
|
|
||||||
limiter.Limits = append(limiter.Limits,
|
|
||||||
fmt.Sprintf("%s %s %s", traffic.ConnLimitKey, cin, cout))
|
|
||||||
}
|
|
||||||
service.Limiter = limiter.Name
|
|
||||||
cfg.Limiters = append(cfg.Limiters, limiter)
|
|
||||||
delete(mh, "limiter.in")
|
|
||||||
delete(mh, "limiter.out")
|
|
||||||
delete(mh, "limiter.conn.in")
|
|
||||||
delete(mh, "limiter.conn.out")
|
|
||||||
}
|
|
||||||
|
|
||||||
if climit := mdutil.GetInt(md, "climiter"); climit > 0 {
|
|
||||||
limiter := &config.LimiterConfig{
|
|
||||||
Name: fmt.Sprintf("%sclimiter-%d", namePrefix, len(cfg.CLimiters)),
|
|
||||||
Limits: []string{fmt.Sprintf("%s %d", conn.GlobalLimitKey, climit)},
|
|
||||||
}
|
|
||||||
service.CLimiter = limiter.Name
|
|
||||||
cfg.CLimiters = append(cfg.CLimiters, limiter)
|
|
||||||
delete(mh, "climiter")
|
|
||||||
}
|
|
||||||
|
|
||||||
if rlimit := mdutil.GetFloat(md, "rlimiter"); rlimit > 0 {
|
|
||||||
limiter := &config.LimiterConfig{
|
|
||||||
Name: fmt.Sprintf("%srlimiter-%d", namePrefix, len(cfg.RLimiters)),
|
|
||||||
Limits: []string{fmt.Sprintf("%s %s", conn.GlobalLimitKey, strconv.FormatFloat(rlimit, 'f', -1, 64))},
|
|
||||||
}
|
|
||||||
service.RLimiter = limiter.Name
|
|
||||||
cfg.RLimiters = append(cfg.RLimiters, limiter)
|
|
||||||
delete(mh, "rlimiter")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildServiceConfig(url *url.URL) (*config.ServiceConfig, error) {
|
|
||||||
namePrefix := ""
|
|
||||||
if v := os.Getenv("_GOST_ID"); v != "" {
|
|
||||||
namePrefix = fmt.Sprintf("go-%s@", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler, listener string
|
|
||||||
schemes := strings.Split(url.Scheme, "+")
|
|
||||||
if len(schemes) == 1 {
|
|
||||||
handler = schemes[0]
|
|
||||||
listener = schemes[0]
|
|
||||||
}
|
|
||||||
if len(schemes) == 2 {
|
|
||||||
handler = schemes[0]
|
|
||||||
listener = schemes[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
svc := &config.ServiceConfig{
|
|
||||||
Addr: url.Host,
|
|
||||||
}
|
|
||||||
|
|
||||||
if h := registry.HandlerRegistry().Get(handler); h == nil {
|
|
||||||
handler = "auto"
|
|
||||||
}
|
|
||||||
if ln := registry.ListenerRegistry().Get(listener); ln == nil {
|
|
||||||
listener = "tcp"
|
|
||||||
if handler == "ssu" {
|
|
||||||
listener = "udp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// forward mode
|
|
||||||
if remotes := strings.Trim(url.EscapedPath(), "/"); remotes != "" {
|
|
||||||
svc.Forwarder = &config.ForwarderConfig{
|
|
||||||
// Targets: strings.Split(remotes, ","),
|
|
||||||
}
|
|
||||||
for i, addr := range strings.Split(remotes, ",") {
|
|
||||||
svc.Forwarder.Nodes = append(svc.Forwarder.Nodes,
|
|
||||||
&config.ForwardNodeConfig{
|
|
||||||
Name: fmt.Sprintf("%starget-%d", namePrefix, i),
|
|
||||||
Addr: addr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if handler != "relay" {
|
|
||||||
if listener == "tcp" || listener == "udp" ||
|
|
||||||
listener == "rtcp" || listener == "rudp" ||
|
|
||||||
listener == "tun" || listener == "tap" ||
|
|
||||||
listener == "dns" {
|
|
||||||
handler = listener
|
|
||||||
} else {
|
|
||||||
handler = "forward"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var auth *config.AuthConfig
|
|
||||||
if url.User != nil {
|
|
||||||
auth = &config.AuthConfig{
|
|
||||||
Username: url.User.Username(),
|
|
||||||
}
|
|
||||||
auth.Password, _ = url.User.Password()
|
|
||||||
}
|
|
||||||
|
|
||||||
m := map[string]any{}
|
|
||||||
for k, v := range url.Query() {
|
|
||||||
if len(v) > 0 {
|
|
||||||
m[k] = v[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
md := mdx.NewMetadata(m)
|
|
||||||
|
|
||||||
if sa := mdutil.GetString(md, "auth"); sa != "" {
|
|
||||||
au, err := parseAuthFromCmd(sa)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
auth = au
|
|
||||||
}
|
|
||||||
delete(m, "auth")
|
|
||||||
|
|
||||||
tlsConfig := &config.TLSConfig{
|
|
||||||
CertFile: mdutil.GetString(md, "certFile", "cert"),
|
|
||||||
KeyFile: mdutil.GetString(md, "keyFile", "key"),
|
|
||||||
CAFile: mdutil.GetString(md, "caFile", "ca"),
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(m, "certFile")
|
|
||||||
delete(m, "cert")
|
|
||||||
delete(m, "keyFile")
|
|
||||||
delete(m, "key")
|
|
||||||
delete(m, "caFile")
|
|
||||||
delete(m, "ca")
|
|
||||||
|
|
||||||
if tlsConfig.CertFile == "" {
|
|
||||||
tlsConfig = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := mdutil.GetString(md, "dns"); v != "" {
|
|
||||||
md.Set("dns", strings.Split(v, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
if svc.Forwarder != nil {
|
|
||||||
svc.Forwarder.Selector = parseSelector(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
svc.Handler = &config.HandlerConfig{
|
|
||||||
Type: handler,
|
|
||||||
Auth: auth,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
svc.Listener = &config.ListenerConfig{
|
|
||||||
Type: listener,
|
|
||||||
TLS: tlsConfig,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
|
|
||||||
svc.Metadata = m
|
|
||||||
|
|
||||||
if svc.Listener.Type == "ssh" || svc.Listener.Type == "sshd" {
|
|
||||||
svc.Handler.Auth = nil
|
|
||||||
svc.Listener.Auth = auth
|
|
||||||
}
|
|
||||||
|
|
||||||
return svc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
|
|
||||||
var connector, dialer string
|
|
||||||
schemes := strings.Split(url.Scheme, "+")
|
|
||||||
if len(schemes) == 1 {
|
|
||||||
connector = schemes[0]
|
|
||||||
dialer = schemes[0]
|
|
||||||
}
|
|
||||||
if len(schemes) == 2 {
|
|
||||||
connector = schemes[0]
|
|
||||||
dialer = schemes[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
node := &config.NodeConfig{
|
|
||||||
Addr: url.Host,
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := registry.ConnectorRegistry().Get(connector); c == nil {
|
|
||||||
connector = "http"
|
|
||||||
}
|
|
||||||
if d := registry.DialerRegistry().Get(dialer); d == nil {
|
|
||||||
dialer = "tcp"
|
|
||||||
if connector == "ssu" {
|
|
||||||
dialer = "udp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var auth *config.AuthConfig
|
|
||||||
if url.User != nil {
|
|
||||||
auth = &config.AuthConfig{
|
|
||||||
Username: url.User.Username(),
|
|
||||||
}
|
|
||||||
auth.Password, _ = url.User.Password()
|
|
||||||
}
|
|
||||||
|
|
||||||
m := map[string]any{}
|
|
||||||
for k, v := range url.Query() {
|
|
||||||
if len(v) > 0 {
|
|
||||||
m[k] = v[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
md := mdx.NewMetadata(m)
|
|
||||||
|
|
||||||
if sauth := mdutil.GetString(md, "auth"); sauth != "" && auth == nil {
|
|
||||||
au, err := parseAuthFromCmd(sauth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
auth = au
|
|
||||||
}
|
|
||||||
delete(m, "auth")
|
|
||||||
|
|
||||||
tlsConfig := &config.TLSConfig{
|
|
||||||
CertFile: mdutil.GetString(md, "certFile", "cert"),
|
|
||||||
KeyFile: mdutil.GetString(md, "keyFile", "key"),
|
|
||||||
CAFile: mdutil.GetString(md, "caFile", "ca"),
|
|
||||||
Secure: mdutil.GetBool(md, "secure"),
|
|
||||||
ServerName: mdutil.GetString(md, "serverName"),
|
|
||||||
}
|
|
||||||
if tlsConfig.ServerName == "" {
|
|
||||||
tlsConfig.ServerName = url.Hostname()
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(m, "certFile")
|
|
||||||
delete(m, "cert")
|
|
||||||
delete(m, "keyFile")
|
|
||||||
delete(m, "key")
|
|
||||||
delete(m, "caFile")
|
|
||||||
delete(m, "ca")
|
|
||||||
delete(m, "secure")
|
|
||||||
delete(m, "serverName")
|
|
||||||
|
|
||||||
if !tlsConfig.Secure && tlsConfig.CertFile == "" && tlsConfig.CAFile == "" {
|
|
||||||
tlsConfig = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
node.Connector = &config.ConnectorConfig{
|
|
||||||
Type: connector,
|
|
||||||
Auth: auth,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
node.Dialer = &config.DialerConfig{
|
|
||||||
Type: dialer,
|
|
||||||
TLS: tlsConfig,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.Dialer.Type == "ssh" || node.Dialer.Type == "sshd" {
|
|
||||||
node.Connector.Auth = nil
|
|
||||||
node.Dialer.Auth = auth
|
|
||||||
}
|
|
||||||
|
|
||||||
return node, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func normCmd(s string) (*url.URL, error) {
|
|
||||||
s = strings.TrimSpace(s)
|
|
||||||
if s == "" {
|
|
||||||
return nil, ErrInvalidCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
if s[0] == ':' || !strings.Contains(s, "://") {
|
|
||||||
s = "auto://" + s
|
|
||||||
}
|
|
||||||
|
|
||||||
url, err := url.Parse(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if url.Scheme == "https" {
|
|
||||||
url.Scheme = "http+tls"
|
|
||||||
}
|
|
||||||
|
|
||||||
return url, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAuthFromCmd(sa string) (*config.AuthConfig, error) {
|
|
||||||
v, err := base64.StdEncoding.DecodeString(sa)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cs := string(v)
|
|
||||||
n := strings.IndexByte(cs, ':')
|
|
||||||
if n < 0 {
|
|
||||||
return &config.AuthConfig{
|
|
||||||
Username: cs,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &config.AuthConfig{
|
|
||||||
Username: cs[:n],
|
|
||||||
Password: cs[n+1:],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSelector(m map[string]any) *config.SelectorConfig {
|
|
||||||
md := mdx.NewMetadata(m)
|
|
||||||
strategy := mdutil.GetString(md, "strategy")
|
|
||||||
maxFails := mdutil.GetInt(md, "maxFails", "max_fails")
|
|
||||||
failTimeout := mdutil.GetDuration(md, "failTimeout", "fail_timeout")
|
|
||||||
if strategy == "" && maxFails <= 0 && failTimeout <= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strategy == "" {
|
|
||||||
strategy = "round"
|
|
||||||
}
|
|
||||||
if maxFails <= 0 {
|
|
||||||
maxFails = 1
|
|
||||||
}
|
|
||||||
if failTimeout <= 0 {
|
|
||||||
failTimeout = 30 * time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(m, "strategy")
|
|
||||||
delete(m, "maxFails")
|
|
||||||
delete(m, "max_fails")
|
|
||||||
delete(m, "failTimeout")
|
|
||||||
delete(m, "fail_timeout")
|
|
||||||
|
|
||||||
return &config.SelectorConfig{
|
|
||||||
Strategy: strategy,
|
|
||||||
MaxFails: maxFails,
|
|
||||||
FailTimeout: failTimeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,204 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
"github.com/go-gost/core/service"
|
|
||||||
"github.com/go-gost/x/api"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
"github.com/go-gost/x/config/parsing"
|
|
||||||
xlogger "github.com/go-gost/x/logger"
|
|
||||||
metrics "github.com/go-gost/x/metrics/service"
|
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildService(cfg *config.Config) (services []service.Service) {
|
|
||||||
if cfg == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, autherCfg := range cfg.Authers {
|
|
||||||
if auther := parsing.ParseAuther(autherCfg); auther != nil {
|
|
||||||
if err := registry.AutherRegistry().Register(autherCfg.Name, auther); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, admissionCfg := range cfg.Admissions {
|
|
||||||
if adm := parsing.ParseAdmission(admissionCfg); adm != nil {
|
|
||||||
if err := registry.AdmissionRegistry().Register(admissionCfg.Name, adm); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, bypassCfg := range cfg.Bypasses {
|
|
||||||
if bp := parsing.ParseBypass(bypassCfg); bp != nil {
|
|
||||||
if err := registry.BypassRegistry().Register(bypassCfg.Name, bp); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, resolverCfg := range cfg.Resolvers {
|
|
||||||
r, err := parsing.ParseResolver(resolverCfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if r != nil {
|
|
||||||
if err := registry.ResolverRegistry().Register(resolverCfg.Name, r); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, hostsCfg := range cfg.Hosts {
|
|
||||||
if h := parsing.ParseHosts(hostsCfg); h != nil {
|
|
||||||
if err := registry.HostsRegistry().Register(hostsCfg.Name, h); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ingressCfg := range cfg.Ingresses {
|
|
||||||
if h := parsing.ParseIngress(ingressCfg); h != nil {
|
|
||||||
if err := registry.IngressRegistry().Register(ingressCfg.Name, h); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, recorderCfg := range cfg.Recorders {
|
|
||||||
if h := parsing.ParseRecorder(recorderCfg); h != nil {
|
|
||||||
if err := registry.RecorderRegistry().Register(recorderCfg.Name, h); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, limiterCfg := range cfg.Limiters {
|
|
||||||
if h := parsing.ParseTrafficLimiter(limiterCfg); h != nil {
|
|
||||||
if err := registry.TrafficLimiterRegistry().Register(limiterCfg.Name, h); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, limiterCfg := range cfg.CLimiters {
|
|
||||||
if h := parsing.ParseConnLimiter(limiterCfg); h != nil {
|
|
||||||
if err := registry.ConnLimiterRegistry().Register(limiterCfg.Name, h); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, limiterCfg := range cfg.RLimiters {
|
|
||||||
if h := parsing.ParseRateLimiter(limiterCfg); h != nil {
|
|
||||||
if err := registry.RateLimiterRegistry().Register(limiterCfg.Name, h); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, hopCfg := range cfg.Hops {
|
|
||||||
hop, err := parsing.ParseHop(hopCfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if hop != nil {
|
|
||||||
if err := registry.HopRegistry().Register(hopCfg.Name, hop); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, chainCfg := range cfg.Chains {
|
|
||||||
c, err := parsing.ParseChain(chainCfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if c != nil {
|
|
||||||
if err := registry.ChainRegistry().Register(chainCfg.Name, c); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, svcCfg := range cfg.Services {
|
|
||||||
svc, err := parsing.ParseService(svcCfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if svc != nil {
|
|
||||||
if err := registry.ServiceRegistry().Register(svcCfg.Name, svc); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
services = append(services, svc)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func logFromConfig(cfg *config.LogConfig) logger.Logger {
|
|
||||||
if cfg == nil {
|
|
||||||
cfg = &config.LogConfig{}
|
|
||||||
}
|
|
||||||
opts := []xlogger.LoggerOption{
|
|
||||||
xlogger.FormatLoggerOption(logger.LogFormat(cfg.Format)),
|
|
||||||
xlogger.LevelLoggerOption(logger.LogLevel(cfg.Level)),
|
|
||||||
}
|
|
||||||
|
|
||||||
var out io.Writer = os.Stderr
|
|
||||||
switch cfg.Output {
|
|
||||||
case "none", "null":
|
|
||||||
return xlogger.Nop()
|
|
||||||
case "stdout":
|
|
||||||
out = os.Stdout
|
|
||||||
case "stderr", "":
|
|
||||||
out = os.Stderr
|
|
||||||
default:
|
|
||||||
if cfg.Rotation != nil {
|
|
||||||
out = &lumberjack.Logger{
|
|
||||||
Filename: cfg.Output,
|
|
||||||
MaxSize: cfg.Rotation.MaxSize,
|
|
||||||
MaxAge: cfg.Rotation.MaxAge,
|
|
||||||
MaxBackups: cfg.Rotation.MaxBackups,
|
|
||||||
LocalTime: cfg.Rotation.LocalTime,
|
|
||||||
Compress: cfg.Rotation.Compress,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
os.MkdirAll(filepath.Dir(cfg.Output), 0755)
|
|
||||||
f, err := os.OpenFile(cfg.Output, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err)
|
|
||||||
} else {
|
|
||||||
out = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opts = append(opts, xlogger.OutputLoggerOption(out))
|
|
||||||
|
|
||||||
return xlogger.NewLogger(opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildAPIService(cfg *config.APIConfig) (service.Service, error) {
|
|
||||||
auther := parsing.ParseAutherFromAuth(cfg.Auth)
|
|
||||||
if cfg.Auther != "" {
|
|
||||||
auther = registry.AutherRegistry().Get(cfg.Auther)
|
|
||||||
}
|
|
||||||
return api.NewService(
|
|
||||||
cfg.Addr,
|
|
||||||
api.PathPrefixOption(cfg.PathPrefix),
|
|
||||||
api.AccessLogOption(cfg.AccessLog),
|
|
||||||
api.AutherOption(auther),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildMetricsService(cfg *config.MetricsConfig) (service.Service, error) {
|
|
||||||
return metrics.NewService(
|
|
||||||
cfg.Addr,
|
|
||||||
metrics.PathOption(cfg.Path),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
216
cmd/gost/main.go
216
cmd/gost/main.go
|
|
@ -4,37 +4,43 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"log"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
"github.com/go-gost/x/config/parsing"
|
|
||||||
xlogger "github.com/go-gost/x/logger"
|
xlogger "github.com/go-gost/x/logger"
|
||||||
xmetrics "github.com/go-gost/x/metrics"
|
"github.com/judwhite/go-svc"
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type stringList []string
|
||||||
log logger.Logger
|
|
||||||
|
|
||||||
|
func (l *stringList) String() string {
|
||||||
|
return fmt.Sprintf("%s", *l)
|
||||||
|
}
|
||||||
|
func (l *stringList) Set(value string) error {
|
||||||
|
*l = append(*l, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
cfgFile string
|
cfgFile string
|
||||||
outputFormat string
|
outputFormat string
|
||||||
services stringList
|
services stringList
|
||||||
nodes stringList
|
nodes stringList
|
||||||
debug bool
|
debug bool
|
||||||
|
trace bool
|
||||||
apiAddr string
|
apiAddr string
|
||||||
metricsAddr string
|
metricsAddr string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds)
|
||||||
|
|
||||||
args := strings.Join(os.Args[1:], " ")
|
args := strings.Join(os.Args[1:], " ")
|
||||||
|
|
||||||
if strings.Contains(args, " -- ") {
|
if strings.Contains(args, " -- ") {
|
||||||
|
|
@ -68,7 +74,9 @@ func worker(id int, args []string, ctx *context.Context, ret *int) {
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Env = append(os.Environ(), fmt.Sprintf("_GOST_ID=%d", id))
|
cmd.Env = append(os.Environ(), fmt.Sprintf("_GOST_ID=%d", id))
|
||||||
|
|
||||||
cmd.Run()
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
if cmd.ProcessState.Exited() {
|
if cmd.ProcessState.Exited() {
|
||||||
*ret = cmd.ProcessState.ExitCode()
|
*ret = cmd.ProcessState.ExitCode()
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +91,7 @@ func init() {
|
||||||
flag.BoolVar(&printVersion, "V", false, "print version")
|
flag.BoolVar(&printVersion, "V", false, "print version")
|
||||||
flag.StringVar(&outputFormat, "O", "", "output format, one of yaml|json format")
|
flag.StringVar(&outputFormat, "O", "", "output format, one of yaml|json format")
|
||||||
flag.BoolVar(&debug, "D", false, "debug mode")
|
flag.BoolVar(&debug, "D", false, "debug mode")
|
||||||
|
flag.BoolVar(&trace, "DD", false, "trace mode")
|
||||||
flag.StringVar(&apiAddr, "api", "", "api service address")
|
flag.StringVar(&apiAddr, "api", "", "api service address")
|
||||||
flag.StringVar(&metricsAddr, "metrics", "", "metrics service address")
|
flag.StringVar(&metricsAddr, "metrics", "", "metrics service address")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
@ -92,194 +101,15 @@ func init() {
|
||||||
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
log = xlogger.NewLogger()
|
|
||||||
logger.SetDefault(log)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cfg := &config.Config{}
|
log := xlogger.NewLogger()
|
||||||
if cfgFile != "" {
|
|
||||||
if err := cfg.ReadFile(cfgFile); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdCfg, err := buildConfigFromCmd(services, nodes)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
cfg = mergeConfig(cfg, cmdCfg)
|
|
||||||
|
|
||||||
if len(cfg.Services) == 0 && apiAddr == "" {
|
|
||||||
if err := cfg.Load(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := os.Getenv("GOST_PROFILING"); v != "" {
|
|
||||||
cfg.Profiling = &config.ProfilingConfig{
|
|
||||||
Addr: v,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if v := os.Getenv("GOST_METRICS"); v != "" {
|
|
||||||
cfg.Metrics = &config.MetricsConfig{
|
|
||||||
Addr: v,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := os.Getenv("GOST_LOGGER_LEVEL"); v != "" {
|
|
||||||
cfg.Log = &config.LogConfig{
|
|
||||||
Level: v,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := os.Getenv("GOST_API"); v != "" {
|
|
||||||
cfg.API = &config.APIConfig{
|
|
||||||
Addr: v,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if debug {
|
|
||||||
if cfg.Log == nil {
|
|
||||||
cfg.Log = &config.LogConfig{}
|
|
||||||
}
|
|
||||||
cfg.Log.Level = string(logger.DebugLevel)
|
|
||||||
}
|
|
||||||
if apiAddr != "" {
|
|
||||||
cfg.API = &config.APIConfig{
|
|
||||||
Addr: apiAddr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if metricsAddr != "" {
|
|
||||||
cfg.Metrics = &config.MetricsConfig{
|
|
||||||
Addr: metricsAddr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log = logFromConfig(cfg.Log)
|
|
||||||
|
|
||||||
logger.SetDefault(log)
|
logger.SetDefault(log)
|
||||||
|
|
||||||
if outputFormat != "" {
|
p := &program{}
|
||||||
if err := cfg.Write(os.Stdout, outputFormat); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Profiling != nil {
|
if err := svc.Run(p); err != nil {
|
||||||
go func() {
|
logger.Default().Fatal(err)
|
||||||
addr := cfg.Profiling.Addr
|
|
||||||
if addr == "" {
|
|
||||||
addr = ":6060"
|
|
||||||
}
|
|
||||||
log.Info("profiling server on ", addr)
|
|
||||||
log.Fatal(http.ListenAndServe(addr, nil))
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.API != nil {
|
|
||||||
s, err := buildAPIService(cfg.API)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer s.Close()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
log.Info("api service on ", s.Addr())
|
|
||||||
log.Fatal(s.Serve())
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Metrics != nil {
|
|
||||||
xmetrics.Init(xmetrics.NewMetrics())
|
|
||||||
if cfg.Metrics.Addr != "" {
|
|
||||||
s, err := buildMetricsService(cfg.Metrics)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
defer s.Close()
|
|
||||||
log.Info("metrics service on ", s.Addr())
|
|
||||||
log.Fatal(s.Serve())
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parsing.BuildDefaultTLSConfig(cfg.TLS)
|
|
||||||
|
|
||||||
services := buildService(cfg)
|
|
||||||
for _, svc := range services {
|
|
||||||
svc := svc
|
|
||||||
go func() {
|
|
||||||
svc.Serve()
|
|
||||||
// svc.Close()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
config.SetGlobal(cfg)
|
|
||||||
|
|
||||||
sigs := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
|
|
||||||
|
|
||||||
for sig := range sigs {
|
|
||||||
switch sig {
|
|
||||||
case syscall.SIGHUP:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
for name, srv := range registry.ServiceRegistry().GetAll() {
|
|
||||||
srv.Close()
|
|
||||||
log.Debugf("service %s shutdown", name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeConfig(cfg1, cfg2 *config.Config) *config.Config {
|
|
||||||
if cfg1 == nil {
|
|
||||||
return cfg2
|
|
||||||
}
|
|
||||||
if cfg2 == nil {
|
|
||||||
return cfg1
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := &config.Config{
|
|
||||||
Services: append(cfg1.Services, cfg2.Services...),
|
|
||||||
Chains: append(cfg1.Chains, cfg2.Chains...),
|
|
||||||
Hops: append(cfg1.Hops, cfg2.Hops...),
|
|
||||||
Authers: append(cfg1.Authers, cfg2.Authers...),
|
|
||||||
Admissions: append(cfg1.Admissions, cfg2.Admissions...),
|
|
||||||
Bypasses: append(cfg1.Bypasses, cfg2.Bypasses...),
|
|
||||||
Resolvers: append(cfg1.Resolvers, cfg2.Resolvers...),
|
|
||||||
Hosts: append(cfg1.Hosts, cfg2.Hosts...),
|
|
||||||
Ingresses: append(cfg1.Ingresses, cfg2.Ingresses...),
|
|
||||||
Recorders: append(cfg1.Recorders, cfg2.Recorders...),
|
|
||||||
Limiters: append(cfg1.Limiters, cfg2.Limiters...),
|
|
||||||
CLimiters: append(cfg1.CLimiters, cfg2.CLimiters...),
|
|
||||||
RLimiters: append(cfg1.RLimiters, cfg2.RLimiters...),
|
|
||||||
TLS: cfg1.TLS,
|
|
||||||
Log: cfg1.Log,
|
|
||||||
API: cfg1.API,
|
|
||||||
Metrics: cfg1.Metrics,
|
|
||||||
Profiling: cfg1.Profiling,
|
|
||||||
}
|
|
||||||
if cfg2.TLS != nil {
|
|
||||||
cfg.TLS = cfg2.TLS
|
|
||||||
}
|
|
||||||
if cfg2.Log != nil {
|
|
||||||
cfg.Log = cfg2.Log
|
|
||||||
}
|
|
||||||
if cfg2.API != nil {
|
|
||||||
cfg.API = cfg2.API
|
|
||||||
}
|
|
||||||
if cfg2.Metrics != nil {
|
|
||||||
cfg.Metrics = cfg2.Metrics
|
|
||||||
}
|
|
||||||
if cfg2.Profiling != nil {
|
|
||||||
cfg.Profiling = cfg2.Profiling
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,274 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/auth"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/core/service"
|
||||||
|
api_service "github.com/go-gost/x/api/service"
|
||||||
|
xauth "github.com/go-gost/x/auth"
|
||||||
|
"github.com/go-gost/x/config"
|
||||||
|
"github.com/go-gost/x/config/loader"
|
||||||
|
auth_parser "github.com/go-gost/x/config/parsing/auth"
|
||||||
|
"github.com/go-gost/x/config/parsing/parser"
|
||||||
|
xmetrics "github.com/go-gost/x/metrics"
|
||||||
|
metrics "github.com/go-gost/x/metrics/service"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
"github.com/judwhite/go-svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type program struct {
|
||||||
|
srvApi service.Service
|
||||||
|
srvMetrics service.Service
|
||||||
|
srvProfiling *http.Server
|
||||||
|
|
||||||
|
cancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) Init(env svc.Environment) error {
|
||||||
|
parser.Init(parser.Args{
|
||||||
|
CfgFile: cfgFile,
|
||||||
|
Services: services,
|
||||||
|
Nodes: nodes,
|
||||||
|
Debug: debug,
|
||||||
|
Trace: trace,
|
||||||
|
ApiAddr: apiAddr,
|
||||||
|
MetricsAddr: metricsAddr,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) Start() error {
|
||||||
|
cfg, err := parser.Parse()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if outputFormat != "" {
|
||||||
|
if err := cfg.Write(os.Stdout, outputFormat); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Set(cfg)
|
||||||
|
|
||||||
|
if err := loader.Load(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.run(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
p.cancel = cancel
|
||||||
|
go p.reload(ctx)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) run(cfg *config.Config) error {
|
||||||
|
for _, svc := range registry.ServiceRegistry().GetAll() {
|
||||||
|
svc := svc
|
||||||
|
go func() {
|
||||||
|
svc.Serve()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.srvApi != nil {
|
||||||
|
p.srvApi.Close()
|
||||||
|
p.srvApi = nil
|
||||||
|
}
|
||||||
|
if cfg.API != nil {
|
||||||
|
s, err := buildApiService(cfg.API)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.srvApi = s
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
log := logger.Default().WithFields(map[string]any{"kind": "service", "service": "@api"})
|
||||||
|
|
||||||
|
log.Info("listening on ", s.Addr())
|
||||||
|
if err := s.Serve(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.srvMetrics != nil {
|
||||||
|
p.srvMetrics.Close()
|
||||||
|
p.srvMetrics = nil
|
||||||
|
}
|
||||||
|
if cfg.Metrics != nil && cfg.Metrics.Addr != "" {
|
||||||
|
s, err := buildMetricsService(cfg.Metrics)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.srvMetrics = s
|
||||||
|
|
||||||
|
xmetrics.Enable(true)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
log := logger.Default().WithFields(map[string]any{"kind": "service", "service": "@metrics"})
|
||||||
|
|
||||||
|
log.Info("listening on ", s.Addr())
|
||||||
|
if err := s.Serve(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.srvProfiling != nil {
|
||||||
|
p.srvProfiling.Close()
|
||||||
|
p.srvProfiling = nil
|
||||||
|
}
|
||||||
|
if cfg.Profiling != nil {
|
||||||
|
addr := cfg.Profiling.Addr
|
||||||
|
if addr == "" {
|
||||||
|
addr = ":6060"
|
||||||
|
}
|
||||||
|
s := &http.Server{
|
||||||
|
Addr: addr,
|
||||||
|
}
|
||||||
|
p.srvProfiling = s
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
log := logger.Default().WithFields(map[string]any{"kind": "service", "service": "@profiling"})
|
||||||
|
|
||||||
|
log.Info("listening on ", addr)
|
||||||
|
if err := s.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) Stop() error {
|
||||||
|
if p.cancel != nil {
|
||||||
|
p.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, srv := range registry.ServiceRegistry().GetAll() {
|
||||||
|
srv.Close()
|
||||||
|
logger.Default().Debugf("service %s shutdown", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.srvApi != nil {
|
||||||
|
p.srvApi.Close()
|
||||||
|
logger.Default().Debug("service @api shutdown")
|
||||||
|
}
|
||||||
|
if p.srvMetrics != nil {
|
||||||
|
p.srvMetrics.Close()
|
||||||
|
logger.Default().Debug("service @metrics shutdown")
|
||||||
|
}
|
||||||
|
if p.srvProfiling != nil {
|
||||||
|
p.srvProfiling.Close()
|
||||||
|
logger.Default().Debug("service @profiling shutdown")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) reload(ctx context.Context) {
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(c, syscall.SIGHUP)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c:
|
||||||
|
if err := p.reloadConfig(); err != nil {
|
||||||
|
logger.Default().Error(err)
|
||||||
|
} else {
|
||||||
|
logger.Default().Info("config reloaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) reloadConfig() error {
|
||||||
|
cfg, err := parser.Parse()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.Set(cfg)
|
||||||
|
|
||||||
|
if err := loader.Load(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.run(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildApiService(cfg *config.APIConfig) (service.Service, error) {
|
||||||
|
var authers []auth.Authenticator
|
||||||
|
if auther := auth_parser.ParseAutherFromAuth(cfg.Auth); auther != nil {
|
||||||
|
authers = append(authers, auther)
|
||||||
|
}
|
||||||
|
if cfg.Auther != "" {
|
||||||
|
authers = append(authers, registry.AutherRegistry().Get(cfg.Auther))
|
||||||
|
}
|
||||||
|
|
||||||
|
var auther auth.Authenticator
|
||||||
|
if len(authers) > 0 {
|
||||||
|
auther = xauth.AuthenticatorGroup(authers...)
|
||||||
|
}
|
||||||
|
|
||||||
|
network := "tcp"
|
||||||
|
addr := cfg.Addr
|
||||||
|
if strings.HasPrefix(addr, "unix://") {
|
||||||
|
network = "unix"
|
||||||
|
addr = strings.TrimPrefix(addr, "unix://")
|
||||||
|
}
|
||||||
|
return api_service.NewService(
|
||||||
|
network, addr,
|
||||||
|
api_service.PathPrefixOption(cfg.PathPrefix),
|
||||||
|
api_service.AccessLogOption(cfg.AccessLog),
|
||||||
|
api_service.AutherOption(auther),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildMetricsService(cfg *config.MetricsConfig) (service.Service, error) {
|
||||||
|
auther := auth_parser.ParseAutherFromAuth(cfg.Auth)
|
||||||
|
if cfg.Auther != "" {
|
||||||
|
auther = registry.AutherRegistry().Get(cfg.Auther)
|
||||||
|
}
|
||||||
|
|
||||||
|
network := "tcp"
|
||||||
|
addr := cfg.Addr
|
||||||
|
if strings.HasPrefix(addr, "unix://") {
|
||||||
|
network = "unix"
|
||||||
|
addr = strings.TrimPrefix(addr, "unix://")
|
||||||
|
}
|
||||||
|
return metrics.NewService(
|
||||||
|
network, addr,
|
||||||
|
metrics.PathOption(cfg.Path),
|
||||||
|
metrics.AutherOption(auther),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -6,13 +6,20 @@ import (
|
||||||
_ "github.com/go-gost/x/connector/forward"
|
_ "github.com/go-gost/x/connector/forward"
|
||||||
_ "github.com/go-gost/x/connector/http"
|
_ "github.com/go-gost/x/connector/http"
|
||||||
_ "github.com/go-gost/x/connector/http2"
|
_ "github.com/go-gost/x/connector/http2"
|
||||||
|
_ "github.com/go-gost/x/connector/masque"
|
||||||
_ "github.com/go-gost/x/connector/relay"
|
_ "github.com/go-gost/x/connector/relay"
|
||||||
|
_ "github.com/go-gost/x/connector/router"
|
||||||
|
_ "github.com/go-gost/x/connector/serial"
|
||||||
_ "github.com/go-gost/x/connector/sni"
|
_ "github.com/go-gost/x/connector/sni"
|
||||||
_ "github.com/go-gost/x/connector/socks/v4"
|
_ "github.com/go-gost/x/connector/socks/v4"
|
||||||
_ "github.com/go-gost/x/connector/socks/v5"
|
_ "github.com/go-gost/x/connector/socks/v5"
|
||||||
_ "github.com/go-gost/x/connector/ss"
|
_ "github.com/go-gost/x/connector/ss"
|
||||||
_ "github.com/go-gost/x/connector/ss/udp"
|
_ "github.com/go-gost/x/connector/ss/udp"
|
||||||
_ "github.com/go-gost/x/connector/sshd"
|
_ "github.com/go-gost/x/connector/sshd"
|
||||||
|
_ "github.com/go-gost/x/connector/tcp"
|
||||||
|
_ "github.com/go-gost/x/connector/tunnel"
|
||||||
|
_ "github.com/go-gost/x/connector/unix"
|
||||||
|
_ "github.com/go-gost/x/connector/masque"
|
||||||
|
|
||||||
// Register dialers
|
// Register dialers
|
||||||
_ "github.com/go-gost/x/dialer/direct"
|
_ "github.com/go-gost/x/dialer/direct"
|
||||||
|
|
@ -22,32 +29,43 @@ import (
|
||||||
_ "github.com/go-gost/x/dialer/http2"
|
_ "github.com/go-gost/x/dialer/http2"
|
||||||
_ "github.com/go-gost/x/dialer/http2/h2"
|
_ "github.com/go-gost/x/dialer/http2/h2"
|
||||||
_ "github.com/go-gost/x/dialer/http3"
|
_ "github.com/go-gost/x/dialer/http3"
|
||||||
|
_ "github.com/go-gost/x/dialer/http3/masque"
|
||||||
|
_ "github.com/go-gost/x/dialer/http3/wt"
|
||||||
_ "github.com/go-gost/x/dialer/icmp"
|
_ "github.com/go-gost/x/dialer/icmp"
|
||||||
_ "github.com/go-gost/x/dialer/kcp"
|
_ "github.com/go-gost/x/dialer/kcp"
|
||||||
|
_ "github.com/go-gost/x/dialer/mtcp"
|
||||||
_ "github.com/go-gost/x/dialer/mtls"
|
_ "github.com/go-gost/x/dialer/mtls"
|
||||||
_ "github.com/go-gost/x/dialer/mws"
|
_ "github.com/go-gost/x/dialer/mws"
|
||||||
_ "github.com/go-gost/x/dialer/obfs/http"
|
_ "github.com/go-gost/x/dialer/obfs/http"
|
||||||
_ "github.com/go-gost/x/dialer/obfs/tls"
|
_ "github.com/go-gost/x/dialer/obfs/tls"
|
||||||
_ "github.com/go-gost/x/dialer/pht"
|
_ "github.com/go-gost/x/dialer/pht"
|
||||||
_ "github.com/go-gost/x/dialer/quic"
|
_ "github.com/go-gost/x/dialer/quic"
|
||||||
|
_ "github.com/go-gost/x/dialer/serial"
|
||||||
_ "github.com/go-gost/x/dialer/ssh"
|
_ "github.com/go-gost/x/dialer/ssh"
|
||||||
_ "github.com/go-gost/x/dialer/sshd"
|
_ "github.com/go-gost/x/dialer/sshd"
|
||||||
_ "github.com/go-gost/x/dialer/tcp"
|
_ "github.com/go-gost/x/dialer/tcp"
|
||||||
_ "github.com/go-gost/x/dialer/tls"
|
_ "github.com/go-gost/x/dialer/tls"
|
||||||
_ "github.com/go-gost/x/dialer/udp"
|
_ "github.com/go-gost/x/dialer/udp"
|
||||||
|
_ "github.com/go-gost/x/dialer/unix"
|
||||||
_ "github.com/go-gost/x/dialer/ws"
|
_ "github.com/go-gost/x/dialer/ws"
|
||||||
|
|
||||||
// Register handlers
|
// Register handlers
|
||||||
|
_ "github.com/go-gost/x/handler/api"
|
||||||
_ "github.com/go-gost/x/handler/auto"
|
_ "github.com/go-gost/x/handler/auto"
|
||||||
_ "github.com/go-gost/x/handler/dns"
|
_ "github.com/go-gost/x/handler/dns"
|
||||||
|
_ "github.com/go-gost/x/handler/file"
|
||||||
_ "github.com/go-gost/x/handler/forward/local"
|
_ "github.com/go-gost/x/handler/forward/local"
|
||||||
_ "github.com/go-gost/x/handler/forward/remote"
|
_ "github.com/go-gost/x/handler/forward/remote"
|
||||||
_ "github.com/go-gost/x/handler/http"
|
_ "github.com/go-gost/x/handler/http"
|
||||||
_ "github.com/go-gost/x/handler/http2"
|
_ "github.com/go-gost/x/handler/http2"
|
||||||
_ "github.com/go-gost/x/handler/http3"
|
_ "github.com/go-gost/x/handler/http3"
|
||||||
|
_ "github.com/go-gost/x/handler/masque"
|
||||||
|
_ "github.com/go-gost/x/handler/metrics"
|
||||||
_ "github.com/go-gost/x/handler/redirect/tcp"
|
_ "github.com/go-gost/x/handler/redirect/tcp"
|
||||||
_ "github.com/go-gost/x/handler/redirect/udp"
|
_ "github.com/go-gost/x/handler/redirect/udp"
|
||||||
_ "github.com/go-gost/x/handler/relay"
|
_ "github.com/go-gost/x/handler/relay"
|
||||||
|
_ "github.com/go-gost/x/handler/router"
|
||||||
|
_ "github.com/go-gost/x/handler/serial"
|
||||||
_ "github.com/go-gost/x/handler/sni"
|
_ "github.com/go-gost/x/handler/sni"
|
||||||
_ "github.com/go-gost/x/handler/socks/v4"
|
_ "github.com/go-gost/x/handler/socks/v4"
|
||||||
_ "github.com/go-gost/x/handler/socks/v5"
|
_ "github.com/go-gost/x/handler/socks/v5"
|
||||||
|
|
@ -56,6 +74,9 @@ import (
|
||||||
_ "github.com/go-gost/x/handler/sshd"
|
_ "github.com/go-gost/x/handler/sshd"
|
||||||
_ "github.com/go-gost/x/handler/tap"
|
_ "github.com/go-gost/x/handler/tap"
|
||||||
_ "github.com/go-gost/x/handler/tun"
|
_ "github.com/go-gost/x/handler/tun"
|
||||||
|
_ "github.com/go-gost/x/handler/tungo"
|
||||||
|
_ "github.com/go-gost/x/handler/tunnel"
|
||||||
|
_ "github.com/go-gost/x/handler/unix"
|
||||||
|
|
||||||
// Register listeners
|
// Register listeners
|
||||||
_ "github.com/go-gost/x/listener/dns"
|
_ "github.com/go-gost/x/listener/dns"
|
||||||
|
|
@ -66,8 +87,10 @@ import (
|
||||||
_ "github.com/go-gost/x/listener/http2/h2"
|
_ "github.com/go-gost/x/listener/http2/h2"
|
||||||
_ "github.com/go-gost/x/listener/http3"
|
_ "github.com/go-gost/x/listener/http3"
|
||||||
_ "github.com/go-gost/x/listener/http3/h3"
|
_ "github.com/go-gost/x/listener/http3/h3"
|
||||||
|
_ "github.com/go-gost/x/listener/http3/wt"
|
||||||
_ "github.com/go-gost/x/listener/icmp"
|
_ "github.com/go-gost/x/listener/icmp"
|
||||||
_ "github.com/go-gost/x/listener/kcp"
|
_ "github.com/go-gost/x/listener/kcp"
|
||||||
|
_ "github.com/go-gost/x/listener/mtcp"
|
||||||
_ "github.com/go-gost/x/listener/mtls"
|
_ "github.com/go-gost/x/listener/mtls"
|
||||||
_ "github.com/go-gost/x/listener/mws"
|
_ "github.com/go-gost/x/listener/mws"
|
||||||
_ "github.com/go-gost/x/listener/obfs/http"
|
_ "github.com/go-gost/x/listener/obfs/http"
|
||||||
|
|
@ -78,12 +101,15 @@ import (
|
||||||
_ "github.com/go-gost/x/listener/redirect/udp"
|
_ "github.com/go-gost/x/listener/redirect/udp"
|
||||||
_ "github.com/go-gost/x/listener/rtcp"
|
_ "github.com/go-gost/x/listener/rtcp"
|
||||||
_ "github.com/go-gost/x/listener/rudp"
|
_ "github.com/go-gost/x/listener/rudp"
|
||||||
|
_ "github.com/go-gost/x/listener/serial"
|
||||||
_ "github.com/go-gost/x/listener/ssh"
|
_ "github.com/go-gost/x/listener/ssh"
|
||||||
_ "github.com/go-gost/x/listener/sshd"
|
_ "github.com/go-gost/x/listener/sshd"
|
||||||
_ "github.com/go-gost/x/listener/tap"
|
_ "github.com/go-gost/x/listener/tap"
|
||||||
_ "github.com/go-gost/x/listener/tcp"
|
_ "github.com/go-gost/x/listener/tcp"
|
||||||
_ "github.com/go-gost/x/listener/tls"
|
_ "github.com/go-gost/x/listener/tls"
|
||||||
_ "github.com/go-gost/x/listener/tun"
|
_ "github.com/go-gost/x/listener/tun"
|
||||||
|
_ "github.com/go-gost/x/listener/tungo"
|
||||||
_ "github.com/go-gost/x/listener/udp"
|
_ "github.com/go-gost/x/listener/udp"
|
||||||
|
_ "github.com/go-gost/x/listener/unix"
|
||||||
_ "github.com/go-gost/x/listener/ws"
|
_ "github.com/go-gost/x/listener/ws"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
const (
|
var (
|
||||||
version = "3.0.0-rc.3"
|
version = "3.3.0"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
version: "3.4"
|
|
||||||
services:
|
|
||||||
gost:
|
|
||||||
build: .
|
|
||||||
216
go.mod
216
go.mod
|
|
@ -1,112 +1,170 @@
|
||||||
module github.com/go-gost/gost
|
module github.com/go-gost/gost
|
||||||
|
|
||||||
go 1.18
|
go 1.26.3
|
||||||
|
|
||||||
replace github.com/templexxx/cpu v0.0.7 => github.com/templexxx/cpu v0.0.10-0.20211111114238-98168dcec14a
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-gost/core v0.0.0-20230114050924-1a8c1ccb1dc5
|
github.com/go-gost/core v0.4.1
|
||||||
github.com/go-gost/x v0.0.0-20230115094412-834c6447ca15
|
github.com/go-gost/x v0.10.11-0.20260605152603-e45d9a8cc81a
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
github.com/judwhite/go-svc v1.2.1
|
||||||
|
github.com/moby/moby/client v0.4.0
|
||||||
|
github.com/stretchr/testify v1.11.1
|
||||||
|
github.com/testcontainers/testcontainers-go v0.42.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||||
|
github.com/alessio/shellescape v1.4.1 // indirect
|
||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
|
github.com/containerd/errdefs v1.0.0 // indirect
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||||
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
|
github.com/containerd/platforms v0.2.1 // indirect
|
||||||
github.com/coreos/go-iptables v0.5.0 // indirect
|
github.com/coreos/go-iptables v0.5.0 // indirect
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||||
|
github.com/danieljoos/wincred v1.2.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/gin-contrib/cors v1.3.1 // indirect
|
github.com/docker/go-connections v0.7.0 // indirect
|
||||||
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
|
github.com/dunglas/httpsfv v1.1.0 // indirect
|
||||||
|
github.com/ebitengine/purego v0.10.0 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
|
github.com/gin-contrib/cors v1.7.2 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/gin-gonic/gin v1.8.2 // indirect
|
github.com/gin-gonic/gin v1.10.1 // indirect
|
||||||
github.com/go-gost/gosocks4 v0.0.1 // indirect
|
github.com/go-gost/go-shadowsocks2 v0.1.3 // indirect
|
||||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 // indirect
|
github.com/go-gost/gosocks4 v0.1.0 // indirect
|
||||||
github.com/go-gost/relay v0.2.0 // indirect
|
github.com/go-gost/gosocks5 v0.5.0 // indirect
|
||||||
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451 // indirect
|
github.com/go-gost/plugin v0.3.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.0 // indirect
|
github.com/go-gost/relay v0.6.0 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
github.com/go-gost/tls-dissector v0.2.0 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/goccy/go-json v0.10.0 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/gopacket v1.1.19 // indirect
|
github.com/google/gopacket v1.1.19 // indirect
|
||||||
github.com/google/pprof v0.0.0-20221219190121-3cb0bae90811 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid v1.3.1 // indirect
|
github.com/klauspost/compress v1.18.5 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.9.9 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/klauspost/reedsolomon v1.11.8 // indirect
|
||||||
github.com/lucas-clemente/quic-go v0.31.1 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
github.com/marten-seemann/qpack v0.3.0 // indirect
|
github.com/magiconair/properties v1.8.10 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
|
github.com/miekg/dns v1.1.61 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
|
||||||
github.com/miekg/dns v1.1.50 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
|
github.com/moby/go-archive v0.2.0 // indirect
|
||||||
|
github.com/moby/moby/api v1.54.1 // indirect
|
||||||
|
github.com/moby/patternmatcher v0.6.1 // indirect
|
||||||
|
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||||
|
github.com/moby/sys/user v0.4.0 // indirect
|
||||||
|
github.com/moby/sys/userns v0.1.0 // indirect
|
||||||
|
github.com/moby/term v0.5.2 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.6.1 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
github.com/pion/dtls/v3 v3.1.1 // indirect
|
||||||
github.com/pion/dtls/v2 v2.1.5 // indirect
|
github.com/pion/logging v0.2.4 // indirect
|
||||||
github.com/pion/logging v0.2.2 // indirect
|
github.com/pion/transport/v4 v4.0.1 // indirect
|
||||||
github.com/pion/transport v0.14.1 // indirect
|
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||||
github.com/pion/udp v0.1.1 // indirect
|
|
||||||
github.com/pires/go-proxyproto v0.6.2 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
github.com/prometheus/client_model v0.6.0 // indirect
|
||||||
|
github.com/prometheus/common v0.48.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
|
github.com/quic-go/quic-go v0.59.1 // indirect
|
||||||
|
github.com/quic-go/webtransport-go v0.10.0 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/rs/xid v1.3.0 // indirect
|
github.com/rs/xid v1.3.0 // indirect
|
||||||
github.com/shadowsocks/go-shadowsocks2 v0.1.5 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/shadowsocks/go-shadowsocks2 v0.1.6-0.20241020092332-e1fe9ea73740 // indirect
|
||||||
|
github.com/shirou/gopsutil/v4 v4.26.3 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
|
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
|
||||||
github.com/spf13/afero v1.9.3 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/spf13/viper v1.14.0 // indirect
|
github.com/spf13/viper v1.19.0 // indirect
|
||||||
github.com/subosito/gotenv v1.4.1 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/templexxx/cpu v0.0.7 // indirect
|
github.com/templexxx/cpu v0.1.1 // indirect
|
||||||
github.com/templexxx/xorsimd v0.4.1 // indirect
|
github.com/templexxx/xorsimd v0.4.3 // indirect
|
||||||
github.com/tjfoc/gmsm v1.3.2 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||||
github.com/vishvananda/netlink v1.1.0 // indirect
|
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.1 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
github.com/xtaci/smux v1.5.16 // indirect
|
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 // indirect
|
||||||
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
|
github.com/vulcand/predicate v1.2.0 // indirect
|
||||||
|
github.com/xjasonlyu/tun2socks/v2 v2.6.0 // indirect
|
||||||
|
github.com/xtaci/kcp-go/v5 v5.6.5 // indirect
|
||||||
|
github.com/xtaci/smux v1.5.31 // indirect
|
||||||
github.com/xtaci/tcpraw v1.2.25 // indirect
|
github.com/xtaci/tcpraw v1.2.25 // indirect
|
||||||
github.com/yl2chen/cidranger v1.0.2 // indirect
|
github.com/yl2chen/cidranger v1.0.2 // indirect
|
||||||
golang.org/x/crypto v0.4.0 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 // indirect
|
github.com/zalando/go-keyring v0.2.4 // indirect
|
||||||
golang.org/x/mod v0.7.0 // indirect
|
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||||
golang.org/x/net v0.4.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
golang.org/x/sys v0.3.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||||
golang.org/x/text v0.5.0 // indirect
|
go.opentelemetry.io/otel v1.41.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
go.opentelemetry.io/otel/metric v1.41.0 // indirect
|
||||||
golang.org/x/tools v0.4.0 // indirect
|
go.opentelemetry.io/otel/trace v1.41.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e // indirect
|
golang.org/x/crypto v0.50.0 // indirect
|
||||||
google.golang.org/grpc v1.51.0 // indirect
|
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
golang.org/x/mod v0.34.0 // indirect
|
||||||
|
golang.org/x/net v0.53.0 // indirect
|
||||||
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
|
golang.org/x/sys v0.43.0 // indirect
|
||||||
|
golang.org/x/term v0.42.0 // indirect
|
||||||
|
golang.org/x/text v0.36.0 // indirect
|
||||||
|
golang.org/x/time v0.12.0 // indirect
|
||||||
|
golang.org/x/tools v0.43.0 // indirect
|
||||||
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||||
|
google.golang.org/grpc v1.79.3 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
gvisor.dev/gvisor v0.0.0-20250523182742-eede7a881b20 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Check Root User
|
||||||
|
|
||||||
|
# If you want to run as another user, please modify $EUID to be owned by this user
|
||||||
|
if [[ "$EUID" -ne '0' ]]; then
|
||||||
|
echo "$(tput setaf 1)Error: You must run this script as root!$(tput sgr0)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set the desired GitHub repository
|
||||||
|
repo="go-gost/gost"
|
||||||
|
base_url="https://api.github.com/repos/$repo/releases"
|
||||||
|
|
||||||
|
# Function to download and install gost
|
||||||
|
install_gost() {
|
||||||
|
version=$1
|
||||||
|
# Detect the operating system
|
||||||
|
if [[ "$(uname)" == "Linux" ]]; then
|
||||||
|
os="linux"
|
||||||
|
elif [[ "$(uname)" == "Darwin" ]]; then
|
||||||
|
os="darwin"
|
||||||
|
elif [[ "$(uname)" == "MINGW"* ]]; then
|
||||||
|
os="windows"
|
||||||
|
else
|
||||||
|
echo "Unsupported operating system."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect the CPU architecture
|
||||||
|
arch=$(uname -m)
|
||||||
|
case $arch in
|
||||||
|
x86_64)
|
||||||
|
cpu_arch="amd64"
|
||||||
|
;;
|
||||||
|
armv5*)
|
||||||
|
cpu_arch="armv5"
|
||||||
|
;;
|
||||||
|
armv6*)
|
||||||
|
cpu_arch="armv6"
|
||||||
|
;;
|
||||||
|
armv7*)
|
||||||
|
cpu_arch="armv7"
|
||||||
|
;;
|
||||||
|
aarch64|arm64)
|
||||||
|
cpu_arch="arm64"
|
||||||
|
;;
|
||||||
|
i686)
|
||||||
|
cpu_arch="386"
|
||||||
|
;;
|
||||||
|
mips64*)
|
||||||
|
cpu_arch="mips64"
|
||||||
|
;;
|
||||||
|
mips*)
|
||||||
|
cpu_arch="mips"
|
||||||
|
;;
|
||||||
|
mipsel*)
|
||||||
|
cpu_arch="mipsle"
|
||||||
|
;;
|
||||||
|
riscv64)
|
||||||
|
cpu_arch="riscv64"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported CPU architecture."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
get_download_url="$base_url/tags/$version"
|
||||||
|
download_url=$(curl -s "$get_download_url" | awk -F'"' -v re=".*${os}.*${cpu_arch}.*" '/"browser_download_url":/ && $4 ~ re { print $4 }' | head -n 1)
|
||||||
|
|
||||||
|
# Download and install the binary
|
||||||
|
install_path="/usr/local/bin"
|
||||||
|
echo "Downloading and installing gost version $version..."
|
||||||
|
curl -fsSL "$download_url" | tar -xzC "$install_path" gost
|
||||||
|
chmod +x "$install_path/gost"
|
||||||
|
|
||||||
|
# Remove binary from macOS quarantine when installing for first time
|
||||||
|
[[ "$os" == "darwin" ]] && { xattr -d com.apple.quarantine "$install_path/gost" 2>&-; }
|
||||||
|
|
||||||
|
echo "gost installation completed!"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Retrieve available versions from GitHub API
|
||||||
|
versions=$(curl -s "$base_url" | awk -F'"' '/"tag_name":/ {print $4}')
|
||||||
|
|
||||||
|
# Check if --install option provided
|
||||||
|
if [[ "$1" == "--install" ]]; then
|
||||||
|
# Install the latest version automatically
|
||||||
|
latest_version=$(echo "$versions" | head -n 1)
|
||||||
|
install_gost $latest_version
|
||||||
|
else
|
||||||
|
# Display available versions to the user
|
||||||
|
echo "Available gost versions:"
|
||||||
|
select version in $versions; do
|
||||||
|
if [[ -n $version ]]; then
|
||||||
|
install_gost $version
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "Invalid choice! Please select a valid option."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
FROM alpine:3.22
|
||||||
|
|
||||||
|
# add tools needed by e2e containers and health checks
|
||||||
|
RUN apk add --no-cache iptables curl netcat-openbsd python3
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
# End-to-End Tests
|
||||||
|
|
||||||
|
Integration tests that spin up real gost instances inside Docker containers and verify protocol behavior over the network.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [Docker](https://docs.docker.com/get-docker/) (running daemon)
|
||||||
|
- Go toolchain (for compiling the gost binary under test)
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
From the repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all e2e tests
|
||||||
|
go test ./tests/e2e/ -v -timeout 10m
|
||||||
|
|
||||||
|
# Run a specific test suite
|
||||||
|
go test ./tests/e2e/ -v -run TestShadowsocksSuite -timeout 5m
|
||||||
|
go test ./tests/e2e/ -v -run TestParallelSelectorSuite -timeout 5m
|
||||||
|
|
||||||
|
# Use a pre-built gost binary (skips compilation)
|
||||||
|
go test ./tests/e2e/ -v -gost-bin /path/to/gost
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/e2e/
|
||||||
|
├── Dockerfile # Shared base image (Alpine + curl, python3, etc.)
|
||||||
|
├── main_test.go # TestMain: compiles gost, creates Docker network
|
||||||
|
├── utils.go # Helpers: container lifecycle, config rendering
|
||||||
|
├── scripts/
|
||||||
|
│ ├── tcp_echo.py # HTTP echo server (responds with "hello-gost")
|
||||||
|
│ └── udp_echo.py # UDP echo server (reflects payloads)
|
||||||
|
├── testdata/ # config files or data files for running cases
|
||||||
|
├── shadowsocks_test.go # Shadowsocks protocol tests
|
||||||
|
└── parallel_selector_test.go # Parallel node selector tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### How it works
|
||||||
|
|
||||||
|
1. **TestMain** (`main_test.go`) compiles the gost binary from `../../cmd/gost` (unless `-gost-bin` is provided) and creates a shared Docker network for all containers.
|
||||||
|
2. Each test suite starts echo server containers (TCP/UDP), then launches separate gost containers for server and client roles.
|
||||||
|
3. Client configs use Go template syntax (`{{.ServerAddr}}`) so the server address is injected at runtime.
|
||||||
|
4. Tests verify end-to-end connectivity by sending traffic through the gost proxy chain and checking that the echo server responds correctly.
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
- Increase `-timeout` for CI or slow networks. Container image builds on first run take extra time.
|
||||||
|
- Use `-gost-bin` to avoid recompiling when iterating on tests locally.
|
||||||
|
- Add `-v` to see container log output on failure.
|
||||||
|
- RunGostContainer will wait for exposedPorts automatically, but it's not reliable for udp ports. So, you should check the readiness of udp ports inside cases.
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/testcontainers/testcontainers-go/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SharedNetworkName string
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
flag.Parse()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
shouldCleanup := false
|
||||||
|
|
||||||
|
if GostBinPath == "" {
|
||||||
|
GostBinPath = "/tmp/gost-test-bin"
|
||||||
|
cmd := exec.Command("go", "build", "-o", GostBinPath, "../../cmd/gost")
|
||||||
|
cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
fmt.Printf("Failed to compile gost: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
shouldCleanup = true
|
||||||
|
}
|
||||||
|
|
||||||
|
net, err := network.New(ctx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create network: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
SharedNetworkName = net.Name
|
||||||
|
|
||||||
|
code := m.Run()
|
||||||
|
|
||||||
|
net.Remove(ctx)
|
||||||
|
if shouldCleanup {
|
||||||
|
os.Remove(GostBinPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/testcontainers/testcontainers-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ParallelSelectorSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
ctx context.Context
|
||||||
|
echoC testcontainers.Container
|
||||||
|
echoIP string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ParallelSelectorSuite) SetupSuite() {
|
||||||
|
s.ctx = context.Background()
|
||||||
|
|
||||||
|
echoC, err := RunEchoContainer(s.ctx, SharedNetworkName)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.echoC = echoC
|
||||||
|
|
||||||
|
echoIP, err := echoC.ContainerIP(s.ctx)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.echoIP = echoIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ParallelSelectorSuite) TearDownSuite() {
|
||||||
|
if s.echoC != nil {
|
||||||
|
s.echoC.Terminate(s.ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ParallelSelectorSuite) TestParallelSelector() {
|
||||||
|
gostC, err := RunGostContainerWithPorts(s.ctx, SharedNetworkName, "testdata/parallel_selector/server.yaml", "8080/tcp")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer gostC.Terminate(s.ctx)
|
||||||
|
|
||||||
|
// Test the proxy by running curl inside the gost container
|
||||||
|
cmd := []string{"curl", "-v", "-s", "-x", "http://127.0.0.1:8080", fmt.Sprintf("http://%s:5678", s.echoIP)}
|
||||||
|
code, out, err := gostC.Exec(s.ctx, cmd)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(out)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Equal(0, code)
|
||||||
|
|
||||||
|
s.Require().Contains(string(body), "hello-gost")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParallelSelectorSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(ParallelSelectorSuite))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(BaseHTTPRequestHandler):
|
||||||
|
def do_GET(self):
|
||||||
|
body = b"hello-gost"
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-Length", str(len(body)))
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(body)
|
||||||
|
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
HTTPServer(("0.0.0.0", 5678), Handler).serve_forever()
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
sock.bind(("0.0.0.0", 5679))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
data, addr = sock.recvfrom(2048)
|
||||||
|
sock.sendto(data, addr)
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/testcontainers/testcontainers-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShadowsocksSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
ctx context.Context
|
||||||
|
echoC testcontainers.Container
|
||||||
|
echoIP string
|
||||||
|
udpC testcontainers.Container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShadowsocksSuite) SetupSuite() {
|
||||||
|
s.ctx = context.Background()
|
||||||
|
|
||||||
|
s.T().Logf("start tcp echo container...")
|
||||||
|
echoC, err := RunEchoContainer(s.ctx, SharedNetworkName)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.echoC = echoC
|
||||||
|
|
||||||
|
echoIP, err := echoC.ContainerIP(s.ctx)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.echoIP = echoIP
|
||||||
|
|
||||||
|
s.T().Logf("start udp echo container...")
|
||||||
|
udpC, err := RunUDPEchoContainer(s.ctx, SharedNetworkName)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.udpC = udpC
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShadowsocksSuite) TearDownSuite() {
|
||||||
|
if s.echoC != nil {
|
||||||
|
s.echoC.Terminate(s.ctx)
|
||||||
|
}
|
||||||
|
if s.udpC != nil {
|
||||||
|
s.udpC.Terminate(s.ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShadowsocksSuite) TestShadowsocksTCP() {
|
||||||
|
s.runTCPCase("aes256gcm", "testdata/shadowsocks/tcp_server_aes256gcm.yaml", "testdata/shadowsocks/tcp_client_aes256gcm.yaml")
|
||||||
|
s.runTCPCase("chacha20", "testdata/shadowsocks/tcp_server_chacha20.yaml", "testdata/shadowsocks/tcp_client_chacha20.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShadowsocksSuite) TestShadowsocks2022TCP() {
|
||||||
|
s.runTCPCase("2022-aes128", "testdata/shadowsocks/tcp_server_2022_aes128.yaml", "testdata/shadowsocks/tcp_client_2022_aes128.yaml")
|
||||||
|
s.runTCPCase("2022-aes256", "testdata/shadowsocks/tcp_server_2022_aes256.yaml", "testdata/shadowsocks/tcp_client_2022_aes256.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShadowsocksSuite) TestShadowsocks2022TCPMultiPSK() {
|
||||||
|
s.runTCPCase("2022-aes128-multipsk", "testdata/shadowsocks/tcp_server_2022_aes128_multipsk.yaml", "testdata/shadowsocks/tcp_client_2022_aes128_multipsk.yaml")
|
||||||
|
s.runTCPCase("2022-aes256-multipsk", "testdata/shadowsocks/tcp_server_2022_aes256_multipsk.yaml", "testdata/shadowsocks/tcp_client_2022_aes256_multipsk.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShadowsocksSuite) TestShadowsocksUDP() {
|
||||||
|
s.runUDPCase("aes256gcm", "testdata/shadowsocks/udp_server_aes256gcm.yaml", "testdata/shadowsocks/udp_client_aes256gcm.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShadowsocksSuite) TestShadowsocks2022UDP() {
|
||||||
|
s.runUDPCase("2022-aes128", "testdata/shadowsocks/udp_server_2022_aes128.yaml", "testdata/shadowsocks/udp_client_2022_aes128.yaml")
|
||||||
|
s.runUDPCase("2022-aes256", "testdata/shadowsocks/udp_server_2022_aes256.yaml", "testdata/shadowsocks/udp_client_2022_aes256.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShadowsocksSuite) runUDPCase(name, serverConfig, clientConfig string) {
|
||||||
|
s.T().Run(name, func(t *testing.T) {
|
||||||
|
serverAlias := name + "-ssu-server"
|
||||||
|
serverC, err := RunGostContainerWithOptions(s.ctx, SharedNetworkName, serverConfig, []string{serverAlias}, []string{"8389/udp"})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer serverC.Terminate(s.ctx)
|
||||||
|
|
||||||
|
rendered, err := RenderConfig(clientConfig, ConfigData{ServerAddr: serverAlias + ":8389"})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer os.Remove(rendered)
|
||||||
|
|
||||||
|
clientC, err := RunGostContainerWithPorts(
|
||||||
|
s.ctx,
|
||||||
|
SharedNetworkName,
|
||||||
|
rendered,
|
||||||
|
"9000/udp",
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer clientC.Terminate(s.ctx)
|
||||||
|
|
||||||
|
host, err := clientC.Host(s.ctx)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
port, err := clientC.MappedPort(s.ctx, "9000/udp")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
conn, err := net.DialTimeout("udp", net.JoinHostPort(host, port.Port()), 5*time.Second)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
payload := []byte("hello-gost-udp")
|
||||||
|
buf := make([]byte, 2048)
|
||||||
|
var n int
|
||||||
|
for i := range 5 {
|
||||||
|
_, err = conn.Write(payload)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
n, err = conn.Read(buf)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s.T().Logf("udp read attempt %d failed: %v, retrying...", i+1, err)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
DumpLogs(s.T(), s.ctx, name+" udp client logs", clientC)
|
||||||
|
DumpLogs(s.T(), s.ctx, name+" udp server logs", serverC)
|
||||||
|
}
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Contains(string(buf[:n]), "hello-gost")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShadowsocksSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(ShadowsocksSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShadowsocksSuite) runTCPCase(name, serverConfig, clientConfig string) {
|
||||||
|
s.T().Run(name, func(t *testing.T) {
|
||||||
|
serverAlias := name + "-ss-server"
|
||||||
|
serverC, err := RunGostContainerWithOptions(s.ctx, SharedNetworkName, serverConfig, []string{serverAlias}, []string{"8388/tcp"})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer serverC.Terminate(s.ctx)
|
||||||
|
|
||||||
|
rendered, err := RenderConfig(clientConfig, ConfigData{ServerAddr: serverAlias + ":8388"})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer os.Remove(rendered)
|
||||||
|
|
||||||
|
clientC, err := RunGostContainerWithPorts(s.ctx, SharedNetworkName, rendered, "8080/tcp")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer clientC.Terminate(s.ctx)
|
||||||
|
|
||||||
|
cmd := []string{"curl", "-v", "-s", "-x", "http://127.0.0.1:8080", fmt.Sprintf("http://%s:5678", s.echoIP)}
|
||||||
|
code, out, err := clientC.Exec(s.ctx, cmd)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(out)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
if code != 0 || !strings.Contains(string(body), "hello-gost") {
|
||||||
|
DumpLogs(s.T(), s.ctx, name+" client logs", clientC)
|
||||||
|
DumpLogs(s.T(), s.ctx, name+" server logs", serverC)
|
||||||
|
}
|
||||||
|
s.Require().Equal(0, code)
|
||||||
|
s.Require().Contains(string(body), "hello-gost")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
services:
|
||||||
|
- name: proxy
|
||||||
|
addr: :8080
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
chain: my-chain
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
- name: dummy-1
|
||||||
|
addr: :18081
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: my-chain
|
||||||
|
hops:
|
||||||
|
- name: hop-1
|
||||||
|
selector:
|
||||||
|
strategy: parallel
|
||||||
|
nodes:
|
||||||
|
- name: node-1
|
||||||
|
addr: 127.0.0.1:18081
|
||||||
|
connector:
|
||||||
|
type: http
|
||||||
|
# non existed node
|
||||||
|
- name: node-2
|
||||||
|
addr: 127.0.0.1:18082
|
||||||
|
connector:
|
||||||
|
type: http
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
services:
|
||||||
|
- name: http-proxy
|
||||||
|
addr: :8080
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
chain: ss-tcp-chain
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: ss-tcp-chain
|
||||||
|
hops:
|
||||||
|
- name: ss-hop
|
||||||
|
nodes:
|
||||||
|
- name: ss-node
|
||||||
|
addr: {{.ServerAddr}}
|
||||||
|
connector:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-128-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||||
|
dialer:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
services:
|
||||||
|
- name: http-proxy
|
||||||
|
addr: :8080
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
chain: ss-tcp-chain
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: ss-tcp-chain
|
||||||
|
hops:
|
||||||
|
- name: ss-hop
|
||||||
|
nodes:
|
||||||
|
- name: ss-node
|
||||||
|
addr: {{.ServerAddr}}
|
||||||
|
connector:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-128-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1Ng==:Vbwi6yqCwvPMPR1bCi32Dg==
|
||||||
|
dialer:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
services:
|
||||||
|
- name: http-proxy
|
||||||
|
addr: :8080
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
chain: ss-tcp-chain
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: ss-tcp-chain
|
||||||
|
hops:
|
||||||
|
- name: ss-hop
|
||||||
|
nodes:
|
||||||
|
- name: ss-node
|
||||||
|
addr: {{.ServerAddr}}
|
||||||
|
connector:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-256-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||||
|
dialer:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
services:
|
||||||
|
- name: http-proxy
|
||||||
|
addr: :8080
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
chain: ss-tcp-chain
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: ss-tcp-chain
|
||||||
|
hops:
|
||||||
|
- name: ss-hop
|
||||||
|
nodes:
|
||||||
|
- name: ss-node
|
||||||
|
addr: {{.ServerAddr}}
|
||||||
|
connector:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-256-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=:YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
|
||||||
|
dialer:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
services:
|
||||||
|
- name: http-proxy
|
||||||
|
addr: :8080
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
chain: ss-tcp-chain
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: ss-tcp-chain
|
||||||
|
hops:
|
||||||
|
- name: ss-hop
|
||||||
|
nodes:
|
||||||
|
- name: ss-node
|
||||||
|
addr: {{.ServerAddr}}
|
||||||
|
connector:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: aes-256-gcm
|
||||||
|
password: secret
|
||||||
|
dialer:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
services:
|
||||||
|
- name: http-proxy
|
||||||
|
addr: :8080
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
chain: ss-tcp-chain
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: ss-tcp-chain
|
||||||
|
hops:
|
||||||
|
- name: ss-hop
|
||||||
|
nodes:
|
||||||
|
- name: ss-node
|
||||||
|
addr: {{.ServerAddr}}
|
||||||
|
connector:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: chacha20-ietf-poly1305
|
||||||
|
password: secret
|
||||||
|
dialer:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
services:
|
||||||
|
- name: ss-server
|
||||||
|
addr: :8388
|
||||||
|
handler:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-128-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
services:
|
||||||
|
- name: ss-server
|
||||||
|
addr: :8388
|
||||||
|
handler:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-128-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||||
|
metadata:
|
||||||
|
users:
|
||||||
|
test: Vbwi6yqCwvPMPR1bCi32Dg==
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
services:
|
||||||
|
- name: ss-server
|
||||||
|
addr: :8388
|
||||||
|
handler:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-256-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
services:
|
||||||
|
- name: ss-server
|
||||||
|
addr: :8388
|
||||||
|
handler:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-256-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||||
|
metadata:
|
||||||
|
users:
|
||||||
|
test: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
services:
|
||||||
|
- name: ss-server
|
||||||
|
addr: :8388
|
||||||
|
handler:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: aes-256-gcm
|
||||||
|
password: secret
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
services:
|
||||||
|
- name: ss-server
|
||||||
|
addr: :8388
|
||||||
|
handler:
|
||||||
|
type: ss
|
||||||
|
auth:
|
||||||
|
username: chacha20-ietf-poly1305
|
||||||
|
password: secret
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
services:
|
||||||
|
- name: udp-proxy
|
||||||
|
addr: :9000
|
||||||
|
handler:
|
||||||
|
type: udp
|
||||||
|
chain: ssu-chain
|
||||||
|
forwarder:
|
||||||
|
nodes:
|
||||||
|
- name: udp-echo
|
||||||
|
addr: udp-echo:5679
|
||||||
|
listener:
|
||||||
|
type: udp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: ssu-chain
|
||||||
|
hops:
|
||||||
|
- name: ssu-hop
|
||||||
|
nodes:
|
||||||
|
- name: ssu-node
|
||||||
|
addr: {{.ServerAddr}}
|
||||||
|
connector:
|
||||||
|
type: ssu
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-128-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||||
|
dialer:
|
||||||
|
type: udp
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
services:
|
||||||
|
- name: udp-proxy
|
||||||
|
addr: :9000
|
||||||
|
handler:
|
||||||
|
type: udp
|
||||||
|
chain: ssu-chain
|
||||||
|
forwarder:
|
||||||
|
nodes:
|
||||||
|
- name: udp-echo
|
||||||
|
addr: udp-echo:5679
|
||||||
|
listener:
|
||||||
|
type: udp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: ssu-chain
|
||||||
|
hops:
|
||||||
|
- name: ssu-hop
|
||||||
|
nodes:
|
||||||
|
- name: ssu-node
|
||||||
|
addr: {{.ServerAddr}}
|
||||||
|
connector:
|
||||||
|
type: ssu
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-256-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||||
|
dialer:
|
||||||
|
type: udp
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
services:
|
||||||
|
- name: udp-proxy
|
||||||
|
addr: :9000
|
||||||
|
handler:
|
||||||
|
type: udp
|
||||||
|
chain: ssu-chain
|
||||||
|
forwarder:
|
||||||
|
nodes:
|
||||||
|
- name: udp-echo
|
||||||
|
addr: udp-echo:5679
|
||||||
|
listener:
|
||||||
|
type: udp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: ssu-chain
|
||||||
|
hops:
|
||||||
|
- name: ssu-hop
|
||||||
|
nodes:
|
||||||
|
- name: ssu-node
|
||||||
|
addr: {{.ServerAddr}}
|
||||||
|
connector:
|
||||||
|
type: ssu
|
||||||
|
auth:
|
||||||
|
username: aes-256-gcm
|
||||||
|
password: secret
|
||||||
|
dialer:
|
||||||
|
type: udp
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
services:
|
||||||
|
- name: ssu-server
|
||||||
|
addr: :8389
|
||||||
|
handler:
|
||||||
|
type: ssu
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-128-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||||
|
listener:
|
||||||
|
type: udp
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
services:
|
||||||
|
- name: ssu-server
|
||||||
|
addr: :8389
|
||||||
|
handler:
|
||||||
|
type: ssu
|
||||||
|
auth:
|
||||||
|
username: 2022-blake3-aes-256-gcm
|
||||||
|
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||||
|
listener:
|
||||||
|
type: udp
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
services:
|
||||||
|
- name: ssu-server
|
||||||
|
addr: :8389
|
||||||
|
handler:
|
||||||
|
type: ssu
|
||||||
|
auth:
|
||||||
|
username: aes-256-gcm
|
||||||
|
password: secret
|
||||||
|
listener:
|
||||||
|
type: udp
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/moby/moby/client"
|
||||||
|
"github.com/testcontainers/testcontainers-go"
|
||||||
|
"github.com/testcontainers/testcontainers-go/wait"
|
||||||
|
)
|
||||||
|
|
||||||
|
var GostBinPath string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&GostBinPath, "gost-bin", "", "Path to a pre-built gost binary (skips compilation)")
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigData struct {
|
||||||
|
ServerAddr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func DumpLogs(t *testing.T, ctx context.Context, label string, c testcontainers.Container) {
|
||||||
|
logs, err := c.Logs(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer logs.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(logs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("%s:\n%s", label, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderConfig(tmplPath string, data ConfigData) (string, error) {
|
||||||
|
tmpl, err := template.ParseFiles(tmplPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.CreateTemp("", "gost-e2e-config-*.yaml")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tmpl.Execute(f, data); err != nil {
|
||||||
|
f.Close()
|
||||||
|
os.Remove(f.Name())
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
os.Remove(f.Name())
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.Name(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunEchoContainer(ctx context.Context, networkName string) (testcontainers.Container, error) {
|
||||||
|
req := echoContainerRequest(ctx, networkName)
|
||||||
|
return testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||||
|
ContainerRequest: req,
|
||||||
|
Started: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func echoContainerRequest(_ context.Context, networkName string) testcontainers.ContainerRequest {
|
||||||
|
return testcontainers.ContainerRequest{
|
||||||
|
FromDockerfile: testcontainers.FromDockerfile{
|
||||||
|
Context: ".",
|
||||||
|
Dockerfile: "Dockerfile",
|
||||||
|
Repo: "gost-e2e",
|
||||||
|
Tag: "latest",
|
||||||
|
KeepImage: true,
|
||||||
|
BuildOptionsModifier: func(opts *client.ImageBuildOptions) {
|
||||||
|
opts.NetworkMode = "host"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Networks: []string{networkName},
|
||||||
|
NetworkAliases: map[string][]string{
|
||||||
|
networkName: {"tcp-echo"},
|
||||||
|
},
|
||||||
|
Files: []testcontainers.ContainerFile{
|
||||||
|
{HostFilePath: "scripts/tcp_echo.py", ContainerFilePath: "/scripts/tcp_echo.py", FileMode: 0644},
|
||||||
|
},
|
||||||
|
ExposedPorts: []string{"5678/tcp"},
|
||||||
|
Cmd: []string{"python3", "/scripts/tcp_echo.py"},
|
||||||
|
WaitingFor: wait.ForExposedPort(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunUDPEchoContainer(ctx context.Context, networkName string) (testcontainers.Container, error) {
|
||||||
|
req := udpEchoContainerRequest(ctx, networkName)
|
||||||
|
return testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||||
|
ContainerRequest: req,
|
||||||
|
Started: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func udpEchoContainerRequest(_ context.Context, networkName string) testcontainers.ContainerRequest {
|
||||||
|
return testcontainers.ContainerRequest{
|
||||||
|
FromDockerfile: testcontainers.FromDockerfile{
|
||||||
|
Context: ".",
|
||||||
|
Dockerfile: "Dockerfile",
|
||||||
|
Repo: "gost-e2e",
|
||||||
|
Tag: "latest",
|
||||||
|
KeepImage: true,
|
||||||
|
BuildOptionsModifier: func(opts *client.ImageBuildOptions) {
|
||||||
|
opts.NetworkMode = "host"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Networks: []string{networkName},
|
||||||
|
NetworkAliases: map[string][]string{
|
||||||
|
networkName: {"udp-echo"},
|
||||||
|
},
|
||||||
|
Files: []testcontainers.ContainerFile{
|
||||||
|
{HostFilePath: "scripts/udp_echo.py", ContainerFilePath: "/scripts/udp_echo.py", FileMode: 0644},
|
||||||
|
},
|
||||||
|
ExposedPorts: []string{"5679/udp"},
|
||||||
|
Cmd: []string{"python3", "/scripts/udp_echo.py"},
|
||||||
|
WaitingFor: wait.ForExposedPort().SkipInternalCheck(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunGostContainer(ctx context.Context, networkName, yamlPath string) (testcontainers.Container, error) {
|
||||||
|
return runGostContainer(ctx, networkName, yamlPath, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunGostContainerWithPorts(ctx context.Context, networkName, yamlPath string, exposedPorts ...string) (testcontainers.Container, error) {
|
||||||
|
return runGostContainer(ctx, networkName, yamlPath, nil, exposedPorts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunGostContainerWithOptions(ctx context.Context, networkName, yamlPath string, aliases, exposedPorts []string) (testcontainers.Container, error) {
|
||||||
|
return runGostContainer(ctx, networkName, yamlPath, aliases, exposedPorts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runGostContainer(ctx context.Context, networkName, yamlPath string, aliases, exposedPorts []string) (testcontainers.Container, error) {
|
||||||
|
req := testcontainers.ContainerRequest{
|
||||||
|
FromDockerfile: testcontainers.FromDockerfile{
|
||||||
|
Context: ".",
|
||||||
|
Dockerfile: "Dockerfile",
|
||||||
|
Repo: "gost-e2e",
|
||||||
|
Tag: "latest",
|
||||||
|
KeepImage: true,
|
||||||
|
BuildOptionsModifier: func(opts *client.ImageBuildOptions) {
|
||||||
|
opts.NetworkMode = "host"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExposedPorts: exposedPorts,
|
||||||
|
// interal check for udp ports will be failed
|
||||||
|
WaitingFor: wait.ForExposedPort().SkipInternalCheck(),
|
||||||
|
Networks: []string{networkName},
|
||||||
|
NetworkAliases: map[string][]string{
|
||||||
|
networkName: aliases,
|
||||||
|
},
|
||||||
|
Files: []testcontainers.ContainerFile{
|
||||||
|
{HostFilePath: GostBinPath, ContainerFilePath: "/bin/gost", FileMode: 0755},
|
||||||
|
{HostFilePath: yamlPath, ContainerFilePath: "/config.yaml", FileMode: 0644},
|
||||||
|
},
|
||||||
|
Cmd: []string{"/bin/gost", "-C", "/config.yaml"},
|
||||||
|
}
|
||||||
|
|
||||||
|
return testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||||
|
ContainerRequest: req,
|
||||||
|
Started: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue