1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Windows: Security warning based on server OS

Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
John Howard 2015-06-04 06:30:14 -07:00
parent 31ae9d876a
commit 126529c6d0
11 changed files with 92 additions and 37 deletions

View file

@ -21,6 +21,7 @@ import (
"github.com/docker/docker/graph/tags" "github.com/docker/docker/graph/tags"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/httputils"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
flag "github.com/docker/docker/pkg/mflag" flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers"
@ -188,12 +189,6 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
} }
} }
// windows: show error message about modified file permissions
// FIXME: this is not a valid warning when the daemon is running windows. should be removed once docker engine for windows can build.
if runtime.GOOS == "windows" {
fmt.Fprintln(cli.err, `SECURITY WARNING: You are building a Docker image from Windows against a Linux Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
}
var body io.Reader var body io.Reader
// Setup an upload progress bar // Setup an upload progress bar
// FIXME: ProgressReader shouldn't be this annoying to use // FIXME: ProgressReader shouldn't be this annoying to use
@ -298,7 +293,19 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
out: cli.out, out: cli.out,
headers: headers, headers: headers,
} }
err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), sopts)
serverResp, err := cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), sopts)
// Windows: show error message about modified file permissions.
if runtime.GOOS == "windows" {
h, err := httputils.ParseServerHeader(serverResp.header.Get("Server"))
if err == nil {
if h.OS != "windows" {
fmt.Fprintln(cli.err, `SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
}
}
}
if jerr, ok := err.(*jsonmessage.JSONError); ok { if jerr, ok := err.(*jsonmessage.JSONError); ok {
// If no error code is set, default to 1 // If no error code is set, default to 1
if jerr.Code == 0 { if jerr.Code == 0 {

View file

@ -52,7 +52,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
out: out, out: out,
headers: map[string][]string{"X-Registry-Auth": registryAuthHeader}, headers: map[string][]string{"X-Registry-Auth": registryAuthHeader},
} }
if err := cli.stream("POST", "/images/create?"+v.Encode(), sopts); err != nil { if _, err := cli.stream("POST", "/images/create?"+v.Encode(), sopts); err != nil {
return err return err
} }
return nil return nil

View file

@ -55,7 +55,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
rawTerminal: true, rawTerminal: true,
out: cli.out, out: cli.out,
} }
if err := cli.stream("GET", "/events?"+v.Encode(), sopts); err != nil { if _, err := cli.stream("GET", "/events?"+v.Encode(), sopts); err != nil {
return err return err
} }
return nil return nil

View file

@ -38,7 +38,7 @@ func (cli *DockerCli) CmdExport(args ...string) error {
rawTerminal: true, rawTerminal: true,
out: output, out: output,
} }
if err := cli.stream("GET", "/containers/"+image+"/export", sopts); err != nil { if _, err := cli.stream("GET", "/containers/"+image+"/export", sopts); err != nil {
return err return err
} }

View file

@ -71,5 +71,6 @@ func (cli *DockerCli) CmdImport(args ...string) error {
out: cli.out, out: cli.out,
} }
return cli.stream("POST", "/images/create?"+v.Encode(), sopts) _, err := cli.stream("POST", "/images/create?"+v.Encode(), sopts)
return err
} }

View file

@ -34,7 +34,7 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
in: input, in: input,
out: cli.out, out: cli.out,
} }
if err := cli.stream("POST", "/images/load", sopts); err != nil { if _, err := cli.stream("POST", "/images/load", sopts); err != nil {
return err return err
} }
return nil return nil

View file

@ -65,5 +65,6 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
err: cli.err, err: cli.err,
} }
return cli.stream("GET", "/containers/"+name+"/logs?"+v.Encode(), sopts) _, err = cli.stream("GET", "/containers/"+name+"/logs?"+v.Encode(), sopts)
return err
} }

View file

@ -41,7 +41,7 @@ func (cli *DockerCli) CmdSave(args ...string) error {
if len(cmd.Args()) == 1 { if len(cmd.Args()) == 1 {
image := cmd.Arg(0) image := cmd.Arg(0)
if err := cli.stream("GET", "/images/"+image+"/get", sopts); err != nil { if _, err := cli.stream("GET", "/images/"+image+"/get", sopts); err != nil {
return err return err
} }
} else { } else {
@ -49,7 +49,7 @@ func (cli *DockerCli) CmdSave(args ...string) error {
for _, arg := range cmd.Args() { for _, arg := range cmd.Args() {
v.Add("names", arg) v.Add("names", arg)
} }
if err := cli.stream("GET", "/images/get?"+v.Encode(), sopts); err != nil { if _, err := cli.stream("GET", "/images/get?"+v.Encode(), sopts); err != nil {
return err return err
} }
} }

View file

@ -33,6 +33,12 @@ var (
errConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") errConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
) )
type serverResponse struct {
body io.ReadCloser
header http.Header
statusCode int
}
// HTTPClient creates a new HTTP client with the cli's client transport instance. // HTTPClient creates a new HTTP client with the cli's client transport instance.
func (cli *DockerCli) HTTPClient() *http.Client { func (cli *DockerCli) HTTPClient() *http.Client {
return &http.Client{Transport: cli.transport} return &http.Client{Transport: cli.transport}
@ -48,14 +54,20 @@ func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) {
return params, nil return params, nil
} }
func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers map[string][]string) (io.ReadCloser, http.Header, int, error) { func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers map[string][]string) (*serverResponse, error) {
serverResp := &serverResponse{
body: nil,
statusCode: -1,
}
expectedPayload := (method == "POST" || method == "PUT") expectedPayload := (method == "POST" || method == "PUT")
if expectedPayload && in == nil { if expectedPayload && in == nil {
in = bytes.NewReader([]byte{}) in = bytes.NewReader([]byte{})
} }
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.Version, path), in) req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.Version, path), in)
if err != nil { if err != nil {
return nil, nil, -1, err return serverResp, err
} }
// Add CLI Config's HTTP Headers BEFORE we set the Docker headers // Add CLI Config's HTTP Headers BEFORE we set the Docker headers
@ -79,33 +91,34 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
} }
resp, err := cli.HTTPClient().Do(req) resp, err := cli.HTTPClient().Do(req)
statusCode := -1
if resp != nil { if resp != nil {
statusCode = resp.StatusCode serverResp.statusCode = resp.StatusCode
} }
if err != nil { if err != nil {
if strings.Contains(err.Error(), "connection refused") { if strings.Contains(err.Error(), "connection refused") {
return nil, nil, statusCode, errConnectionRefused return serverResp, errConnectionRefused
} }
if cli.tlsConfig == nil { if cli.tlsConfig == nil {
return nil, nil, statusCode, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?\n* Is your docker daemon up and running?", err) return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?\n* Is your docker daemon up and running?", err)
} }
return nil, nil, statusCode, fmt.Errorf("An error occurred trying to connect: %v", err) return serverResp, fmt.Errorf("An error occurred trying to connect: %v", err)
} }
if statusCode < 200 || statusCode >= 400 { if serverResp.statusCode < 200 || serverResp.statusCode >= 400 {
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, nil, statusCode, err return serverResp, err
} }
if len(body) == 0 { if len(body) == 0 {
return nil, nil, statusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(statusCode), req.URL) return serverResp, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), req.URL)
} }
return nil, nil, statusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body)) return serverResp, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
} }
return resp.Body, resp.Header, statusCode, nil serverResp.body = resp.Body
serverResp.header = resp.Header
return serverResp, nil
} }
func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) { func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
@ -119,13 +132,13 @@ func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reade
} }
// begin the request // begin the request
body, hdr, statusCode, err := cli.clientRequest(method, path, in, map[string][]string{ serverResp, err := cli.clientRequest(method, path, in, map[string][]string{
"X-Registry-Auth": registryAuthHeader, "X-Registry-Auth": registryAuthHeader,
}) })
if err == nil && out != nil { if err == nil && out != nil {
// If we are streaming output, complete the stream since // If we are streaming output, complete the stream since
// errors may not appear until later. // errors may not appear until later.
err = cli.streamBody(body, hdr.Get("Content-Type"), true, out, nil) err = cli.streamBody(serverResp.body, serverResp.header.Get("Content-Type"), true, out, nil)
} }
if err != nil { if err != nil {
// Since errors in a stream appear after status 200 has been written, // Since errors in a stream appear after status 200 has been written,
@ -133,10 +146,10 @@ func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reade
if strings.Contains(err.Error(), "Authentication is required") || if strings.Contains(err.Error(), "Authentication is required") ||
strings.Contains(err.Error(), "Status 401") || strings.Contains(err.Error(), "Status 401") ||
strings.Contains(err.Error(), "status code 401") { strings.Contains(err.Error(), "status code 401") {
statusCode = http.StatusUnauthorized serverResp.statusCode = http.StatusUnauthorized
} }
} }
return body, statusCode, err return serverResp.body, serverResp.statusCode, err
} }
// Resolve the Auth config relevant for this server // Resolve the Auth config relevant for this server
@ -166,8 +179,8 @@ func (cli *DockerCli) call(method, path string, data interface{}, headers map[st
headers["Content-Type"] = []string{"application/json"} headers["Content-Type"] = []string{"application/json"}
} }
body, hdr, statusCode, err := cli.clientRequest(method, path, params, headers) serverResp, err := cli.clientRequest(method, path, params, headers)
return body, hdr, statusCode, err return serverResp.body, serverResp.header, serverResp.statusCode, err
} }
type streamOpts struct { type streamOpts struct {
@ -178,12 +191,12 @@ type streamOpts struct {
headers map[string][]string headers map[string][]string
} }
func (cli *DockerCli) stream(method, path string, opts *streamOpts) error { func (cli *DockerCli) stream(method, path string, opts *streamOpts) (*serverResponse, error) {
body, hdr, _, err := cli.clientRequest(method, path, opts.in, opts.headers) serverResp, err := cli.clientRequest(method, path, opts.in, opts.headers)
if err != nil { if err != nil {
return err return serverResp, err
} }
return cli.streamBody(body, hdr.Get("Content-Type"), opts.rawTerminal, opts.out, opts.err) return serverResp, cli.streamBody(serverResp.body, serverResp.header.Get("Content-Type"), opts.rawTerminal, opts.out, opts.err)
} }
func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, rawTerminal bool, stdout, stderr io.Writer) error { func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, rawTerminal bool, stdout, stderr io.Writer) error {

View file

@ -1502,6 +1502,8 @@ func makeHttpHandler(logging bool, localMethod string, localRoute string, handle
return return
} }
w.Header().Set("Server", "Docker/"+dockerversion.VERSION+" ("+runtime.GOOS+")")
if err := handlerFunc(version, w, r, mux.Vars(r)); err != nil { if err := handlerFunc(version, w, r, mux.Vars(r)); err != nil {
logrus.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err) logrus.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
httpError(w, err) httpError(w, err)

View file

@ -1,8 +1,11 @@
package httputils package httputils
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"regexp"
"strings"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
) )
@ -25,3 +28,31 @@ func NewHTTPRequestError(msg string, res *http.Response) error {
Code: res.StatusCode, Code: res.StatusCode,
} }
} }
type ServerHeader struct {
App string // docker
Ver string // 1.8.0-dev
OS string // windows or linux
}
// parseServerHeader extracts pieces from am HTTP server header
// which is in the format "docker/version (os)" eg docker/1.8.0-dev (windows)
func ParseServerHeader(hdr string) (*ServerHeader, error) {
re := regexp.MustCompile(`.*\((.+)\).*$`)
r := &ServerHeader{}
if matches := re.FindStringSubmatch(hdr); matches != nil {
r.OS = matches[1]
parts := strings.Split(hdr, "/")
if len(parts) != 2 {
return nil, errors.New("Bad header: '/' missing")
}
r.App = parts[0]
v := strings.Split(parts[1], " ")
if len(v) != 2 {
return nil, errors.New("Bad header: Expected single space")
}
r.Ver = v[0]
return r, nil
}
return nil, errors.New("Bad header: Failed regex match")
}