diff --git a/.github/workflows/buildx.yaml b/.github/workflows/buildx.yaml deleted file mode 100644 index 6207e7f8..00000000 --- a/.github/workflows/buildx.yaml +++ /dev/null @@ -1,74 +0,0 @@ -# ref: https://github.com/crazy-max/diun/blob/master/.github/workflows/build.yml - -name: Docker -on: [push] -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Prepare - id: prepare - run: | - if [[ $GITHUB_REF == refs/tags/* ]]; then - echo ::set-output name=version::${GITHUB_REF#refs/tags/v} - elif [[ $GITHUB_REF == refs/heads/master ]]; then - echo ::set-output name=version::latest - elif [[ $GITHUB_REF == refs/heads/* ]]; then - echo ::set-output name=version::${GITHUB_REF#refs/heads/} - else - echo ::set-output name=version::snapshot - fi - - echo ::set-output name=docker_platforms::linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/386,linux/s390x,linux/riscv64 - echo ::set-output name=docker_image::${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }} - - # 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.docker_image }}:${{ steps.prepare.outputs.version }}" \ - --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.docker_image }}:${{ steps.prepare.outputs.version }}" \ - --file docker-compose.yaml - - - name: Clear - if: always() - run: | - rm -f ${HOME}/.docker/config.json - diff --git a/.github/workflows/buildx.yml b/.github/workflows/buildx.yml new file mode 100644 index 00000000..523327e3 --- /dev/null +++ b/.github/workflows/buildx.yml @@ -0,0 +1,72 @@ +# 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 QEMU + uses: docker/setup-qemu-action@v3 + + - 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 }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6a5edb8a..4e73c676 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: - run: git fetch --force --tags - uses: actions/setup-go@v3 with: - go-version: '1.21.7' + go-version: '1.22' cache: true # More assembly might be required: Docker logins, GPG, etc. It all depends # on your needs. diff --git a/Dockerfile b/Dockerfile index 797006c7..1e27c6ec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.22-alpine as builder - -# Convert TARGETPLATFORM to GOARCH format -# https://github.com/tonistiigi/xx -COPY --from=tonistiigi/xx:golang / / - -ARG TARGETPLATFORM +FROM golang:1.22-alpine AS builder RUN apk add --no-cache musl-dev git gcc @@ -12,12 +6,14 @@ ADD . /src WORKDIR /src -ENV GO111MODULE=on ENV CGO_ENABLED=0 -RUN cd cmd/gost && go env && go build -v +RUN cd cmd/gost && go env && go build + +FROM alpine:3.20 -FROM alpine:latest +# add iptables for tun/tap +RUN apk add --no-cache iptables WORKDIR /bin/ diff --git a/cmd/gost/cfg.go b/cmd/gost/cfg.go index 83a83650..df0c53ce 100644 --- a/cmd/gost/cfg.go +++ b/cmd/gost/cfg.go @@ -5,7 +5,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/json" - "errors" + "fmt" "net" "net/url" "os" @@ -57,7 +57,11 @@ func tlsConfig(certFile, keyFile, caFile string) (*tls.Config, error) { cfg := &tls.Config{Certificates: []tls.Certificate{cert}} - if pool, _ := loadCA(caFile); pool != nil { + pool, err := loadCA(caFile) + if err != nil { + return nil, err + } + if pool != nil { cfg.ClientCAs = pool cfg.ClientAuth = tls.RequireAndVerifyClientCert } @@ -75,7 +79,7 @@ func loadCA(caFile string) (cp *x509.CertPool, err error) { return nil, err } if !cp.AppendCertsFromPEM(data) { - return nil, errors.New("AppendCertsFromPEM failed") + return nil, fmt.Errorf("loadCA %s: AppendCertsFromPEM failed", caFile) } return } diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index a2eb077c..00000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,4 +0,0 @@ -version: "3.4" -services: - gost: - build: . diff --git a/http.go b/http.go index 8ef96e80..8f9e3fde 100644 --- a/http.go +++ b/http.go @@ -6,6 +6,7 @@ import ( "context" "encoding/base64" "fmt" + "io" "net" "net/http" "net/http/httputil" @@ -292,27 +293,65 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { } defer cc.Close() - if req.Method == http.MethodConnect { - b := []byte("HTTP/1.1 200 Connection established\r\n" + - "Proxy-Agent: " + proxyAgent + "\r\n\r\n") - if Debug { - log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(b)) - } - conn.Write(b) - } else { - req.Header.Del("Proxy-Connection") + if req.Method != http.MethodConnect { + h.handleProxy(conn, cc, req) + return + } - if err = req.Write(cc); err != nil { - log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) - return - } + b := []byte("HTTP/1.1 200 Connection established\r\n" + + "Proxy-Agent: " + proxyAgent + "\r\n\r\n") + if Debug { + log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(b)) } + conn.Write(b) log.Logf("[http] %s <-> %s", conn.RemoteAddr(), host) transport(conn, cc) log.Logf("[http] %s >-< %s", conn.RemoteAddr(), host) } +func (h *httpHandler) handleProxy(rw, cc io.ReadWriter, req *http.Request) (err error) { + req.Header.Del("Proxy-Connection") + + if err = req.Write(cc); err != nil { + return err + } + + ch := make(chan error, 1) + + go func() { + ch <- copyBuffer(rw, cc) + }() + + for { + err := func() error { + req, err := http.ReadRequest(bufio.NewReader(rw)) + if err != nil { + return err + } + + if Debug { + dump, _ := httputil.DumpRequest(req, false) + log.Log(string(dump)) + } + + req.Header.Del("Proxy-Connection") + + if err = req.Write(cc); err != nil { + return err + } + return nil + }() + ch <- err + + if err != nil { + break + } + } + + return <-ch +} + func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.Response) (ok bool) { u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization")) if Debug && (u != "" || p != "") {