Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

Commit

Permalink
[proxy] rewrite /etc/hosts from outside the container
Browse files Browse the repository at this point in the history
Avoids problems when container user is non-root. Added a test for this
and the --no-rewrite-hosts flag.
  • Loading branch information
paulbellamy committed Aug 6, 2015
1 parent 3180c67 commit 4da9bea
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 117 deletions.
112 changes: 3 additions & 109 deletions prog/weavewait/main.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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:]
}

Expand All @@ -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)

Expand All @@ -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
}
6 changes: 1 addition & 5 deletions proxy/create_container_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
130 changes: 130 additions & 0 deletions proxy/start_container_interceptor.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package proxy

import (
"bufio"
"bytes"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"sort"
"strings"

"github.com/fsouza/go-dockerclient"
Expand Down Expand Up @@ -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
}
3 changes: 0 additions & 3 deletions test/610_proxy_wait_for_weave_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
26 changes: 26 additions & 0 deletions test/615_proxy_rewrite_hosts_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#! /bin/bash

. ./config.sh

start_suite "Proxy rewrites hosts file"

# Default rewrites the host file
weave_on $HOST1 launch-proxy
assert "proxy docker_on $HOST1 run -e 'WEAVE_CIDR=10.2.1.1/24' $SMALL_IMAGE hostname -i | cut -d' ' -f1" "10.2.1.1"

# 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 "proxy docker_on $HOST1 run -e 'WEAVE_CIDR=10.2.1.1/24' non-root hostname -i | cut -d' ' -f1" "10.2.1.1"

# When rewrite hosts is disabled
weave_on $HOST1 stop-proxy
weave_on $HOST1 launch-proxy --no-rewrite-hosts
assert_raises "proxy docker_on $HOST1 run -e 'WEAVE_CIDR=10.2.1.1/24' $SMALL_IMAGE hostname -i | cut -d' ' -f1 | grep -v 10.2.1.1"

end_suite
6 changes: 6 additions & 0 deletions weave
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down

0 comments on commit 4da9bea

Please sign in to comment.