diff --git a/prog/weaveproxy/main.go b/prog/weaveproxy/main.go index 76466794..e1d87a7a 100644 --- a/prog/weaveproxy/main.go +++ b/prog/weaveproxy/main.go @@ -11,21 +11,21 @@ import ( ) var ( - version = "(unreleased version)" - defaultListenAddr = "tcp://0.0.0.0:12375" + version = "(unreleased version)" + defaultListenAddrs = []string{"tcp://0.0.0.0:12375", "unix:///var/run/weave.sock"} ) func main() { var ( debug bool justVersion bool - c = proxy.Config{ListenAddr: defaultListenAddr} + c = proxy.Config{ListenAddrs: defaultListenAddrs} ) c.Version = version getopt.BoolVarLong(&debug, "debug", 'd', "log debugging information") getopt.BoolVarLong(&justVersion, "version", 0, "print version and exit") - getopt.StringVar(&c.ListenAddr, 'H', fmt.Sprintf("address on which to listen (default %s)", defaultListenAddr)) + getopt.ListVar(&c.ListenAddrs, 'H', fmt.Sprintf("address on which to listen (default %s)", defaultListenAddrs)) getopt.BoolVarLong(&c.NoDefaultIPAM, "no-default-ipam", 0, "do not automatically allocate addresses for containers without a WEAVE_CIDR") getopt.StringVarLong(&c.TLSConfig.CACert, "tlscacert", 0, "Trust certs signed only by this CA") getopt.StringVarLong(&c.TLSConfig.Cert, "tlscert", 0, "Path to TLS certificate file") @@ -52,22 +52,10 @@ func main() { Info.Println("weave proxy", version) Info.Println("Command line arguments:", strings.Join(os.Args[1:], " ")) - protoAddrParts := strings.SplitN(c.ListenAddr, "://", 2) - if len(protoAddrParts) == 2 { - if protoAddrParts[0] != "tcp" { - Error.Fatalf("Invalid protocol format: %q", protoAddrParts[0]) - } - c.ListenAddr = protoAddrParts[1] - } else { - c.ListenAddr = protoAddrParts[0] - } - p, err := proxy.NewProxy(c) if err != nil { Error.Fatalf("Could not start proxy: %s", err) } - if err := p.ListenAndServe(); err != nil { - Error.Fatalf("Could not listen on %s: %s", p.ListenAddr, err) - } + p.ListenAndServe() } diff --git a/proxy/proxy.go b/proxy/proxy.go index 7191a4a4..a2a756ed 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -2,10 +2,13 @@ package proxy import ( "crypto/tls" + "fmt" "net" "net/http" + "os" "regexp" "strings" + "syscall" "github.com/fsouza/go-dockerclient" . "github.com/weaveworks/weave/common" @@ -15,6 +18,8 @@ const ( defaultCaFile = "ca.pem" defaultKeyFile = "key.pem" defaultCertFile = "cert.pem" + dockerSock = "/var/run/docker.sock" + dockerSockUnix = "unix://" + dockerSock ) var ( @@ -24,7 +29,7 @@ var ( ) type Config struct { - ListenAddr string + ListenAddrs []string NoDefaultIPAM bool TLSConfig TLSConfig Version string @@ -45,7 +50,7 @@ func NewProxy(c Config) (*Proxy, error) { Error.Fatalf("Could not configure tls for proxy: %s", err) } - client, err := docker.NewClient("unix:///var/run/docker.sock") + client, err := docker.NewClient(dockerSockUnix) if err != nil { return nil, err } @@ -63,7 +68,7 @@ func NewProxy(c Config) (*Proxy, error) { } func (proxy *Proxy) Dial() (net.Conn, error) { - return net.Dial("unix", "/var/run/docker.sock") + return net.Dial("unix", dockerSock) } func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -83,19 +88,97 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { proxy.Intercept(i, w, r) } -func (proxy *Proxy) ListenAndServe() error { - listener, err := net.Listen("tcp", proxy.ListenAddr) +func (proxy *Proxy) ListenAndServe() { + listeners := []net.Listener{} + addrs := []string{} + for _, addr := range proxy.ListenAddrs { + listener, normalisedAddr, err := proxy.listen(addr) + if err != nil { + Error.Fatalf("Cannot listen on %s: %s", addr, err) + } + listeners = append(listeners, listener) + addrs = append(addrs, normalisedAddr) + } + + for _, addr := range addrs { + Info.Println("proxy listening on", addr) + } + + errs := make(chan error) + for _, listener := range listeners { + go func(listener net.Listener) { + errs <- (&http.Server{Handler: proxy}).Serve(listener) + }(listener) + } + for range listeners { + err := <-errs + if err != nil { + Error.Fatalf("Serve failed: %s", err) + } + } +} + +func copyOwnerAndPermissions(from, to string) error { + stat, err := os.Stat(from) if err != nil { return err } - - if proxy.TLSConfig.enabled() { - listener = tls.NewListener(listener, proxy.TLSConfig.Config) + if err = os.Chmod(to, stat.Mode()); err != nil { + return err } - Info.Println("proxy listening on", proxy.ListenAddr) + moreStat, ok := stat.Sys().(*syscall.Stat_t) + if !ok { + return nil + } - return (&http.Server{Handler: proxy}).Serve(listener) + if err = os.Chown(to, int(moreStat.Uid), int(moreStat.Gid)); err != nil { + return err + } + + return nil +} + +func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) { + var ( + listener net.Listener + err error + proto, addr string + ) + + if protoAddrParts := strings.SplitN(protoAndAddr, "://", 2); len(protoAddrParts) == 2 { + proto, addr = protoAddrParts[0], protoAddrParts[1] + } else if strings.HasPrefix(protoAndAddr, "/") { + proto, addr = "unix", protoAndAddr + } else { + proto, addr = "tcp", protoAndAddr + } + + switch proto { + case "tcp": + listener, err = net.Listen(proto, addr) + if err != nil { + return nil, "", err + } + if proxy.TLSConfig.enabled() { + listener = tls.NewListener(listener, proxy.TLSConfig.Config) + } + + case "unix": + os.Remove(addr) // remove socket from last invocation + listener, err = net.Listen(proto, addr) + if err != nil { + return nil, "", err + } + if err = copyOwnerAndPermissions(dockerSock, addr); err != nil { + return nil, "", err + } + + default: + Error.Fatalf("Invalid protocol format: %q", proto) + } + + return listener, fmt.Sprintf("%s://%s", proto, addr), nil } func (proxy *Proxy) weaveCIDRsFromConfig(config *docker.Config) ([]string, bool) { diff --git a/site/proxy.md b/site/proxy.md index 0514e30b..3ec5ed76 100644 --- a/site/proxy.md +++ b/site/proxy.md @@ -36,13 +36,14 @@ The first form is more convenient, however you can only pass proxy related configuration arguments to `launch-proxy` so if you need to modify the default behaviour you will have to use the latter. -By default, the proxy listens on port 12375, on all network -interfaces. This can be adjusted with the `-H` argument, e.g. +By default, the proxy listens on /var/run/weave.sock and port 12375, on +all network interfaces. This can be adjusted with the `-H` argument, e.g. host1$ weave launch-proxy -H tcp://127.0.0.1:9999 -If you are working with a remote docker daemon, then any firewalls -inbetween need to be configured to permit access to the proxy port. +Multiple -H arguments can be specified. If you are working with a remote +docker daemon, then any firewalls inbetween need to be configured to permit +access to the proxy port. All docker commands can be run via the proxy, so it is safe to adjust your `DOCKER_HOST` to point at the proxy. Weave provides a convenient diff --git a/weave b/weave index 88609fcc..4326d360 100755 --- a/weave +++ b/weave @@ -58,7 +58,7 @@ usage() { echo "where = [:]" echo " = /" echo " = [ip:] | net: | net:default" - echo " = [tcp://][]:" + echo " = [tcp://][]: | [unix://]/path/to/socket" echo " = or weave internal peer ID" exit 1 } @@ -925,11 +925,7 @@ proxy_args() { proxy_addr() { if addr=$(docker logs $PROXY_CONTAINER_NAME 2>/dev/null | head -n3 | grep -oE "proxy listening on .*"); then - addr=${addr##* } - host=${addr%:*} - [ "$host" = "0.0.0.0" ] && host=$PROXY_HOST - port=${addr#*:} - echo "${1}tcp://${host:-$PROXY_HOST}:${port:-$PROXY_PORT}" + echo "${1}${addr##* }" | sed "s/0.0.0.0/$PROXY_HOST/g" return 0 fi echo "$PROXY_CONTAINER_NAME container is not present. Have you launched it?" >&2 @@ -1035,7 +1031,7 @@ launch_proxy() { proxy_args "$@" PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \ $PROXY_VOLUMES \ - -v /var/run/docker.sock:/var/run/docker.sock \ + -v /var/run:/var/run \ -v /proc:/hostproc \ -e PROCFS=/hostproc \ -e WEAVE_CIDR=none \