зеркало из https://github.com/microsoft/docker.git
Windows: Security warning based on server OS
Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
Родитель
31ae9d876a
Коммит
126529c6d0
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/docker/docker/graph/tags"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
"github.com/docker/docker/pkg/httputils"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"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
|
||||
// Setup an upload progress bar
|
||||
// FIXME: ProgressReader shouldn't be this annoying to use
|
||||
|
@ -298,7 +293,19 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
out: cli.out,
|
||||
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 no error code is set, default to 1
|
||||
if jerr.Code == 0 {
|
||||
|
|
|
@ -52,7 +52,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
|
|||
out: out,
|
||||
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 nil
|
||||
|
|
|
@ -55,7 +55,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
|
|||
rawTerminal: true,
|
||||
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 nil
|
||||
|
|
|
@ -38,7 +38,7 @@ func (cli *DockerCli) CmdExport(args ...string) error {
|
|||
rawTerminal: true,
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -71,5 +71,6 @@ func (cli *DockerCli) CmdImport(args ...string) error {
|
|||
out: cli.out,
|
||||
}
|
||||
|
||||
return cli.stream("POST", "/images/create?"+v.Encode(), sopts)
|
||||
_, err := cli.stream("POST", "/images/create?"+v.Encode(), sopts)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
|
|||
in: input,
|
||||
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 nil
|
||||
|
|
|
@ -65,5 +65,6 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func (cli *DockerCli) CmdSave(args ...string) error {
|
|||
|
||||
if len(cmd.Args()) == 1 {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
|
@ -49,7 +49,7 @@ func (cli *DockerCli) CmdSave(args ...string) error {
|
|||
for _, arg := range cmd.Args() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,12 @@ var (
|
|||
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.
|
||||
func (cli *DockerCli) HTTPClient() *http.Client {
|
||||
return &http.Client{Transport: cli.transport}
|
||||
|
@ -48,14 +54,20 @@ func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) {
|
|||
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")
|
||||
if expectedPayload && in == nil {
|
||||
in = bytes.NewReader([]byte{})
|
||||
}
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.Version, path), in)
|
||||
if err != nil {
|
||||
return nil, nil, -1, err
|
||||
return serverResp, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
statusCode := -1
|
||||
if resp != nil {
|
||||
statusCode = resp.StatusCode
|
||||
serverResp.statusCode = resp.StatusCode
|
||||
}
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return nil, nil, statusCode, errConnectionRefused
|
||||
return serverResp, errConnectionRefused
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, nil, statusCode, err
|
||||
return serverResp, err
|
||||
}
|
||||
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) {
|
||||
|
@ -119,13 +132,13 @@ func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reade
|
|||
}
|
||||
|
||||
// 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,
|
||||
})
|
||||
if err == nil && out != nil {
|
||||
// If we are streaming output, complete the stream since
|
||||
// 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 {
|
||||
// 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") ||
|
||||
strings.Contains(err.Error(), "Status 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
|
||||
|
@ -166,8 +179,8 @@ func (cli *DockerCli) call(method, path string, data interface{}, headers map[st
|
|||
headers["Content-Type"] = []string{"application/json"}
|
||||
}
|
||||
|
||||
body, hdr, statusCode, err := cli.clientRequest(method, path, params, headers)
|
||||
return body, hdr, statusCode, err
|
||||
serverResp, err := cli.clientRequest(method, path, params, headers)
|
||||
return serverResp.body, serverResp.header, serverResp.statusCode, err
|
||||
}
|
||||
|
||||
type streamOpts struct {
|
||||
|
@ -178,12 +191,12 @@ type streamOpts struct {
|
|||
headers map[string][]string
|
||||
}
|
||||
|
||||
func (cli *DockerCli) stream(method, path string, opts *streamOpts) error {
|
||||
body, hdr, _, err := cli.clientRequest(method, path, opts.in, opts.headers)
|
||||
func (cli *DockerCli) stream(method, path string, opts *streamOpts) (*serverResponse, error) {
|
||||
serverResp, err := cli.clientRequest(method, path, opts.in, opts.headers)
|
||||
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 {
|
||||
|
|
|
@ -1502,6 +1502,8 @@ func makeHttpHandler(logging bool, localMethod string, localRoute string, handle
|
|||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Server", "Docker/"+dockerversion.VERSION+" ("+runtime.GOOS+")")
|
||||
|
||||
if err := handlerFunc(version, w, r, mux.Vars(r)); err != nil {
|
||||
logrus.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
|
||||
httpError(w, err)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package httputils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
)
|
||||
|
@ -25,3 +28,31 @@ func NewHTTPRequestError(msg string, res *http.Response) error {
|
|||
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")
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче