From c31358259f4dc94ce49aa85642178becaec0f79b Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Tue, 2 Jun 2015 12:53:15 +0100 Subject: [PATCH 1/9] [proxy] Add tls support Caveats: * TLS files must be present on the target server (and readable) * TLS file paths cannot contain spaces * TLS file paths must be absolute (i.e. cannot be relative) * Bash argument parsing is terrible --- .gitignore | 1 + Makefile | 1 + prog/weaveproxy/main.go | 38 ++++++++++++------ proxy/proxy.go | 39 ++++++++++++++---- proxy/tls.go | 76 +++++++++++++++++++++++++++++++++++ test/670_proxy_tls_test.sh | 18 +++++++++ test/setup.sh | 5 ++- test/tls/generate_certs.go | 81 ++++++++++++++++++++++++++++++++++++++ weave | 52 +++++++++++++++++++++++- 9 files changed, 290 insertions(+), 21 deletions(-) create mode 100644 proxy/tls.go create mode 100755 test/670_proxy_tls_test.sh create mode 100644 test/tls/generate_certs.go diff --git a/.gitignore b/.gitignore index 40c314a9..6cdb2e7b 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ prog/weaveexec/weavewait prog/weaveexec/sigproxy prog/weaveexec/docker*.tgz Vagrantfile.local +test/tls/*.pem diff --git a/Makefile b/Makefile index c9c6579c..07b84f91 100644 --- a/Makefile +++ b/Makefile @@ -111,6 +111,7 @@ publish: $(PUBLISH) clean: -$(SUDO) docker rmi $(IMAGES) rm -f $(EXES) $(EXPORTS) + rm -f test/tls/*.pem build: $(SUDO) go clean -i net diff --git a/prog/weaveproxy/main.go b/prog/weaveproxy/main.go index 8b43ee6f..eaa669b8 100644 --- a/prog/weaveproxy/main.go +++ b/prog/weaveproxy/main.go @@ -16,13 +16,25 @@ var ( ) func main() { - var target, listen string - var withDNS, withIPAM, debug, justVersion bool + var ( + debug bool + justVersion bool + listen string + target string + tlsConfig proxy.TLSConfig + withDNS bool + withIPAM bool + ) getopt.BoolVarLong(&debug, "debug", 'd', "log debugging information") getopt.BoolVarLong(&justVersion, "version", 0, "print version and exit") - getopt.StringVar(&target, 'H', fmt.Sprintf("docker daemon URL to proxy (default %s)", defaultTarget)) getopt.StringVar(&listen, 'L', fmt.Sprintf("address on which to listen (default %s)", defaultListen)) + getopt.StringVar(&target, 'H', fmt.Sprintf("docker daemon URL to proxy (default %s)", defaultTarget)) + getopt.StringVarLong(&tlsConfig.CACert, "tlscacert", 0, "Trust certs signed only by this CA") + getopt.StringVarLong(&tlsConfig.Cert, "tlscert", 0, "Path to TLS certificate file") + getopt.BoolVarLong(&tlsConfig.Enabled, "tls", 0, "Use TLS; implied by --tlsverify") + getopt.StringVarLong(&tlsConfig.Key, "tlskey", 0, "Path to TLS key file") + getopt.BoolVarLong(&tlsConfig.Verify, "tlsverify", 0, "Use TLS and verify the remote") getopt.BoolVarLong(&withDNS, "with-dns", 'w', "instruct created containers to use weaveDNS as their nameserver") getopt.BoolVarLong(&withIPAM, "with-ipam", 'i', "automatically allocate addresses for containers without a WEAVE_CIDR") getopt.Parse() @@ -32,6 +44,10 @@ func main() { os.Exit(0) } + if debug { + InitDefaultLogging(true) + } + if target == "" { target = defaultTarget } @@ -40,18 +56,18 @@ func main() { listen = defaultListen } - if debug { - InitDefaultLogging(true) - } - - p, err := proxy.NewProxy(version, target, listen, withDNS, withIPAM) + p, err := proxy.NewProxy( + version, + target, + listen, + withDNS, + withIPAM, + &tlsConfig, + ) if err != nil { Error.Fatalf("Could not start proxy: %s", err) } - Info.Printf("Listening on %s", listen) - Info.Printf("Proxying %s", target) - if err := p.ListenAndServe(); err != nil { Error.Fatalf("Could not listen on %s: %s", listen, err) } diff --git a/proxy/proxy.go b/proxy/proxy.go index 492a2cdb..42c4be69 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -2,6 +2,7 @@ package proxy import ( "bytes" + "crypto/tls" "fmt" "net" "net/http" @@ -13,6 +14,12 @@ import ( . "github.com/weaveworks/weave/common" ) +const ( + defaultCaFile = "ca.pem" + defaultKeyFile = "key.pem" + defaultCertFile = "cert.pem" +) + var ( containerCreateRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/create$") containerStartRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/[^/]*/(re)?start$") @@ -24,13 +31,14 @@ type Proxy struct { version string client *docker.Client dockerAddr string - listenAddr string - withDNS bool dockerBridgeIP string + listenAddr string + tlsConfig *TLSConfig + withDNS bool withIPAM bool } -func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool) (*Proxy, error) { +func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool, tlsConfig *TLSConfig) (*Proxy, error) { u, err := url.Parse(dockerAddr) if err != nil { return nil, err @@ -44,6 +52,10 @@ func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool) (* } } + if err := tlsConfig.loadCerts(); err != nil { + Error.Fatalf("Could not configure tls for proxy: %s", err) + } + client, err := docker.NewClient(dockerAddr) if err != nil { return nil, err @@ -65,8 +77,9 @@ func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool) (* client: client, dockerAddr: dockerAddr, listenAddr: listenAddr, - withDNS: withDNS, dockerBridgeIP: string(dockerBridgeIP), + tlsConfig: tlsConfig, + withDNS: withDNS, withIPAM: withIPAM, }, nil } @@ -112,8 +125,18 @@ func (proxy *Proxy) Status() string { } func (proxy *Proxy) ListenAndServe() error { - return (&http.Server{ - Addr: proxy.listenAddr, - Handler: proxy, - }).ListenAndServe() + listener, err := net.Listen("tcp", proxy.listenAddr) + if err != nil { + return err + } + + if proxy.tlsConfig.enabled() { + listener = tls.NewListener(listener, proxy.tlsConfig.Config) + Info.Println("TLS Enabled") + } + + Info.Printf("Listening on %s", proxy.listenAddr) + Info.Printf("Proxying %s", proxy.dockerAddr) + + return (&http.Server{Handler: proxy}).Serve(listener) } diff --git a/proxy/tls.go b/proxy/tls.go new file mode 100644 index 00000000..229e0ce3 --- /dev/null +++ b/proxy/tls.go @@ -0,0 +1,76 @@ +package proxy + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/docker/docker/pkg/homedir" +) + +type TLSConfig struct { + Enabled, Verify bool + Cert, Key, CACert string + *tls.Config +} + +func (c *TLSConfig) enabled() bool { + if c == nil { + return false + } + return c.Enabled || c.Verify +} + +func (c *TLSConfig) loadCerts() error { + if !c.enabled() { + return nil + } + + dockerCertPath := os.Getenv("DOCKER_CERT_PATH") + if dockerCertPath == "" { + dockerCertPath = filepath.Join(homedir.Get(), ".docker") + } + + if c.CACert == "" { + c.CACert = filepath.Join(dockerCertPath, defaultCaFile) + } + if c.Cert == "" { + c.Cert = filepath.Join(dockerCertPath, defaultCertFile) + } + if c.Key == "" { + c.Key = filepath.Join(dockerCertPath, defaultKeyFile) + } + + tlsConfig := &tls.Config{ + NextProtos: []string{"http/1.1"}, + // Avoid fallback on insecure SSL protocols + MinVersion: tls.VersionTLS10, + } + + if c.Verify { + certPool := x509.NewCertPool() + file, err := ioutil.ReadFile(c.CACert) + if err != nil { + return fmt.Errorf("Couldn't read CA certificate: %v", err) + } + certPool.AppendCertsFromPEM(file) + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert + tlsConfig.ClientCAs = certPool + } + + _, errCert := os.Stat(c.Cert) + _, errKey := os.Stat(c.Key) + if errCert == nil && errKey == nil { + cert, err := tls.LoadX509KeyPair(c.Cert, c.Key) + if err != nil { + return fmt.Errorf("Couldn't load X509 key pair: %q. Make sure the key is encrypted", err) + } + tlsConfig.Certificates = []tls.Certificate{cert} + } + + c.Config = tlsConfig + return nil +} diff --git a/test/670_proxy_tls_test.sh b/test/670_proxy_tls_test.sh new file mode 100755 index 00000000..1d284133 --- /dev/null +++ b/test/670_proxy_tls_test.sh @@ -0,0 +1,18 @@ +#! /bin/bash + +. ./config.sh + +start_suite "Boot the proxy with TLS-enabled Docker support" + +PWD=$($SSH $HOST1 pwd) + +weave_on $HOST1 launch +weave_on $HOST1 launch-proxy \ + --tlsverify \ + --tlscacert $PWD/tls/ca.pem \ + --tlscert $PWD/tls/$HOST1.pem \ + --tlskey $PWD/tls/$HOST1-key.pem + +assert_raises "DOCKER_CERT_PATH=./tls proxy docker_on $HOST1 --tlsverify ps" + +end_suite diff --git a/test/setup.sh b/test/setup.sh index 9aae4c46..be7da148 100755 --- a/test/setup.sh +++ b/test/setup.sh @@ -4,7 +4,9 @@ set -e . ./config.sh -echo Copying weave images and script to hosts +(cd ./tls && go get ./... && go run generate_certs.go $HOSTS) + +echo Copying weave images, scripts, and certificates to hosts for HOST in $HOSTS; do docker_on $HOST load -i ../weave.tar docker_on $HOST load -i ../weavedns.tar @@ -13,5 +15,6 @@ for HOST in $HOSTS; do cat ../bin/docker-ns | run_on $HOST sh -c "cat > $DOCKER_NS" cat ../weave | run_on $HOST sh -c "cat > ./weave" run_on $HOST chmod a+x $DOCKER_NS ./weave + rsync -az -e "$SSH" ./tls/ $HOST:~/tls run_on $HOST sudo service docker restart done diff --git a/test/tls/generate_certs.go b/test/tls/generate_certs.go new file mode 100644 index 00000000..d2389baf --- /dev/null +++ b/test/tls/generate_certs.go @@ -0,0 +1,81 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/docker/machine/utils" +) + +const ( + org = "vagrant" + bits = 2048 + caKeyPath = "ca-key.pem" + caCertPath = "ca.pem" + clientKeyPath = "key.pem" + clientCertPath = "cert.pem" + serverKeyPath = "server-key.pem" + serverCertPath = "server.pem" +) + +func main() { + if _, err := os.Stat(caCertPath); os.IsNotExist(err) { + log.Printf("Creating CA: %s", caCertPath) + + // check if the key path exists; if so, error + if _, err := os.Stat(caKeyPath); err == nil { + log.Fatalf("The CA key already exists. Please remove it or specify a different key/cert.") + } + + if err := utils.GenerateCACertificate(caCertPath, caKeyPath, org, bits); err != nil { + log.Printf("Error generating CA certificate: %s", err) + } + } + + if _, err := os.Stat(clientCertPath); os.IsNotExist(err) { + log.Printf("Creating client certificate: %s", clientCertPath) + + // check if the key path exists; if so, error + if _, err := os.Stat(clientKeyPath); err == nil { + log.Fatalf("The client key already exists. Please remove it or specify a different key/cert.") + } + + err = utils.GenerateCert( + []string{""}, + clientCertPath, + clientKeyPath, + caCertPath, + caKeyPath, + org, + bits, + ) + if err != nil { + log.Fatalf("Error generating client certificate: %s", err) + } + } + + if len(os.Args) <= 1 { + return + } + for _, ip := range os.Args[1:] { + serverKeyPath := fmt.Sprintf("%s-key.pem", ip) + serverCertPath := fmt.Sprintf("%s.pem", ip) + + if _, err := os.Stat(serverCertPath); os.IsNotExist(err) { + log.Printf("Creating server certificate: %s", serverCertPath) + err = utils.GenerateCert( + []string{ip}, + serverCertPath, + serverKeyPath, + caCertPath, + caKeyPath, + org, + bits, + ) + if err != nil { + log.Fatalf("error generating server cert: %s", err) + } + } + } +} diff --git a/weave b/weave index 9811680b..8aecd8c1 100755 --- a/weave +++ b/weave @@ -746,6 +746,53 @@ get_ipam_cidr() { fi } +###################################################################### +# weave proxy helpers +###################################################################### + + +# TODO: Handle relative paths for args +# TODO: Handle args with spaces +proxy_args() { + PROXY_VOLUMES="" + PROXY_ARGS="" + while [ $# -gt 0 ]; do + case "$1" in + --tlscacert) + shift + PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/ca.pem" + PROXY_ARGS="$PROXY_ARGS --tlscacert /home/weave/tls/ca.pem" + ;; + --tlscacert=*) + PROXY_VOLUMES="$PROXY_VOLUMES -v ${1#*=}:/home/weave/tls/ca.pem" + PROXY_ARGS="$PROXY_ARGS --tlscacert /home/weave/tls/ca.pem" + ;; + --tlscert) + shift + PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/cert.pem" + PROXY_ARGS="$PROXY_ARGS --tlscert /home/weave/tls/cert.pem" + ;; + --tlscert=*) + PROXY_VOLUMES="$PROXY_VOLUMES -v ${1#*=}:/home/weave/tls/cert.pem" + PROXY_ARGS="$PROXY_ARGS --tlscert /home/weave/tls/cert.pem" + ;; + --tlskey) + shift + PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/key.pem" + PROXY_ARGS="$PROXY_ARGS --tlskey /home/weave/tls/key.pem" + ;; + --tlskey=*) + PROXY_VOLUMES="$PROXY_VOLUMES -v ${1#*=}:/home/weave/tls/key.pem" + PROXY_ARGS="$PROXY_ARGS --tlskey /home/weave/tls/key.pem" + ;; + *) + PROXY_ARGS="$PROXY_ARGS $1" + ;; + esac + shift + done +} + ########################################################################################## [ $(id -u) = 0 ] || { @@ -880,12 +927,15 @@ case "$COMMAND" in # Set WEAVEPROXY_DOCKER_ARGS in the environment in order to supply # additional parameters, such as resource limits, to docker # when launching the weaveproxy container. + proxy_args "$@" PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \ -p $PROXY_PORT:$PROXY_PORT/tcp \ -v /var/run/docker.sock:/var/run/docker.sock -v /proc:/hostproc \ -e PROCFS=/hostproc \ + $PROXY_VOLUMES \ --entrypoint=/home/weave/weaveproxy \ - $WEAVEPROXY_DOCKER_ARGS $EXEC_IMAGE "$@") + $WEAVEPROXY_DOCKER_ARGS $EXEC_IMAGE $PROXY_ARGS) + wait_for_status_ip $PROXY_CONTAINER_NAME 127.0.0.1 $PROXY_PORT echo $PROXY_CONTAINER ;; From c0cf9e509c05d0b288e1e407d7545092dd9fcf32 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Thu, 4 Jun 2015 10:25:05 +0100 Subject: [PATCH 2/9] [proxy] Added proxy.Config struct for initialization --- prog/weaveproxy/main.go | 50 ++++++++------------ proxy/proxy.go | 100 ++++++++++++++++++++-------------------- 2 files changed, 70 insertions(+), 80 deletions(-) diff --git a/prog/weaveproxy/main.go b/prog/weaveproxy/main.go index eaa669b8..6b86128f 100644 --- a/prog/weaveproxy/main.go +++ b/prog/weaveproxy/main.go @@ -10,33 +10,30 @@ import ( ) var ( - version = "(unreleased version)" - defaultTarget = "unix:///var/run/docker.sock" - defaultListen = ":12375" + version = "(unreleased version)" + defaultDockerAddr = "unix:///var/run/docker.sock" + defaultListenAddr = ":12375" ) func main() { var ( debug bool justVersion bool - listen string - target string - tlsConfig proxy.TLSConfig - withDNS bool - withIPAM bool + c proxy.Config ) + c.Version = version getopt.BoolVarLong(&debug, "debug", 'd', "log debugging information") getopt.BoolVarLong(&justVersion, "version", 0, "print version and exit") - getopt.StringVar(&listen, 'L', fmt.Sprintf("address on which to listen (default %s)", defaultListen)) - getopt.StringVar(&target, 'H', fmt.Sprintf("docker daemon URL to proxy (default %s)", defaultTarget)) - getopt.StringVarLong(&tlsConfig.CACert, "tlscacert", 0, "Trust certs signed only by this CA") - getopt.StringVarLong(&tlsConfig.Cert, "tlscert", 0, "Path to TLS certificate file") - getopt.BoolVarLong(&tlsConfig.Enabled, "tls", 0, "Use TLS; implied by --tlsverify") - getopt.StringVarLong(&tlsConfig.Key, "tlskey", 0, "Path to TLS key file") - getopt.BoolVarLong(&tlsConfig.Verify, "tlsverify", 0, "Use TLS and verify the remote") - getopt.BoolVarLong(&withDNS, "with-dns", 'w', "instruct created containers to use weaveDNS as their nameserver") - getopt.BoolVarLong(&withIPAM, "with-ipam", 'i', "automatically allocate addresses for containers without a WEAVE_CIDR") + getopt.StringVar(&c.ListenAddr, 'L', fmt.Sprintf("address on which to listen (default %s)", defaultListenAddr)) + getopt.StringVar(&c.DockerAddr, 'H', fmt.Sprintf("docker daemon URL to proxy (default %s)", defaultDockerAddr)) + 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") + getopt.BoolVarLong(&c.TLSConfig.Enabled, "tls", 0, "Use TLS; implied by --tlsverify") + getopt.StringVarLong(&c.TLSConfig.Key, "tlskey", 0, "Path to TLS key file") + getopt.BoolVarLong(&c.TLSConfig.Verify, "tlsverify", 0, "Use TLS and verify the remote") + getopt.BoolVarLong(&c.WithDNS, "with-dns", 'w', "instruct created containers to use weaveDNS as their nameserver") + getopt.BoolVarLong(&c.WithIPAM, "with-ipam", 'i', "automatically allocate addresses for containers without a WEAVE_CIDR") getopt.Parse() if justVersion { @@ -48,27 +45,20 @@ func main() { InitDefaultLogging(true) } - if target == "" { - target = defaultTarget + if c.DockerAddr == "" { + c.DockerAddr = defaultDockerAddr } - if listen == "" { - listen = defaultListen + if c.ListenAddr == "" { + c.ListenAddr = defaultListenAddr } - p, err := proxy.NewProxy( - version, - target, - listen, - withDNS, - withIPAM, - &tlsConfig, - ) + 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", listen, err) + Error.Fatalf("Could not listen on %s: %s", p.ListenAddr, err) } } diff --git a/proxy/proxy.go b/proxy/proxy.go index 42c4be69..f48f8305 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -27,36 +27,24 @@ var ( ) type Proxy struct { - Dial func() (net.Conn, error) - version string + Config + + dial func() (net.Conn, error) client *docker.Client - dockerAddr string dockerBridgeIP string - listenAddr string - tlsConfig *TLSConfig - withDNS bool - withIPAM bool } -func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool, tlsConfig *TLSConfig) (*Proxy, error) { - u, err := url.Parse(dockerAddr) - if err != nil { - return nil, err - } +type Config struct { + DockerAddr string + ListenAddr string + TLSConfig TLSConfig + Version string + WithDNS bool + WithIPAM bool +} - var dockerBridgeIP []byte - if withDNS { - dockerBridgeIP, err = callWeave("docker-bridge-ip") - if err != nil { - return nil, err - } - } - - if err := tlsConfig.loadCerts(); err != nil { - Error.Fatalf("Could not configure tls for proxy: %s", err) - } - - client, err := docker.NewClient(dockerAddr) +func NewProxy(c Config) (*Proxy, error) { + u, err := url.Parse(c.DockerAddr) if err != nil { return nil, err } @@ -69,19 +57,31 @@ func NewProxy(version, dockerAddr, listenAddr string, withDNS, withIPAM bool, tl targetAddr = u.Path } - return &Proxy{ - Dial: func() (net.Conn, error) { + p := &Proxy{ + Config: c, + dial: func() (net.Conn, error) { return net.Dial(u.Scheme, targetAddr) }, - version: version, - client: client, - dockerAddr: dockerAddr, - listenAddr: listenAddr, - dockerBridgeIP: string(dockerBridgeIP), - tlsConfig: tlsConfig, - withDNS: withDNS, - withIPAM: withIPAM, - }, nil + } + + if p.WithDNS { + dockerBridgeIP, err := callWeave("docker-bridge-ip") + if err != nil { + return nil, err + } + p.dockerBridgeIP = string(dockerBridgeIP) + } + + if err := p.TLSConfig.loadCerts(); err != nil { + Error.Fatalf("Could not configure tls for proxy: %s", err) + } + + p.client, err = docker.NewClient(p.DockerAddr) + if err != nil { + return nil, err + } + + return p, nil } func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -89,13 +89,13 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path switch { case containerCreateRegexp.MatchString(path): - proxy.serveWithInterceptor(&createContainerInterceptor{proxy.client, proxy.withDNS, proxy.dockerBridgeIP, proxy.withIPAM}, w, r) + proxy.serveWithInterceptor(&createContainerInterceptor{proxy.client, proxy.WithDNS, proxy.dockerBridgeIP, proxy.WithIPAM}, w, r) case containerStartRegexp.MatchString(path): - proxy.serveWithInterceptor(&startContainerInterceptor{proxy.client, proxy.withDNS, proxy.withIPAM}, w, r) + proxy.serveWithInterceptor(&startContainerInterceptor{proxy.client, proxy.WithDNS, proxy.WithIPAM}, w, r) case execCreateRegexp.MatchString(path): - proxy.serveWithInterceptor(&createExecInterceptor{proxy.client, proxy.withIPAM}, w, r) + proxy.serveWithInterceptor(&createExecInterceptor{proxy.client, proxy.WithIPAM}, w, r) case strings.HasPrefix(path, "/status"): - fmt.Fprintln(w, "weave proxy", proxy.version) + fmt.Fprintln(w, "weave proxy", proxy.Version) fmt.Fprintln(w, proxy.Status()) default: proxy.serveWithInterceptor(&nullInterceptor{}, w, r) @@ -103,20 +103,20 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func (proxy *Proxy) serveWithInterceptor(i interceptor, w http.ResponseWriter, r *http.Request) { - newClient(proxy.Dial, i).ServeHTTP(w, r) + newClient(proxy.dial, i).ServeHTTP(w, r) } // Return status string func (proxy *Proxy) Status() string { var buf bytes.Buffer - fmt.Fprintln(&buf, "Listen address is", proxy.listenAddr) - fmt.Fprintln(&buf, "Docker address is", proxy.dockerAddr) - if proxy.withDNS { + fmt.Fprintln(&buf, "Listen address is", proxy.ListenAddr) + fmt.Fprintln(&buf, "Docker address is", proxy.DockerAddr) + if proxy.WithDNS { fmt.Fprintln(&buf, "DNS on") } else { fmt.Fprintln(&buf, "DNS off") } - if proxy.withIPAM { + if proxy.WithIPAM { fmt.Fprintln(&buf, "IPAM on") } else { fmt.Fprintln(&buf, "IPAM off") @@ -125,18 +125,18 @@ func (proxy *Proxy) Status() string { } func (proxy *Proxy) ListenAndServe() error { - listener, err := net.Listen("tcp", proxy.listenAddr) + listener, err := net.Listen("tcp", proxy.ListenAddr) if err != nil { return err } - if proxy.tlsConfig.enabled() { - listener = tls.NewListener(listener, proxy.tlsConfig.Config) + if proxy.TLSConfig.enabled() { + listener = tls.NewListener(listener, proxy.TLSConfig.Config) Info.Println("TLS Enabled") } - Info.Printf("Listening on %s", proxy.listenAddr) - Info.Printf("Proxying %s", proxy.dockerAddr) + Info.Printf("Listening on %s", proxy.ListenAddr) + Info.Printf("Proxying %s", proxy.DockerAddr) return (&http.Server{Handler: proxy}).Serve(listener) } From a1ffbb528c825b2b933cb10f061847f2196ab249 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Thu, 4 Jun 2015 14:25:21 +0100 Subject: [PATCH 3/9] adding documentation on using the proxy with tls --- site/proxy.md | 48 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/site/proxy.md b/site/proxy.md index ed1727d1..9819f34c 100644 --- a/site/proxy.md +++ b/site/proxy.md @@ -5,6 +5,16 @@ layout: default # Weave Proxy + * [Advantages](#advantages) + * [Setup](#setup) + * [Usage](#usage) + * [Usage with IPAM](#usage-with-ipam) + * [Usage with WeaveDNS](#usage-with-weavedns) + * [Usage with TLS](#usage-with-tls) + * [Limitations](#limitations) + +## Advantages + Instead of the `weave` command-line utility, you may prefer to use the standard [Docker command-line interface](https://docs.docker.com/reference/commandline/cli/), or the @@ -20,7 +30,7 @@ starting a container's application process. Furthermore, containers can be started in foreground mode, and can be automatically removed (with the ususal `--rm`). -## Setup +## Setup Start the proxy with @@ -35,7 +45,7 @@ your `DOCKER_HOST`. host1$ export DOCKER_HOST=tcp://host1:12375 host1$ docker ps -## Usage +## Usage When containers are created via the weave proxy, their entrypoint will be modified to wait for the weave network interface to become @@ -56,7 +66,7 @@ Multiple IP addresses and networks can be supplied in the `WEAVE_CIDR` variable by space-separating them, as in `WEAVE_CIDR="10.2.1.1/24 10.2.2.1/24"`. -## Usage with IPAM +## Usage with IPAM To automatically assign a unique IP address to a container, weave must be told on startup what range of addresses to allocate from. For @@ -86,7 +96,7 @@ and attached to the weave network when we start them. For example: More details on IPAM can be found in the [IPAM documentation](ipam.html). -## Usage with WeaveDNS +## Usage with WeaveDNS Containers started via the proxy can be automatically configured to use WeaveDNS for name resolution. To accomplish this we need to launch @@ -102,7 +112,33 @@ resolution. WeaveDNS is used in addition to any dns servers specified via the `--dns` option. More details on weaveDNS can be found in the [weaveDNS documentation](weavedns.html). -## Limitations +## Usage with TLS + +If you are using docker with TLS enabled, you probably want to talk to +the proxy over TLS. Launching the proxy with TLS support is done +with the same command-line flags as the docker daemon. For example, if +you have generated your certificates and keys into the docker host's +`/tls` directory, we can launch the proxy with: + + host1$ weave launch-proxy --tlsverify --tlscacert=/tls/ca.pem \ + --tlscert=/tls/server-cert.pem --tlskey=/tls/server-key.pem + +The paths to your certificates and key, must be provided as absolute +paths which exist on the docker host. + +With the proxy running over TLS, we can configure our regular docker +client + + host1$ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem \ + --tlskey=key.pem -H=tcp://host1:12375 version + +The certificates and keys used by the client will need to be present +on your client machine. + +More details on generating certificates, and configuring docker with +TLS can be found in the [docker +documentation](https://docs.docker.com/articles/https/). + +## Limitations -* The proxy does not currently support TLS. * If you have a firewall, you will need to make sure port 12375 is open. From fa6707effaaa7aa474e14048d598a32d6f7b42b3 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Thu, 4 Jun 2015 17:33:52 +0100 Subject: [PATCH 4/9] [docs] adding paragraph about docker listening on unix socket for proxy --- site/proxy.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/site/proxy.md b/site/proxy.md index 9819f34c..b315b961 100644 --- a/site/proxy.md +++ b/site/proxy.md @@ -123,7 +123,7 @@ you have generated your certificates and keys into the docker host's host1$ weave launch-proxy --tlsverify --tlscacert=/tls/ca.pem \ --tlscert=/tls/server-cert.pem --tlskey=/tls/server-key.pem -The paths to your certificates and key, must be provided as absolute +The paths to your certificates and key must be provided as absolute paths which exist on the docker host. With the proxy running over TLS, we can configure our regular docker @@ -135,6 +135,15 @@ client The certificates and keys used by the client will need to be present on your client machine. +Because the proxy connects to the docker daemon at +`unix:///var/run/docker.sock`, you must ensure that the daemon is +listening there. To do this, you will need to pass the `-H +unix:///var/run/docker.sock` option when booting the docker daemon, in +addition to the `-H` options for configuring the TCP listener. Passing +multiple `-H` options to the docker daemon is documented on the +[docker basics +documentation](https://docs.docker.com/articles/basics/). + More details on generating certificates, and configuring docker with TLS can be found in the [docker documentation](https://docs.docker.com/articles/https/). From b8595b9062143ee0be314a313a55fb199f33d4a3 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Fri, 5 Jun 2015 11:58:05 +0100 Subject: [PATCH 5/9] [proxy] cleaner default args --- prog/weaveproxy/main.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/prog/weaveproxy/main.go b/prog/weaveproxy/main.go index 6b86128f..bc19d365 100644 --- a/prog/weaveproxy/main.go +++ b/prog/weaveproxy/main.go @@ -19,7 +19,10 @@ func main() { var ( debug bool justVersion bool - c proxy.Config + c = proxy.Config{ + DockerAddr: defaultDockerAddr, + ListenAddr: defaultListenAddr, + } ) c.Version = version @@ -45,14 +48,6 @@ func main() { InitDefaultLogging(true) } - if c.DockerAddr == "" { - c.DockerAddr = defaultDockerAddr - } - - if c.ListenAddr == "" { - c.ListenAddr = defaultListenAddr - } - p, err := proxy.NewProxy(c) if err != nil { Error.Fatalf("Could not start proxy: %s", err) From 127cffba6ffe0786a2967ebfbe0359156ce10ace Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Fri, 5 Jun 2015 13:38:48 +0100 Subject: [PATCH 6/9] [proxy] add TLS to /status --- proxy/proxy.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proxy/proxy.go b/proxy/proxy.go index f48f8305..67f5b87e 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -111,6 +111,14 @@ func (proxy *Proxy) Status() string { var buf bytes.Buffer fmt.Fprintln(&buf, "Listen address is", proxy.ListenAddr) fmt.Fprintln(&buf, "Docker address is", proxy.DockerAddr) + switch { + case proxy.TLSConfig.Verify: + fmt.Fprintln(&buf, "TLS verify") + case proxy.TLSConfig.Enabled: + fmt.Fprintln(&buf, "TLS on") + default: + fmt.Fprintln(&buf, "TLS off") + } if proxy.WithDNS { fmt.Fprintln(&buf, "DNS on") } else { From bddf79855da8388895edd279ff09b93478c995ad Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Fri, 5 Jun 2015 13:59:30 +0100 Subject: [PATCH 7/9] [proxy] refactor tls arg parsing --- weave | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/weave b/weave index 8aecd8c1..e20394db 100755 --- a/weave +++ b/weave @@ -753,6 +753,12 @@ get_ipam_cidr() { # TODO: Handle relative paths for args # TODO: Handle args with spaces +tls_arg() { + filename=${3:-$2} + PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/$filename.pem" + PROXY_ARGS="$PROXY_ARGS --tls$2 /home/weave/tls/$filename.pem" +} + proxy_args() { PROXY_VOLUMES="" PROXY_ARGS="" @@ -760,30 +766,24 @@ proxy_args() { case "$1" in --tlscacert) shift - PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/ca.pem" - PROXY_ARGS="$PROXY_ARGS --tlscacert /home/weave/tls/ca.pem" + tls_arg "$1" cacert ca ;; --tlscacert=*) - PROXY_VOLUMES="$PROXY_VOLUMES -v ${1#*=}:/home/weave/tls/ca.pem" - PROXY_ARGS="$PROXY_ARGS --tlscacert /home/weave/tls/ca.pem" + tls_arg "${1#*=}" cacert ca ;; --tlscert) shift - PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/cert.pem" - PROXY_ARGS="$PROXY_ARGS --tlscert /home/weave/tls/cert.pem" + tls_arg "$1" cert ;; --tlscert=*) - PROXY_VOLUMES="$PROXY_VOLUMES -v ${1#*=}:/home/weave/tls/cert.pem" - PROXY_ARGS="$PROXY_ARGS --tlscert /home/weave/tls/cert.pem" + tls_arg "${1#*=}" cert ;; --tlskey) shift - PROXY_VOLUMES="$PROXY_VOLUMES -v $1:/home/weave/tls/key.pem" - PROXY_ARGS="$PROXY_ARGS --tlskey /home/weave/tls/key.pem" + tls_arg "$1" key ;; --tlskey=*) - PROXY_VOLUMES="$PROXY_VOLUMES -v ${1#*=}:/home/weave/tls/key.pem" - PROXY_ARGS="$PROXY_ARGS --tlskey /home/weave/tls/key.pem" + tls_arg "${1#*=}" key ;; *) PROXY_ARGS="$PROXY_ARGS $1" From 14cc33c5128b248950c662d39f9a68b31c7a13c4 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Fri, 5 Jun 2015 14:10:38 +0100 Subject: [PATCH 8/9] reordering args to launch-proxy docker command --- weave | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/weave b/weave index e20394db..1c9d3389 100755 --- a/weave +++ b/weave @@ -930,12 +930,12 @@ case "$COMMAND" in proxy_args "$@" PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \ -p $PROXY_PORT:$PROXY_PORT/tcp \ - -v /var/run/docker.sock:/var/run/docker.sock -v /proc:/hostproc \ - -e PROCFS=/hostproc \ $PROXY_VOLUMES \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v /proc:/hostproc \ + -e PROCFS=/hostproc \ --entrypoint=/home/weave/weaveproxy \ $WEAVEPROXY_DOCKER_ARGS $EXEC_IMAGE $PROXY_ARGS) - wait_for_status_ip $PROXY_CONTAINER_NAME 127.0.0.1 $PROXY_PORT echo $PROXY_CONTAINER ;; From edeb531940d81deb9fb8c93219993a195dcd7638 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Fri, 5 Jun 2015 14:11:06 +0100 Subject: [PATCH 9/9] [docs] rewording proxy TLS docs --- site/proxy.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/site/proxy.md b/site/proxy.md index b315b961..9712b288 100644 --- a/site/proxy.md +++ b/site/proxy.md @@ -114,8 +114,8 @@ via the `--dns` option. More details on weaveDNS can be found in the ## Usage with TLS -If you are using docker with TLS enabled, you probably want to talk to -the proxy over TLS. Launching the proxy with TLS support is done +If you are using docker with TLS, you probably want to talk to +the proxy over TLS. Launching the proxy with TLS is done with the same command-line flags as the docker daemon. For example, if you have generated your certificates and keys into the docker host's `/tls` directory, we can launch the proxy with: @@ -126,6 +126,14 @@ you have generated your certificates and keys into the docker host's The paths to your certificates and key must be provided as absolute paths which exist on the docker host. +Because the proxy connects to the docker daemon at +`unix:///var/run/docker.sock`, you must ensure that the daemon is +listening there. To do this, you will need to pass the `-H +unix:///var/run/docker.sock` option when starting the docker daemon, in +addition to the `-H` options for configuring the TCP listener. See +[the Docker +documentation](https://docs.docker.com/articles/basics/#bind-docker-to-another-hostport-or-a-unix-socket) for an example. + With the proxy running over TLS, we can configure our regular docker client @@ -135,15 +143,6 @@ client The certificates and keys used by the client will need to be present on your client machine. -Because the proxy connects to the docker daemon at -`unix:///var/run/docker.sock`, you must ensure that the daemon is -listening there. To do this, you will need to pass the `-H -unix:///var/run/docker.sock` option when booting the docker daemon, in -addition to the `-H` options for configuring the TCP listener. Passing -multiple `-H` options to the docker daemon is documented on the -[docker basics -documentation](https://docs.docker.com/articles/basics/). - More details on generating certificates, and configuring docker with TLS can be found in the [docker documentation](https://docs.docker.com/articles/https/).