fix probe resistance

This commit is contained in:
ginuerzh 2022-01-11 17:30:46 +08:00
parent 9574a532ee
commit f86fcb7eba
9 changed files with 156 additions and 107 deletions

View File

@ -36,39 +36,54 @@ jobs:
# Set output parameters. # Set output parameters.
echo ::set-output name=tags::${TAGS} echo ::set-output name=tags::${TAGS}
echo ::set-output name=docker_image::${DOCKER_IMAGE} 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
# https://github.com/actions/checkout # https://github.com/crazy-max/ghaction-docker-buildx
- name: Checkout
uses: actions/checkout@v2
- name: Docker Login
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v1 uses: crazy-max/ghaction-docker-buildx@v1
with:
version: latest
- name: Environment - name: Environment
run: | run: |
echo home=$HOME echo home=$HOME
echo git_ref=$GITHUB_REF echo git_ref=$GITHUB_REF
echo git_sha=$GITHUB_SHA echo git_sha=$GITHUB_SHA
echo version=${{ steps.prepare.outputs.version }}
echo image=${{ steps.prepare.outputs.docker_image }} 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 }} echo avail_platforms=${{ steps.buildx.outputs.platforms }}
- name: Build and push # https://github.com/actions/checkout
uses: docker/build-push-action@v2 - name: Checkout
with: uses: actions/checkout@v2
builder: ${{ steps.buildx.outputs.name }}
context: . - name: Docker Buildx (no push)
file: ./Dockerfile run: |
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 docker buildx bake \
push: true --set ${{ github.event.repository.name }}.platform=${{ steps.prepare.outputs.docker_platforms }} \
tags: ${{ steps.prepare.outputs.tags }} --set ${{ github.event.repository.name }}.output=type=image,push=false \
cache-from: type=registry,ref=${{ steps.prepare.outputs.docker_image }}:buildcache --set ${{ github.event.repository.name }}.tags="${{ steps.prepare.outputs.tags }}" \
cache-to: type=registry,ref=${{ steps.prepare.outputs.docker_image }}:buildcache,mode=max --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

View File

@ -1,17 +1,19 @@
FROM golang:1-alpine as builder FROM --platform=$BUILDPLATFORM golang:1-alpine as builder
RUN apk add --no-cache musl-dev gcc # Convert TARGETPLATFORM to GOARCH format
# https://github.com/tonistiigi/xx
COPY --from=tonistiigi/xx:golang / /
WORKDIR /mod ARG TARGETPLATFORM
ADD go.mod go.sum ./ RUN apk add --no-cache musl-dev git gcc
RUN go mod download
ADD . /src ADD . /src
WORKDIR /src WORKDIR /src
ENV GO111MODULE=on
RUN cd cmd/gost && go env && go build RUN cd cmd/gost && go env && go build
FROM alpine:latest FROM alpine:latest

4
docker-compose.yaml Normal file
View File

@ -0,0 +1,4 @@
version: "3.4"
services:
gost:
build: .

View File

@ -5,7 +5,6 @@ log:
services: services:
- name: http+tcp - name: http+tcp
url: "http://gost:gost@:8000"
addr: ":28000" addr: ":28000"
handler: handler:
type: http type: http
@ -23,7 +22,6 @@ services:
metadata: metadata:
keepAlive: 15s keepAlive: 15s
- name: ss - name: ss
url: "ss://chacha20:gost@:8000"
addr: ":28338" addr: ":28338"
handler: handler:
type: ss type: ss
@ -40,7 +38,6 @@ services:
metadata: metadata:
keepAlive: 15s keepAlive: 15s
- name: socks5 - name: socks5
url: "socks5://gost:gost@:1080"
addr: ":21080" addr: ":21080"
handler: handler:
type: socks5 type: socks5
@ -59,7 +56,6 @@ services:
metadata: metadata:
keepAlive: 15s keepAlive: 15s
- name: socks5+tcp - name: socks5+tcp
url: "socks5://gost:gost@:1080"
addr: ":21081" addr: ":21081"
handler: handler:
type: socks5 type: socks5
@ -74,7 +70,6 @@ services:
metadata: metadata:
keepAlive: 15s keepAlive: 15s
- name: forward - name: forward
url: "socks5://gost:gost@:1080"
addr: ":10053" addr: ":10053"
forwarder: forwarder:
targets: targets:
@ -161,7 +156,6 @@ chains:
nodes: nodes:
- name: node01 - name: node01
addr: ":8081" addr: ":8081"
url: "http://gost:gost@:8081"
# bypass: bypass01 # bypass: bypass01
connector: connector:
type: http type: http
@ -173,7 +167,6 @@ chains:
metadata: {} metadata: {}
- name: node02 - name: node02
addr: ":8082" addr: ":8082"
url: "http://gost:gost@:8082"
# bypass: bypass01 # bypass: bypass01
connector: connector:
type: http type: http
@ -192,7 +185,6 @@ chains:
nodes: nodes:
- name: node03 - name: node03
addr: ":8083" addr: ":8083"
url: "http://gost:gost@:8083"
# bypass: bypass01 # bypass: bypass01
connector: connector:
type: http type: http
@ -222,7 +214,6 @@ chains:
nodes: nodes:
- name: node01 - name: node01
addr: ":21080" addr: ":21080"
url: "http://gost:gost@:8081"
# bypass: bypass01 # bypass: bypass01
connector: connector:
type: socks5 type: socks5

View File

@ -287,22 +287,29 @@ func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.
if !strings.HasPrefix(url, "http") { if !strings.HasPrefix(url, "http") {
url = "http://" + url url = "http://" + url
} }
if r, err := http.Get(url); err == nil { r, err := http.Get(url)
resp = r if err != nil {
defer r.Body.Close() h.logger.Error(err)
break
} }
resp = r
defer resp.Body.Close()
case "host": case "host":
cc, err := net.Dial("tcp", pr.Value) cc, err := net.Dial("tcp", pr.Value)
if err == nil { if err != nil {
defer cc.Close() h.logger.Error(err)
break
req.Write(cc)
handler.Transport(conn, cc)
return
} }
defer cc.Close()
req.Write(cc)
handler.Transport(conn, cc)
return
case "file": case "file":
f, _ := os.Open(pr.Value) f, _ := os.Open(pr.Value)
if f != nil { if f != nil {
defer f.Close()
resp.StatusCode = http.StatusOK resp.StatusCode = http.StatusOK
if finfo, _ := f.Stat(); finfo != nil { if finfo, _ := f.Stat(); finfo != nil {
resp.ContentLength = finfo.Size() resp.ContentLength = finfo.Size()
@ -313,6 +320,9 @@ func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.
} }
} }
if resp.Header == nil {
resp.Header = http.Header{}
}
if resp.StatusCode == 0 { if resp.StatusCode == 0 {
resp.StatusCode = http.StatusProxyAuthRequired resp.StatusCode = http.StatusProxyAuthRequired
resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"") resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
@ -325,7 +335,6 @@ func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.
h.logger.Info("proxy authentication required") h.logger.Info("proxy authentication required")
} else { } else {
resp.Header = http.Header{}
resp.Header.Set("Server", "nginx/1.20.1") resp.Header.Set("Server", "nginx/1.20.1")
resp.Header.Set("Date", time.Now().Format(http.TimeFormat)) resp.Header.Set("Date", time.Now().Format(http.TimeFormat))
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {

View File

@ -1,11 +1,15 @@
package http2 package http2
import ( import (
"bufio"
"bytes"
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"errors" "errors"
"hash/crc32" "hash/crc32"
"io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
@ -130,33 +134,22 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
w.Header().Set("Proxy-Agent", h.md.proxyAgent) w.Header().Set("Proxy-Agent", h.md.proxyAgent)
} }
/*
if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) {
log.Logf("[http2] %s - %s : Unauthorized to tcp connect to %s",
r.RemoteAddr, laddr, host)
w.WriteHeader(http.StatusForbidden)
return
}
*/
if h.options.Bypass != nil && h.options.Bypass.Contains(addr) { if h.options.Bypass != nil && h.options.Bypass.Contains(addr) {
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
h.logger.Info("bypass: ", addr) h.logger.Info("bypass: ", addr)
return return
} }
/* resp := &http.Response{
resp := &http.Response{ ProtoMajor: 2,
ProtoMajor: 2, ProtoMinor: 0,
ProtoMinor: 0, Header: http.Header{},
Header: http.Header{}, Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})), }
}
if !h.authenticate(w, r, resp) { if !h.authenticate(w, req, resp) {
return return
} }
*/
// delete the proxy related headers. // delete the proxy related headers.
req.Header.Del("Proxy-Authorization") req.Header.Del("Proxy-Authorization")
@ -248,17 +241,16 @@ func (h *http2Handler) basicProxyAuth(proxyAuth string) (username, password stri
return cs[:s], cs[s+1:], true return cs[:s], cs[s+1:], true
} }
func (h *http2Handler) authenticate(conn net.Conn, req *http.Request, resp *http.Response) (ok bool) { func (h *http2Handler) authenticate(w http.ResponseWriter, r *http.Request, resp *http.Response) (ok bool) {
u, p, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization")) u, p, _ := h.basicProxyAuth(r.Header.Get("Proxy-Authorization"))
if h.authenticator == nil || h.authenticator.Authenticate(u, p) { if h.authenticator == nil || h.authenticator.Authenticate(u, p) {
return true return true
} }
pr := h.md.probeResist pr := h.md.probeResistance
// probing resistance is enabled, and knocking host is mismatch. // probing resistance is enabled, and knocking host is mismatch.
if pr != nil && (pr.Knock == "" || !strings.EqualFold(req.URL.Hostname(), pr.Knock)) { if pr != nil && (pr.Knock == "" || !strings.EqualFold(r.URL.Hostname(), pr.Knock)) {
resp.StatusCode = http.StatusServiceUnavailable // default status code resp.StatusCode = http.StatusServiceUnavailable // default status code
switch pr.Type { switch pr.Type {
case "code": case "code":
resp.StatusCode, _ = strconv.Atoi(pr.Value) resp.StatusCode, _ = strconv.Atoi(pr.Value)
@ -267,22 +259,30 @@ func (h *http2Handler) authenticate(conn net.Conn, req *http.Request, resp *http
if !strings.HasPrefix(url, "http") { if !strings.HasPrefix(url, "http") {
url = "http://" + url url = "http://" + url
} }
if r, err := http.Get(url); err == nil { r, err := http.Get(url)
resp = r if err != nil {
defer r.Body.Close() h.logger.Error(err)
break
} }
resp = r
defer resp.Body.Close()
case "host": case "host":
cc, err := net.Dial("tcp", pr.Value) cc, err := net.Dial("tcp", pr.Value)
if err == nil { if err != nil {
defer cc.Close() h.logger.Error(err)
break
req.Write(cc)
handler.Transport(conn, cc)
return
} }
defer cc.Close()
if err := h.forwardRequest(w, r, cc); err != nil {
h.logger.Error(err)
}
return
case "file": case "file":
f, _ := os.Open(pr.Value) f, _ := os.Open(pr.Value)
if f != nil { if f != nil {
defer f.Close()
resp.StatusCode = http.StatusOK resp.StatusCode = http.StatusOK
if finfo, _ := f.Stat(); finfo != nil { if finfo, _ := f.Stat(); finfo != nil {
resp.ContentLength = finfo.Size() resp.ContentLength = finfo.Size()
@ -296,7 +296,7 @@ func (h *http2Handler) authenticate(conn net.Conn, req *http.Request, resp *http
if resp.StatusCode == 0 { if resp.StatusCode == 0 {
resp.StatusCode = http.StatusProxyAuthRequired resp.StatusCode = http.StatusProxyAuthRequired
resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"") resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
if strings.ToLower(req.Header.Get("Proxy-Connection")) == "keep-alive" { if strings.ToLower(r.Header.Get("Proxy-Connection")) == "keep-alive" {
// XXX libcurl will keep sending auth request in same conn // XXX libcurl will keep sending auth request in same conn
// which we don't supported yet. // which we don't supported yet.
resp.Header.Add("Connection", "close") resp.Header.Add("Connection", "close")
@ -318,6 +318,31 @@ func (h *http2Handler) authenticate(conn net.Conn, req *http.Request, resp *http
h.logger.Debug(string(dump)) h.logger.Debug(string(dump))
} }
resp.Write(conn) h.writeResponse(w, resp)
return return
} }
func (h *http2Handler) forwardRequest(w http.ResponseWriter, r *http.Request, rw io.ReadWriter) (err error) {
if err = r.Write(rw); err != nil {
return
}
resp, err := http.ReadResponse(bufio.NewReader(rw), r)
if err != nil {
return
}
defer resp.Body.Close()
return h.writeResponse(w, resp)
}
func (h *http2Handler) writeResponse(w http.ResponseWriter, resp *http.Response) error {
for k, v := range resp.Header {
for _, vv := range v {
w.Header().Add(k, vv)
}
}
w.WriteHeader(resp.StatusCode)
_, err := io.Copy(flushWriter{w}, resp.Body)
return err
}

View File

@ -7,16 +7,16 @@ import (
) )
type metadata struct { type metadata struct {
proxyAgent string proxyAgent string
probeResist *probeResist probeResistance *probeResistance
sni bool sni bool
enableUDP bool enableUDP bool
} }
func (h *http2Handler) parseMetadata(md mdata.Metadata) error { func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
const ( const (
proxyAgent = "proxyAgent" proxyAgent = "proxyAgent"
probeResistKey = "probeResist" probeResistKey = "probeResistance"
knock = "knock" knock = "knock"
sni = "sni" sni = "sni"
enableUDP = "udp" enableUDP = "udp"
@ -26,7 +26,7 @@ func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
if v := mdata.GetString(md, probeResistKey); v != "" { if v := mdata.GetString(md, probeResistKey); v != "" {
if ss := strings.SplitN(v, ":", 2); len(ss) == 2 { if ss := strings.SplitN(v, ":", 2); len(ss) == 2 {
h.md.probeResist = &probeResist{ h.md.probeResistance = &probeResistance{
Type: ss[0], Type: ss[0],
Value: ss[1], Value: ss[1],
Knock: mdata.GetString(md, knock), Knock: mdata.GetString(md, knock),
@ -39,7 +39,7 @@ func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
return nil return nil
} }
type probeResist struct { type probeResistance struct {
Type string Type string
Value string Value string
Knock string Knock string

View File

@ -66,18 +66,22 @@ func (c *ClientConn) SetWriteDeadline(t time.Time) error {
// a dummy HTTP2 server conn used by HTTP2 handler // a dummy HTTP2 server conn used by HTTP2 handler
type ServerConn struct { type ServerConn struct {
r *http.Request r *http.Request
w http.ResponseWriter w http.ResponseWriter
cancel context.CancelFunc localAddr net.Addr
remoteAddr net.Addr
cancel context.CancelFunc
} }
func NewServerConn(w http.ResponseWriter, r *http.Request) *ServerConn { func NewServerConn(w http.ResponseWriter, r *http.Request, localAddr, remoteAddr net.Addr) *ServerConn {
ctx, cancel := context.WithCancel(r.Context()) ctx, cancel := context.WithCancel(r.Context())
return &ServerConn{ return &ServerConn{
r: r.Clone(ctx), r: r.Clone(ctx),
w: w, w: w,
cancel: cancel, localAddr: localAddr,
remoteAddr: remoteAddr,
cancel: cancel,
} }
} }
@ -112,13 +116,11 @@ func (c *ServerConn) Close() error {
} }
func (c *ServerConn) LocalAddr() net.Addr { func (c *ServerConn) LocalAddr() net.Addr {
addr, _ := net.ResolveTCPAddr("tcp", c.r.Host) return c.localAddr
return addr
} }
func (c *ServerConn) RemoteAddr() net.Addr { func (c *ServerConn) RemoteAddr() net.Addr {
addr, _ := net.ResolveTCPAddr("tcp", c.r.RemoteAddr) return c.remoteAddr
return addr
} }
func (c *ServerConn) SetDeadline(t time.Time) error { func (c *ServerConn) SetDeadline(t time.Time) error {

View File

@ -106,7 +106,8 @@ func (l *http2Listener) Close() (err error) {
} }
func (l *http2Listener) handleFunc(w http.ResponseWriter, r *http.Request) { func (l *http2Listener) handleFunc(w http.ResponseWriter, r *http.Request) {
conn := http2_util.NewServerConn(w, r) raddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr)
conn := http2_util.NewServerConn(w, r, l.addr, raddr)
select { select {
case l.cqueue <- conn: case l.cqueue <- conn:
default: default: