From f55655216f54afd409a2535fd2680bf0b5cee96a Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Wed, 5 Aug 2015 14:30:10 +0100 Subject: [PATCH] [proxy] rewrite /etc/hosts from outside the container Avoids problems when container user is non-root. Added a test for this and the --no-rewrite-hosts flag. --- prog/weavewait/main.go | 112 +--------------------- proxy/create_container_interceptor.go | 6 +- proxy/start_container_interceptor.go | 130 ++++++++++++++++++++++++++ test/610_proxy_wait_for_weave_test.sh | 3 - test/615_proxy_rewrite_hosts_test.sh | 32 +++++++ weave | 6 ++ 6 files changed, 172 insertions(+), 117 deletions(-) create mode 100755 test/615_proxy_rewrite_hosts_test.sh diff --git a/prog/weavewait/main.go b/prog/weavewait/main.go index 146edc9e..c3adfafc 100644 --- a/prog/weavewait/main.go +++ b/prog/weavewait/main.go @@ -1,17 +1,11 @@ package main import ( - "bufio" - "bytes" "errors" "fmt" - "io/ioutil" - "net" "os" "os/exec" "os/signal" - "sort" - "strings" "syscall" weavenet "github.com/weaveworks/weave/net" @@ -23,19 +17,12 @@ var ( func main() { var ( - args = os.Args[1:] - notInExec = true - rewriteHosts = true + args = os.Args[1:] + notInExec = true ) if len(args) > 0 && args[0] == "-s" { notInExec = false - rewriteHosts = false - args = args[1:] - } - - if len(args) > 0 && args[0] == "-h" { - rewriteHosts = false args = args[1:] } @@ -45,17 +32,13 @@ func main() { <-usr2 } - iface, err := weavenet.EnsureInterface("ethwe", -1) + _, err := weavenet.EnsureInterface("ethwe", -1) checkErr(err) if len(args) == 0 { checkErr(ErrNoCommandSpecified) } - if rewriteHosts { - updateHosts(iface) - } - binary, err := exec.LookPath(args[0]) checkErr(err) @@ -68,92 +51,3 @@ func checkErr(err error) { os.Exit(1) } } - -func updateHosts(iface *net.Interface) { - addrs, err := iface.Addrs() - checkErr(err) - if len(addrs) == 0 { - return - } - hostname, err := os.Hostname() - checkErr(err) - - hosts := parseHosts() - - // Remove existing ips pointing to our hostname - toRemove := []string{} - for ip, addrs := range hosts { - for _, addr := range addrs { - if addr == hostname { - toRemove = append(toRemove, ip) - break - } - } - } - for _, ip := range toRemove { - delete(hosts, ip) - } - - // Add the weave ip(s) - for _, addr := range addrs { - if addr, ok := addr.(*net.IPNet); ok { - ip := addr.IP.String() - hosts[ip] = append(hosts[ip], hostname) - } - } - - writeHosts(hosts) -} - -func parseHosts() map[string][]string { - f, err := os.Open("/etc/hosts") - checkErr(err) - defer f.Close() - ips := map[string][]string{} - scanner := bufio.NewScanner(f) - for scanner.Scan() { - line := scanner.Text() - - // Remove any comments - if i := strings.IndexByte(line, '#'); i != -1 { - line = line[:i] - } - - fields := strings.Fields(line) - if len(fields) > 0 { - ips[fields[0]] = append(ips[fields[0]], fields[1:]...) - } - } - checkErr(scanner.Err()) - return ips -} - -func writeHosts(contents map[string][]string) { - ips := []string{} - for ip := range contents { - ips = append(ips, ip) - } - sort.Strings(ips) - - buf := &bytes.Buffer{} - fmt.Fprintln(buf, "# modified by weave") - for _, ip := range ips { - if addrs := contents[ip]; len(addrs) > 0 { - fmt.Fprintf(buf, "%s\t%s\n", ip, strings.Join(uniqueStrs(addrs), " ")) - } - } - checkErr(ioutil.WriteFile("/etc/hosts", buf.Bytes(), 644)) -} - -func uniqueStrs(s []string) []string { - m := map[string]struct{}{} - result := []string{} - for _, str := range s { - if _, ok := m[str]; !ok { - m[str] = struct{}{} - result = append(result, str) - } - } - sort.Strings(result) - return result -} diff --git a/proxy/create_container_interceptor.go b/proxy/create_container_interceptor.go index ebb3ab0b..2bac7efc 100644 --- a/proxy/create_container_interceptor.go +++ b/proxy/create_container_interceptor.go @@ -119,11 +119,7 @@ func (i *createContainerInterceptor) setWeaveWaitEntrypoint(container *docker.Co } if len(container.Entrypoint) == 0 || container.Entrypoint[0] != weaveWaitEntrypoint[0] { - entrypoint := weaveWaitEntrypoint - if i.proxy.NoRewriteHosts { - entrypoint = append(entrypoint, "-h") - } - container.Entrypoint = append(entrypoint, container.Entrypoint...) + container.Entrypoint = append(weaveWaitEntrypoint, container.Entrypoint...) } return nil diff --git a/proxy/start_container_interceptor.go b/proxy/start_container_interceptor.go index a699e510..1c5f1754 100644 --- a/proxy/start_container_interceptor.go +++ b/proxy/start_container_interceptor.go @@ -1,8 +1,15 @@ package proxy import ( + "bufio" + "bytes" "errors" + "fmt" + "io/ioutil" + "net" "net/http" + "os" + "sort" "strings" "github.com/fsouza/go-dockerclient" @@ -41,5 +48,128 @@ func (i *startContainerInterceptor) InterceptResponse(r *http.Response) error { Log.Warningf("Attaching container %s to weave network: %s", container.ID, string(stderr)) } + if !i.proxy.NoRewriteHosts { + ips, err := weaveContainerIPs(container) + if err != nil { + return err + } + if len(ips) > 0 { + if err := updateHosts(container.HostsPath, container.Config.Hostname, ips); err != nil { + return err + } + } + } + return i.proxy.client.KillContainer(docker.KillContainerOptions{ID: container.ID, Signal: docker.SIGUSR2}) } + +func weaveContainerIPs(container *docker.Container) ([]net.IP, error) { + stdout, stderr, err := callWeave("ps", container.ID) + if err != nil || len(stderr) > 0 { + return nil, errors.New(string(stderr)) + } + if len(stdout) <= 0 { + return nil, nil + } + + fields := strings.Fields(string(stdout)) + if len(fields) <= 2 { + return nil, nil + } + + var ips []net.IP + for _, cidr := range fields[2:] { + ip, _, err := net.ParseCIDR(cidr) + if err != nil { + return nil, err + } + ips = append(ips, ip) + } + return ips, nil +} + +func updateHosts(path, hostname string, ips []net.IP) error { + hosts, err := parseHosts(path) + if err != nil { + return err + } + + // Remove existing ips pointing to our hostname + toRemove := []string{} + for ip, addrs := range hosts { + for _, addr := range addrs { + if addr == hostname { + toRemove = append(toRemove, ip) + break + } + } + } + for _, ip := range toRemove { + delete(hosts, ip) + } + + // Add the weave ip(s) + for _, ip := range ips { + ipStr := ip.String() + hosts[ipStr] = append(hosts[ipStr], hostname) + } + + return writeHosts(path, hosts) +} + +func parseHosts(path string) (map[string][]string, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + ips := map[string][]string{} + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + + // Remove any comments + if i := strings.IndexByte(line, '#'); i != -1 { + line = line[:i] + } + + fields := strings.Fields(line) + if len(fields) > 0 { + ips[fields[0]] = append(ips[fields[0]], fields[1:]...) + } + } + if scanner.Err() != nil { + return nil, scanner.Err() + } + return ips, nil +} + +func writeHosts(path string, contents map[string][]string) error { + ips := []string{} + for ip := range contents { + ips = append(ips, ip) + } + sort.Strings(ips) + + buf := &bytes.Buffer{} + fmt.Fprintln(buf, "# modified by weave") + for _, ip := range ips { + if addrs := contents[ip]; len(addrs) > 0 { + fmt.Fprintf(buf, "%s\t%s\n", ip, strings.Join(uniqueStrs(addrs), " ")) + } + } + return ioutil.WriteFile(path, buf.Bytes(), 644) +} + +func uniqueStrs(s []string) []string { + m := map[string]struct{}{} + result := []string{} + for _, str := range s { + if _, ok := m[str]; !ok { + m[str] = struct{}{} + result = append(result, str) + } + } + sort.Strings(result) + return result +} diff --git a/test/610_proxy_wait_for_weave_test.sh b/test/610_proxy_wait_for_weave_test.sh index 39393e14..5a738b61 100755 --- a/test/610_proxy_wait_for_weave_test.sh +++ b/test/610_proxy_wait_for_weave_test.sh @@ -19,9 +19,6 @@ COMMITTED_IMAGE=$(proxy docker_on $HOST1 commit c1) assert_raises "proxy docker_on $HOST1 run --name c2 $COMMITTED_IMAGE" assert "entrypoint c2" "$(entrypoint $COMMITTED_IMAGE)" -# Check weave IP is first ip returned, so java (etc) prefer weave. -assert "proxy docker_on $HOST1 run -e 'WEAVE_CIDR=10.2.1.1/24' $BASE_IMAGE hostname -i | cut -d' ' -f1" "10.2.1.1" - # Check exec works on containers without weavewait docker_on $HOST1 run -dit --name c3 $SMALL_IMAGE /bin/sh assert_raises "proxy docker_on $HOST1 exec c3 true" diff --git a/test/615_proxy_rewrite_hosts_test.sh b/test/615_proxy_rewrite_hosts_test.sh new file mode 100755 index 00000000..95f29047 --- /dev/null +++ b/test/615_proxy_rewrite_hosts_test.sh @@ -0,0 +1,32 @@ +#! /bin/bash + +. ./config.sh + +expected_ip=10.2.1.1 + +image_ip() { + proxy docker_on $HOST1 run -e "WEAVE_CIDR=$expected_ip/24" $1 hostname -i | cut -d' ' -f1 +} + +start_suite "Proxy rewrites hosts file" + +# Default rewrites the host file +weave_on $HOST1 launch-proxy +assert "image_ip $SMALL_IMAGE" $expected_ip + +# When container user is non-root +docker_on $HOST1 build -t non-root >/dev/null - <<- EOF + FROM $SMALL_IMAGE + RUN adduser -D -s /bin/sh user + ENV HOME /home/user + USER user + CMD true +EOF +assert "image_ip non-root" $expected_ip + +# When rewrite hosts is disabled +weave_on $HOST1 stop-proxy +weave_on $HOST1 launch-proxy --no-rewrite-hosts +assert_raises "image_ip $SMALL_IMAGE | grep -v $expected_ip" + +end_suite diff --git a/weave b/weave index ae7f2f87..d7408688 100755 --- a/weave +++ b/weave @@ -1131,8 +1131,14 @@ launch_proxy() { # when launching the weaveproxy container. docker_client_args $DOCKER_CLIENT_ARGS proxy_args "$@" + HOSTS_PATH=$(docker run --rm \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --entrypoint /bin/sh \ + $EXEC_IMAGE -c 'docker inspect -f {{.HostsPath}} $(hostname)') + CONTAINERS_PATH="$(dirname $(dirname "$HOSTS_PATH"))" PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \ $PROXY_VOLUMES \ + -v "$CONTAINERS_PATH":"$CONTAINERS_PATH" \ -v /var/run:/var/run \ -v /proc:/hostproc \ -e PROCFS=/hostproc \