diff --git a/proxy/create_container_interceptor.go b/proxy/create_container_interceptor.go index 5ea64704..d40efaff 100644 --- a/proxy/create_container_interceptor.go +++ b/proxy/create_container_interceptor.go @@ -2,16 +2,11 @@ package proxy import ( "errors" - "fmt" - "io/ioutil" "net/http" "strings" "github.com/fsouza/go-dockerclient" - . "github.com/weaveworks/weave/common" - "github.com/weaveworks/weave/nameserver" - "github.com/weaveworks/weave/router" ) const MaxDockerHostname = 64 @@ -55,7 +50,7 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error { if container.Config == nil { container.Config = &docker.Config{} } - i.addWeaveWaitVolume(container.HostConfig) + i.proxy.addWeaveWaitVolume(container.HostConfig) if err := i.setWeaveWaitEntrypoint(container.Config); err != nil { return err } @@ -66,8 +61,11 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error { } } hostname = i.proxy.hostnameMatchRegexp.ReplaceAllString(hostname, i.proxy.HostnameReplacement) - if err := i.setWeaveDNS(&container, hostname); err != nil { - return err + if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" { + i.setHostname(&container, hostname, dnsDomain) + if err := i.proxy.setWeaveDNS(container.HostConfig, container.Hostname, dnsDomain); err != nil { + return err + } } return marshalRequestBody(r, container) @@ -76,18 +74,6 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error { return nil } -func (i *createContainerInterceptor) addWeaveWaitVolume(hostConfig *docker.HostConfig) { - var binds []string - for _, bind := range hostConfig.Binds { - s := strings.Split(bind, ":") - if len(s) >= 2 && s[1] == "/w" { - continue - } - binds = append(binds, bind) - } - hostConfig.Binds = append(binds, fmt.Sprintf("%s:/w:ro", i.proxy.weaveWaitVolume)) -} - func (i *createContainerInterceptor) setWeaveWaitEntrypoint(container *docker.Config) error { if len(container.Entrypoint) == 0 { image, err := i.proxy.client.InspectImage(container.Image) @@ -117,18 +103,7 @@ func (i *createContainerInterceptor) setWeaveWaitEntrypoint(container *docker.Co return nil } -func (i *createContainerInterceptor) setWeaveDNS(container *createContainerRequestBody, name string) error { - if i.proxy.WithoutDNS { - return nil - } - - dnsDomain, dnsRunning := i.getDNSDomain() - if !(dnsRunning || i.proxy.WithDNS) { - return nil - } - - container.HostConfig.DNS = append(container.HostConfig.DNS, i.proxy.dockerBridgeIP) - +func (i *createContainerInterceptor) setHostname(container *createContainerRequestBody, name, dnsDomain string) { if container.Hostname == "" && name != "" { // Strip trailing period because it's unusual to see it used on the end of a host name trimmedDNSDomain := strings.TrimSuffix(dnsDomain, ".") @@ -140,39 +115,7 @@ func (i *createContainerInterceptor) setWeaveDNS(container *createContainerReque } } - if len(container.HostConfig.DNSSearch) == 0 { - if container.Hostname == "" { - container.HostConfig.DNSSearch = []string{dnsDomain} - } else { - container.HostConfig.DNSSearch = []string{"."} - } - } - - return nil -} - -func (i *createContainerInterceptor) getDNSDomain() (domain string, running bool) { - domain = nameserver.DefaultDomain - weaveContainer, err := i.proxy.client.InspectContainer("weave") - if err != nil || - weaveContainer.NetworkSettings == nil || - weaveContainer.NetworkSettings.IPAddress == "" { - return - } - - url := fmt.Sprintf("http://%s:%d/domain", weaveContainer.NetworkSettings.IPAddress, router.HTTPPort) - resp, err := http.Get(url) - if err != nil || resp.StatusCode != http.StatusOK { - return - } - defer resp.Body.Close() - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return - } - - return string(b), true + return } func (i *createContainerInterceptor) InterceptResponse(r *http.Response) error { diff --git a/proxy/proxy.go b/proxy/proxy.go index eeead60a..e6ca9599 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "errors" "fmt" + "io/ioutil" "net" "net/http" "os" @@ -15,6 +16,8 @@ import ( docker "github.com/fsouza/go-dockerclient" . "github.com/weaveworks/weave/common" weavedocker "github.com/weaveworks/weave/common/docker" + "github.com/weaveworks/weave/nameserver" + "github.com/weaveworks/weave/router" ) const ( @@ -365,3 +368,60 @@ func (proxy *Proxy) weaveCIDRsFromConfig(config *docker.Config, hostConfig *dock } return nil, nil } + +func (proxy *Proxy) addWeaveWaitVolume(hostConfig *docker.HostConfig) { + var binds []string + for _, bind := range hostConfig.Binds { + s := strings.Split(bind, ":") + if len(s) >= 2 && s[1] == "/w" { + continue + } + binds = append(binds, bind) + } + hostConfig.Binds = append(binds, fmt.Sprintf("%s:/w:ro", proxy.weaveWaitVolume)) +} + +func (proxy *Proxy) setWeaveDNS(hostConfig *docker.HostConfig, hostname, dnsDomain string) error { + hostConfig.DNS = append(hostConfig.DNS, proxy.dockerBridgeIP) + + if len(hostConfig.DNSSearch) == 0 { + if hostname == "" { + hostConfig.DNSSearch = []string{dnsDomain} + } else { + hostConfig.DNSSearch = []string{"."} + } + } + + return nil +} + +func (proxy *Proxy) getDNSDomain() (domain string) { + if proxy.WithoutDNS { + return "" + } + + if proxy.WithDNS { + domain = nameserver.DefaultDomain + } + + weaveContainer, err := proxy.client.InspectContainer("weave") + if err != nil || + weaveContainer.NetworkSettings == nil || + weaveContainer.NetworkSettings.IPAddress == "" { + return + } + + url := fmt.Sprintf("http://%s:%d/domain", weaveContainer.NetworkSettings.IPAddress, router.HTTPPort) + resp, err := http.Get(url) + if err != nil || resp.StatusCode != http.StatusOK { + return + } + defer resp.Body.Close() + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return + } + + return string(b) +} diff --git a/proxy/start_container_interceptor.go b/proxy/start_container_interceptor.go index 6d549cf2..019ac8cd 100644 --- a/proxy/start_container_interceptor.go +++ b/proxy/start_container_interceptor.go @@ -2,16 +2,44 @@ package proxy import ( "net/http" + + "github.com/fsouza/go-dockerclient" ) type startContainerInterceptor struct{ proxy *Proxy } +type startContainerRequestBody struct { + HostConfig *docker.HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty"` +} + func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error { container, err := inspectContainerInPath(i.proxy.client, r.URL.Path) - if err == nil && containerShouldAttach(container) { - i.proxy.createWait(r, container.ID) + if err != nil { + return err } - return err + + if !containerShouldAttach(container) || r.Header.Get("Content-Type") != "application/json" { + i.proxy.createWait(r, container.ID) + return nil + } + + hostConfig := &docker.HostConfig{} + if err := unmarshalRequestBody(r, &hostConfig); err != nil { + return err + } + + i.proxy.addWeaveWaitVolume(hostConfig) + if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" { + if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil { + return err + } + } + + if err := marshalRequestBody(r, hostConfig); err != nil { + return err + } + i.proxy.createWait(r, container.ID) + return nil } func (i *startContainerInterceptor) InterceptResponse(r *http.Response) error { diff --git a/test/666_abuse_of_start_test.sh b/test/666_abuse_of_start_test.sh new file mode 100755 index 00000000..4689208a --- /dev/null +++ b/test/666_abuse_of_start_test.sh @@ -0,0 +1,21 @@ +#! /bin/bash + +. ./config.sh + +start_suite "Abuse of 'start' operation" + +weave_on $HOST1 launch +docker_bridge_ip=$(weave_on $HOST1 docker-bridge-ip) +proxy_start_container $HOST1 --name=c1 + +# Start c2 with a sneaky HostConfig +proxy docker_on $HOST1 create --name=c2 $SMALL_IMAGE $CHECK_ETHWE_UP +proxy docker_api_on $HOST1 POST /containers/c2/start '{"NetworkMode": "container:c1"}' +assert "docker_on $HOST1 inspect -f '{{.State.Running}} {{.State.ExitCode}} {{.HostConfig.Dns}}' c2" "false 0 [$docker_bridge_ip]" + +# Start c3 with HostConfig having empty binds and null dns/networking settings +proxy docker_on $HOST1 create --name=c3 -v /tmp:/hosttmp $SMALL_IMAGE $CHECK_ETHWE_UP +proxy docker_api_on $HOST1 POST /containers/c3/start '{"Binds":[],"Dns":null,"DnsSearch":null,"ExtraHosts":null,"VolumesFrom":null,"Devices":null,"NetworkMode":""}' +assert "docker_on $HOST1 inspect -f '{{.State.Running}} {{.State.ExitCode}} {{.HostConfig.Dns}}' c3" "false 0 [$docker_bridge_ip]" + +end_suite diff --git a/test/666_the_beast.sh b/test/666_the_beast.sh deleted file mode 100755 index a0073532..00000000 --- a/test/666_the_beast.sh +++ /dev/null @@ -1,15 +0,0 @@ -#! /bin/bash - -. ./config.sh - -start_suite "Abuse of 'start' operation" - -weave_on $HOST1 launch - -proxy_start_container $HOST1 --name=c1 -proxy docker_on $HOST1 create --name=c2 $SMALL_IMAGE grep "^1$" /sys/class/net/ethwe/carrier -# Now start c2 with a sneaky HostConfig -curl -X POST -H Content-Type:application/json -d '{"HostConfig": {"NetworkMode": "container:c1"}}' http://$HOST1:12375/containers/c2/start -assert "docker_on $HOST1 inspect -f '{{.State.Running}} {{.State.ExitCode}}' c2" "false 0" - -end_suite diff --git a/test/config.sh b/test/config.sh index 1d9e6b49..ac9b2453 100644 --- a/test/config.sh +++ b/test/config.sh @@ -99,6 +99,16 @@ docker_on() { docker -H tcp://$host:$DOCKER_PORT "$@" } +docker_api_on() { + host=$1 + method=$2 + url=$3 + data=$4 + shift 4 + [ -z "$DEBUG" ] || greyly echo "Docker (API) on $host:$DOCKER_PORT: $method $url" >&2 + echo -n "$data" | curl -s -X "$method" -H Content-Type:application/json "http://$host:$DOCKER_PORT/v1.15$url" -d @- +} + proxy() { DOCKER_PORT=12375 "$@" }