From 6aefe40b6dc86e76d77d86f5911a55e50c033254 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Wed, 12 Aug 2015 10:21:46 +0000 Subject: [PATCH] Add socks proxy. --- .gitignore | 2 ++ socks/Dockerfile | 7 ++++ socks/Makefile | 29 ++++++++++++++++ socks/connect.sh | 23 +++++++++++++ socks/main.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+) create mode 100644 socks/Dockerfile create mode 100644 socks/Makefile create mode 100755 socks/connect.sh create mode 100644 socks/main.go diff --git a/.gitignore b/.gitignore index 5cd748c0c..78fe8261e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ cover/cover +socks/proxy +socks/image.tar diff --git a/socks/Dockerfile b/socks/Dockerfile new file mode 100644 index 000000000..867cd6bc5 --- /dev/null +++ b/socks/Dockerfile @@ -0,0 +1,7 @@ +FROM gliderlabs/alpine +MAINTAINER Weaveworks Inc +WORKDIR / +COPY proxy / +EXPOSE 8000 +EXPOSE 8080 +ENTRYPOINT ["/proxy"] diff --git a/socks/Makefile b/socks/Makefile new file mode 100644 index 000000000..2daeda643 --- /dev/null +++ b/socks/Makefile @@ -0,0 +1,29 @@ +.PHONY: all clean + +IMAGE_TAR=image.tar +IMAGE_NAME=weaveworks/socksproxy +PROXY_EXE=proxy +NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \ + rm $@; \ + echo "\nYour go standard library was built without the 'netgo' build tag."; \ + echo "To fix that, run"; \ + echo " sudo go clean -i net"; \ + echo " sudo go install -tags netgo std"; \ + false; \ +} + +all: $(IMAGE_TAR) + +$(IMAGE_TAR): Dockerfile $(PROXY_EXE) + docker build -t $(IMAGE_NAME) . + docker save $(IMAGE_NAME):latest > $@ + +$(PROXY_EXE): *.go + go get -tags netgo ./$(@D) + go build -ldflags "-extldflags \"-static\" -linkmode=external" -tags netgo -o $@ ./$(@D) + $(NETGO_CHECK) + +clean: + -docker rmi $(IMAGE_NAME) + rm -rf $(PROXY_EXE) $(IMAGE_TAR) + go clean ./... diff --git a/socks/connect.sh b/socks/connect.sh new file mode 100755 index 000000000..0d5ef84da --- /dev/null +++ b/socks/connect.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -eu + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +HOST=$1 + +echo "Starting proxy container..." +PROXY_CONTAINER=$(ssh $HOST weave run -d weaveworks/socksproxy) + +function finish { + echo "Removing proxy container.." + ssh $HOST docker rm -f $PROXY_CONTAINER +} +trap finish EXIT + +PROXY_IP=$(ssh $HOST -- "docker inspect --format='{{.NetworkSettings.IPAddress}}' $PROXY_CONTAINER") +echo 'Please configure your browser for proxy http://localhost:8080/proxy.pac' +ssh -L8000:$PROXY_IP:8000 -L8080:$PROXY_IP:8080 $HOST docker attach $PROXY_CONTAINER diff --git a/socks/main.go b/socks/main.go new file mode 100644 index 000000000..c86ded74b --- /dev/null +++ b/socks/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "fmt" + "net" + "net/http" + "strings" + "os" + "text/template" + + socks5 "github.com/armon/go-socks5" + "github.com/weaveworks/weave/common" + "github.com/docker/docker/pkg/mflag" +) + +const ( + pacfile = ` +function FindProxyForURL(url, host) { + if(shExpMatch(host, "*.weave.local")) { + return "SOCKS5 localhost:8000"; + } + {{range $key, $value := .}} + if (host == "{{$key}}") { + return "SOCKS5 localhost:8000"; + } + {{end}} + return "DIRECT"; +} +` +) + +func main() { + var as []string + common.ListVar(&as, []string{"a", "-alias"}, []string{}, "Specify hostname aliases in the form alias:hostname. Can be repeated.") + mflag.Parse() + + var aliases = map[string]string{} + for _, a := range as { + parts := strings.SplitN(a, ":", 2) + if len(parts) != 2 { + fmt.Printf("'%s' is not a valid alias.\n", a) + mflag.Usage() + os.Exit(1) + } + aliases[parts[0]] = parts[1] + } + + go socksProxy(aliases) + + t := template.Must(template.New("pacfile").Parse(pacfile)) + http.HandleFunc("/proxy.pac", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/x-ns-proxy-autoconfig") + t.Execute(w, aliases) + }) + + if err := http.ListenAndServe(":8080", nil); err != nil { + panic(err) + } +} + +type aliasingResolver struct { + aliases map[string]string + socks5.NameResolver +} + +func (r aliasingResolver) Resolve(name string) (net.IP, error) { + if alias, ok := r.aliases[name]; ok { + return r.NameResolver.Resolve(alias) + } + return r.NameResolver.Resolve(name) +} + +func socksProxy(aliases map[string]string) { + conf := &socks5.Config{ + Resolver: aliasingResolver{ + aliases: aliases, + NameResolver: socks5.DNSResolver{}, + }, + } + server, err := socks5.New(conf) + if err != nil { + panic(err) + } + if err := server.ListenAndServe("tcp", ":8000"); err != nil { + panic(err) + } +}