Merge pull request #264 from runcom/split-flags

cmd: per command tls flags
This commit is contained in:
Antonio Murdaca 2016-12-08 18:12:11 +01:00 committed by GitHub
commit a515fefda9
21 changed files with 353 additions and 159 deletions

View File

@ -13,12 +13,12 @@ import (
// contextsFromGlobalOptions returns source and destionation types.SystemContext depending on c. // contextsFromGlobalOptions returns source and destionation types.SystemContext depending on c.
func contextsFromGlobalOptions(c *cli.Context) (*types.SystemContext, *types.SystemContext, error) { func contextsFromGlobalOptions(c *cli.Context) (*types.SystemContext, *types.SystemContext, error) {
sourceCtx, err := contextFromGlobalOptions(c, "src-creds") sourceCtx, err := contextFromGlobalOptions(c, "src-")
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
destinationCtx, err := contextFromGlobalOptions(c, "dest-creds") destinationCtx, err := contextFromGlobalOptions(c, "dest-")
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -87,5 +87,23 @@ var copyCmd = cli.Command{
Value: "", Value: "",
Usage: "Use `USERNAME[:PASSWORD]` for accessing the destination registry", Usage: "Use `USERNAME[:PASSWORD]` for accessing the destination registry",
}, },
cli.StringFlag{
Name: "src-cert-dir",
Value: "",
Usage: "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the source registry",
},
cli.BoolTFlag{
Name: "src-tls-verify",
Usage: "require HTTPS and verify certificates when talking to the docker source registry (defaults to true)",
},
cli.StringFlag{
Name: "dest-cert-dir",
Value: "",
Usage: "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the destination registry",
},
cli.BoolTFlag{
Name: "dest-tls-verify",
Usage: "require HTTPS and verify certificates when talking to the docker destination registry (defaults to true)",
},
}, },
} }

View File

@ -18,7 +18,7 @@ func deleteHandler(context *cli.Context) error {
return fmt.Errorf("Invalid source name %s: %v", context.Args()[0], err) return fmt.Errorf("Invalid source name %s: %v", context.Args()[0], err)
} }
ctx, err := contextFromGlobalOptions(context, "creds") ctx, err := contextFromGlobalOptions(context, "")
if err != nil { if err != nil {
return err return err
} }
@ -39,5 +39,14 @@ var deleteCmd = cli.Command{
Value: "", Value: "",
Usage: "Use `USERNAME[:PASSWORD]` for accessing the registry", Usage: "Use `USERNAME[:PASSWORD]` for accessing the registry",
}, },
cli.StringFlag{
Name: "cert-dir",
Value: "",
Usage: "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the registry",
},
cli.BoolTFlag{
Name: "tls-verify",
Usage: "require HTTPS and verify certificates when talking to docker registries (defaults to true)",
},
}, },
} }

View File

@ -30,6 +30,15 @@ var inspectCmd = cli.Command{
Usage: "Inspect image IMAGE-NAME", Usage: "Inspect image IMAGE-NAME",
ArgsUsage: "IMAGE-NAME", ArgsUsage: "IMAGE-NAME",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{
Name: "cert-path",
Value: "",
Usage: "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the registry",
},
cli.BoolTFlag{
Name: "tls-verify",
Usage: "require HTTPS and verify certificates when talking to docker registries (defaults to true)",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "raw", Name: "raw",
Usage: "output raw manifest", Usage: "output raw manifest",

View File

@ -19,6 +19,7 @@ var layersCmd = cli.Command{
Name: "layers", Name: "layers",
Usage: "Get layers of IMAGE-NAME", Usage: "Get layers of IMAGE-NAME",
ArgsUsage: "IMAGE-NAME [LAYER...]", ArgsUsage: "IMAGE-NAME [LAYER...]",
Hidden: true,
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
fmt.Fprintln(os.Stderr, `DEPRECATED: skopeo layers is deprecated in favor of skopeo copy`) fmt.Fprintln(os.Stderr, `DEPRECATED: skopeo layers is deprecated in favor of skopeo copy`)
if c.NArg() == 0 { if c.NArg() == 0 {

View File

@ -30,14 +30,10 @@ func createApp() *cli.App {
Name: "debug", Name: "debug",
Usage: "enable debug output", Usage: "enable debug output",
}, },
cli.StringFlag{
Name: "cert-path",
Value: "",
Usage: "use certificates at `PATH` (cert.pem, key.pem) to connect to the registry",
},
cli.BoolTFlag{ cli.BoolTFlag{
Name: "tls-verify", Name: "tls-verify",
Usage: "require HTTPS and verify certificates when talking to docker registries (defaults to true)", Usage: "require HTTPS and verify certificates when talking to docker registries (defaults to true)",
Hidden: true,
}, },
cli.StringFlag{ cli.StringFlag{
Name: "policy", Name: "policy",
@ -54,6 +50,9 @@ func createApp() *cli.App {
if c.GlobalBool("debug") { if c.GlobalBool("debug") {
logrus.SetLevel(logrus.DebugLevel) logrus.SetLevel(logrus.DebugLevel)
} }
if c.GlobalIsSet("tls-verify") {
logrus.Warn("'--tls-verify' is deprecated, please set this on the specific subcommand")
}
return nil return nil
} }
app.Commands = []cli.Command{ app.Commands = []cli.Command{

View File

@ -9,15 +9,20 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
func contextFromGlobalOptions(c *cli.Context, credsFlag string) (*types.SystemContext, error) { func contextFromGlobalOptions(c *cli.Context, flagPrefix string) (*types.SystemContext, error) {
ctx := &types.SystemContext{ ctx := &types.SystemContext{
RegistriesDirPath: c.GlobalString("registries.d"), RegistriesDirPath: c.GlobalString("registries.d"),
DockerCertPath: c.GlobalString("cert-path"), DockerCertPath: c.String(flagPrefix + "cert-dir"),
// DEPRECATED: keep this here for backward compatibility, but override
// them if per subcommand flags are provided (see below).
DockerInsecureSkipTLSVerify: !c.GlobalBoolT("tls-verify"), DockerInsecureSkipTLSVerify: !c.GlobalBoolT("tls-verify"),
} }
if c.IsSet(credsFlag) { if c.IsSet(flagPrefix + "tls-verify") {
ctx.DockerInsecureSkipTLSVerify = !c.BoolT(flagPrefix + "tls-verify")
}
if c.IsSet(flagPrefix + "creds") {
var err error var err error
ctx.DockerAuthConfig, err = getDockerAuth(c.String(credsFlag)) ctx.DockerAuthConfig, err = getDockerAuth(c.String(flagPrefix + "creds"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -58,7 +63,7 @@ func parseImage(c *cli.Context) (types.Image, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx, err := contextFromGlobalOptions(c, "creds") ctx, err := contextFromGlobalOptions(c, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -73,7 +78,7 @@ func parseImageSource(c *cli.Context, name string, requestedManifestMIMETypes []
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx, err := contextFromGlobalOptions(c, "creds") ctx, err := contextFromGlobalOptions(c, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -23,7 +23,11 @@ _skopeo_copy() {
local options_with_args=" local options_with_args="
--sign-by --sign-by
--src-creds --screds --src-creds --screds
--src-cert-path
--src-tls-verify
--dest-creds --dcreds --dest-creds --dcreds
--dest-cert-path
--dest-tls-verify
" "
local boolean_options=" local boolean_options="
--remove-signatures --remove-signatures
@ -34,9 +38,11 @@ _skopeo_copy() {
_skopeo_inspect() { _skopeo_inspect() {
local options_with_args=" local options_with_args="
--creds --creds
--cert-path
" "
local boolean_options=" local boolean_options="
--raw --raw
--tls-verify
" "
_complete_ "$options_with_args" "$boolean_options" _complete_ "$options_with_args" "$boolean_options"
} }
@ -69,29 +75,32 @@ _skopeo_manifest_digest() {
_skopeo_delete() { _skopeo_delete() {
local options_with_args=" local options_with_args="
--creds --creds
--cert-path
" "
local boolean_options=" local boolean_options="
--tls-verify
" "
_complete_ "$options_with_args" "$boolean_options" _complete_ "$options_with_args" "$boolean_options"
} }
_skopeo_layers() { _skopeo_layers() {
local options_with_args=" local options_with_args="
--creds
--cert-path
" "
local boolean_options=" local boolean_options="
--tls-verify
" "
_complete_ "$options_with_args" "$boolean_options" _complete_ "$options_with_args" "$boolean_options"
} }
_skopeo_skopeo() { _skopeo_skopeo() {
local options_with_args=" local options_with_args="
--cert-path
--policy --policy
--registries.d --registries.d
" "
local boolean_options=" local boolean_options="
--debug --debug
--tls-verify
--version -v --version -v
--help -h --help -h
" "

View File

@ -37,14 +37,10 @@ Most commands refer to container images, using a _transport_`:`_details_ format.
**--debug** enable debug output **--debug** enable debug output
**--cert-path** _path_ Use certificates at _path_ (cert.pem, key.pem) to connect to the registry
**--policy** _path-to-policy_ Path to a policy.json file to use for verifying signatures and deciding whether an image is trusted, overriding the default trust policy file. **--policy** _path-to-policy_ Path to a policy.json file to use for verifying signatures and deciding whether an image is trusted, overriding the default trust policy file.
**--registries.d** _dir_ use registry configuration files in _dir_ (e.g. for docker signature storage), overriding the default path. **--registries.d** _dir_ use registry configuration files in _dir_ (e.g. for docker signature storage), overriding the default path.
**--tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to docker registries (defaults to true)
**--help**|**-h** Show help **--help**|**-h** Show help
**--version**|**-v** print the version number **--version**|**-v** print the version number
@ -70,6 +66,14 @@ Uses the system's trust policy to validate images, rejects images not trusted by
**--dest-creds** _username[:password]_ for accessing the destination registry **--dest-creds** _username[:password]_ for accessing the destination registry
**--src-cert-dir** _path_ Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the source registry
**--src-tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to docker source registry (defaults to true)
**--dest-cert-dir** _path_ Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the destination registry
**--dest-tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to docker destination registry (defaults to true)
Existing signatures, if any, are preserved as well. Existing signatures, if any, are preserved as well.
## skopeo delete ## skopeo delete
@ -83,6 +87,10 @@ $ docker exec -it registry bin/registry garbage-collect /etc/docker/registry/con
**--creds** _username[:password]_ for accessing the registry **--creds** _username[:password]_ for accessing the registry
**--cert-dir** _path_ Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the registry
**--tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to docker registries (defaults to true)
Additionally, the registry must allow deletions by setting `REGISTRY_STORAGE_DELETE_ENABLED=true` for the registry daemon. Additionally, the registry must allow deletions by setting `REGISTRY_STORAGE_DELETE_ENABLED=true` for the registry daemon.
## skopeo inspect ## skopeo inspect
@ -96,12 +104,9 @@ Return low-level information about _image-name_ in a registry
**--creds** _username[:password]_ for accessing the registry **--creds** _username[:password]_ for accessing the registry
## skopeo layers **--cert-dir** _path_ Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the registry
**skopeo layers** _image-name_
Get image layers of _image-name_ **--tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to docker registries (defaults to true)
_image-name_ name of the image to retrieve layers
## skopeo manifest-digest ## skopeo manifest-digest
**skopeo manifest-digest** _manifest-file_ **skopeo manifest-digest** _manifest-file_

View File

@ -16,7 +16,7 @@ clone git github.com/pmezard/go-difflib master
# docker deps from https://github.com/docker/docker/blob/v1.11.2/hack/vendor.sh # docker deps from https://github.com/docker/docker/blob/v1.11.2/hack/vendor.sh
clone git github.com/docker/docker v1.12.1 clone git github.com/docker/docker v1.12.1
clone git github.com/docker/engine-api 4eca04ae18f4f93f40196a17b9aa6e11262a7269 clone git github.com/docker/engine-api 4eca04ae18f4f93f40196a17b9aa6e11262a7269
clone git github.com/docker/go-connections v0.2.0 clone git github.com/docker/go-connections 4ccf312bf1d35e5dbda654e57a9be4c3f3cd0366
clone git github.com/vbatts/tar-split v0.9.11 clone git github.com/vbatts/tar-split v0.9.11
clone git github.com/gorilla/context 14f550f51a clone git github.com/gorilla/context 14f550f51a
clone git github.com/gorilla/mux e444e69cbd clone git github.com/gorilla/mux e444e69cbd

View File

@ -87,6 +87,64 @@ func newTransport() *http.Transport {
return tr return tr
} }
func setupCertificates(dir string, tlsc *tls.Config) error {
if dir == "" {
return nil
}
fs, err := ioutil.ReadDir(dir)
if err != nil && !os.IsNotExist(err) {
return err
}
for _, f := range fs {
fullPath := filepath.Join(dir, f.Name())
if strings.HasSuffix(f.Name(), ".crt") {
systemPool, err := tlsconfig.SystemCertPool()
if err != nil {
return fmt.Errorf("unable to get system cert pool: %v", err)
}
tlsc.RootCAs = systemPool
logrus.Debugf("crt: %s", fullPath)
data, err := ioutil.ReadFile(fullPath)
if err != nil {
return err
}
tlsc.RootCAs.AppendCertsFromPEM(data)
}
if strings.HasSuffix(f.Name(), ".cert") {
certName := f.Name()
keyName := certName[:len(certName)-5] + ".key"
logrus.Debugf("cert: %s", fullPath)
if !hasFile(fs, keyName) {
return fmt.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName)
}
cert, err := tls.LoadX509KeyPair(filepath.Join(dir, certName), filepath.Join(dir, keyName))
if err != nil {
return err
}
tlsc.Certificates = append(tlsc.Certificates, cert)
}
if strings.HasSuffix(f.Name(), ".key") {
keyName := f.Name()
certName := keyName[:len(keyName)-4] + ".cert"
logrus.Debugf("key: %s", fullPath)
if !hasFile(fs, certName) {
return fmt.Errorf("missing client certificate %s for key %s", certName, keyName)
}
}
}
return nil
}
func hasFile(files []os.FileInfo, name string) bool {
for _, f := range files {
if f.Name() == name {
return true
}
}
return false
}
// newDockerClient returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry) // newDockerClient returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection) // “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool) (*dockerClient, error) { func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool) (*dockerClient, error) {
@ -102,13 +160,10 @@ func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool)
if ctx != nil && (ctx.DockerCertPath != "" || ctx.DockerInsecureSkipTLSVerify) { if ctx != nil && (ctx.DockerCertPath != "" || ctx.DockerInsecureSkipTLSVerify) {
tlsc := &tls.Config{} tlsc := &tls.Config{}
if ctx.DockerCertPath != "" { if err := setupCertificates(ctx.DockerCertPath, tlsc); err != nil {
cert, err := tls.LoadX509KeyPair(filepath.Join(ctx.DockerCertPath, "cert.pem"), filepath.Join(ctx.DockerCertPath, "key.pem")) return nil, err
if err != nil {
return nil, fmt.Errorf("Error loading x509 key pair: %s", err)
}
tlsc.Certificates = append(tlsc.Certificates, cert)
} }
tlsc.InsecureSkipVerify = ctx.DockerInsecureSkipTLSVerify tlsc.InsecureSkipVerify = ctx.DockerInsecureSkipTLSVerify
tr.TLSClientConfig = tlsc tr.TLSClientConfig = tlsc
} }

View File

@ -270,7 +270,10 @@ type SystemContext struct {
RegistriesDirPath string RegistriesDirPath string
// === docker.Transport overrides === // === docker.Transport overrides ===
DockerCertPath string // If not "", a directory containing "cert.pem" and "key.pem" used when talking to a Docker Registry // If not "", a directory containing a CA certificate (ending with ".crt"),
// a client certificate (ending with ".cert") and a client ceritificate key
// (ending with ".key") used when talking to a Docker Registry.
DockerCertPath string
DockerInsecureSkipTLSVerify bool // Allow contacting docker registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections. DockerInsecureSkipTLSVerify bool // Allow contacting docker registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
// if nil, the library tries to parse ~/.docker/config.json to retrieve credentials // if nil, the library tries to parse ~/.docker/config.json to retrieve credentials
DockerAuthConfig *DockerAuthConfig DockerAuthConfig *DockerAuthConfig

View File

@ -85,14 +85,10 @@ func (p Port) Port() string {
// Int returns the port number of a Port as an int // Int returns the port number of a Port as an int
func (p Port) Int() int { func (p Port) Int() int {
portStr := p.Port() portStr := p.Port()
if len(portStr) == 0 {
return 0
}
// We don't need to check for an error because we're going to // We don't need to check for an error because we're going to
// assume that any error would have been found, and reported, in NewPort() // assume that any error would have been found, and reported, in NewPort()
port, _ := strconv.ParseUint(portStr, 10, 16) port, _ := ParsePort(portStr)
return int(port) return port
} }
// Range returns the start/end port numbers of a Port range as ints // Range returns the start/end port numbers of a Port range as ints
@ -132,48 +128,78 @@ func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
exposedPorts = make(map[Port]struct{}, len(ports)) exposedPorts = make(map[Port]struct{}, len(ports))
bindings = make(map[Port][]PortBinding) bindings = make(map[Port][]PortBinding)
) )
for _, rawPort := range ports { for _, rawPort := range ports {
proto := "tcp" portMappings, err := ParsePortSpec(rawPort)
if i := strings.LastIndex(rawPort, "/"); i != -1 {
proto = rawPort[i+1:]
rawPort = rawPort[:i]
}
if !strings.Contains(rawPort, ":") {
rawPort = fmt.Sprintf("::%s", rawPort)
} else if len(strings.Split(rawPort, ":")) == 2 {
rawPort = fmt.Sprintf(":%s", rawPort)
}
parts, err := PartParser(portSpecTemplate, rawPort)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
var ( for _, portMapping := range portMappings {
containerPort = parts["containerPort"] port := portMapping.Port
rawIP = parts["ip"] if _, exists := exposedPorts[port]; !exists {
hostPort = parts["hostPort"] exposedPorts[port] = struct{}{}
) }
bslice, exists := bindings[port]
if !exists {
bslice = []PortBinding{}
}
bindings[port] = append(bslice, portMapping.Binding)
}
}
return exposedPorts, bindings, nil
}
if rawIP != "" && net.ParseIP(rawIP) == nil { // PortMapping is a data object mapping a Port to a PortBinding
return nil, nil, fmt.Errorf("Invalid ip address: %s", rawIP) type PortMapping struct {
Port Port
Binding PortBinding
}
func splitParts(rawport string) (string, string, string) {
parts := strings.Split(rawport, ":")
n := len(parts)
containerport := parts[n-1]
switch n {
case 1:
return "", "", containerport
case 2:
return "", parts[0], containerport
case 3:
return parts[0], parts[1], containerport
default:
return strings.Join(parts[:n-2], ":"), parts[n-2], containerport
}
}
// ParsePortSpec parses a port specification string into a slice of PortMappings
func ParsePortSpec(rawPort string) ([]PortMapping, error) {
var proto string
rawIP, hostPort, containerPort := splitParts(rawPort)
proto, containerPort = SplitProtoPort(containerPort)
// Strip [] from IPV6 addresses
ip, _, err := net.SplitHostPort(rawIP + ":")
if err != nil {
return nil, fmt.Errorf("Invalid ip address %v: %s", rawIP, err)
}
if ip != "" && net.ParseIP(ip) == nil {
return nil, fmt.Errorf("Invalid ip address: %s", ip)
} }
if containerPort == "" { if containerPort == "" {
return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort) return nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
} }
startPort, endPort, err := ParsePortRange(containerPort) startPort, endPort, err := ParsePortRange(containerPort)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort) return nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
} }
var startHostPort, endHostPort uint64 = 0, 0 var startHostPort, endHostPort uint64 = 0, 0
if len(hostPort) > 0 { if len(hostPort) > 0 {
startHostPort, endHostPort, err = ParsePortRange(hostPort) startHostPort, endHostPort, err = ParsePortRange(hostPort)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort) return nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
} }
} }
@ -182,14 +208,15 @@ func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
// In this case, use the host port range as the dynamic // In this case, use the host port range as the dynamic
// host port range to allocate into. // host port range to allocate into.
if endPort != startPort { if endPort != startPort {
return nil, nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort) return nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
} }
} }
if !validateProto(strings.ToLower(proto)) { if !validateProto(strings.ToLower(proto)) {
return nil, nil, fmt.Errorf("Invalid proto: %s", proto) return nil, fmt.Errorf("Invalid proto: %s", proto)
} }
ports := []PortMapping{}
for i := uint64(0); i <= (endPort - startPort); i++ { for i := uint64(0); i <= (endPort - startPort); i++ {
containerPort = strconv.FormatUint(startPort+i, 10) containerPort = strconv.FormatUint(startPort+i, 10)
if len(hostPort) > 0 { if len(hostPort) > 0 {
@ -202,22 +229,14 @@ func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
} }
port, err := NewPort(strings.ToLower(proto), containerPort) port, err := NewPort(strings.ToLower(proto), containerPort)
if err != nil { if err != nil {
return nil, nil, err return nil, err
}
if _, exists := exposedPorts[port]; !exists {
exposedPorts[port] = struct{}{}
} }
binding := PortBinding{ binding := PortBinding{
HostIP: rawIP, HostIP: ip,
HostPort: hostPort, HostPort: hostPort,
} }
bslice, exists := bindings[port] ports = append(ports, PortMapping{Port: port, Binding: binding})
if !exists {
bslice = []PortBinding{}
} }
bindings[port] = append(bslice, binding) return ports, nil
}
}
return exposedPorts, bindings, nil
} }

View File

@ -8,6 +8,7 @@ import (
// PartParser parses and validates the specified string (data) using the specified template // PartParser parses and validates the specified string (data) using the specified template
// e.g. ip:public:private -> 192.168.0.1:80:8000 // e.g. ip:public:private -> 192.168.0.1:80:8000
// DEPRECATED: do not use, this function may be removed in a future version
func PartParser(template, data string) (map[string]string, error) { func PartParser(template, data string) (map[string]string, error) {
// ip:public:private // ip:public:private
var ( var (

View File

@ -79,11 +79,3 @@ func (a dummyAddr) Network() string {
func (a dummyAddr) String() string { func (a dummyAddr) String() string {
return string(a) return string(a)
} }
// timeoutError is used when there is a timeout with a connection
// this implements the net.Error interface
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }

View File

@ -2,6 +2,7 @@
package sockets package sockets
import ( import (
"errors"
"net" "net"
"net/http" "net/http"
"time" "time"
@ -10,6 +11,9 @@ import (
// Why 32? See https://github.com/docker/docker/pull/8035. // Why 32? See https://github.com/docker/docker/pull/8035.
const defaultTimeout = 32 * time.Second const defaultTimeout = 32 * time.Second
// ErrProtocolNotAvailable is returned when a given transport protocol is not provided by the operating system.
var ErrProtocolNotAvailable = errors.New("protocol not available")
// ConfigureTransport configures the specified Transport according to the // ConfigureTransport configures the specified Transport according to the
// specified proto and addr. // specified proto and addr.
// If the proto is unix (using a unix socket to communicate) or npipe the // If the proto is unix (using a unix socket to communicate) or npipe the
@ -17,17 +21,9 @@ const defaultTimeout = 32 * time.Second
func ConfigureTransport(tr *http.Transport, proto, addr string) error { func ConfigureTransport(tr *http.Transport, proto, addr string) error {
switch proto { switch proto {
case "unix": case "unix":
// No need for compression in local communications. return configureUnixTransport(tr, proto, addr)
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return net.DialTimeout(proto, addr, defaultTimeout)
}
case "npipe": case "npipe":
// No need for compression in local communications. return configureNpipeTransport(tr, proto, addr)
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return DialPipe(addr, defaultTimeout)
}
default: default:
tr.Proxy = http.ProxyFromEnvironment tr.Proxy = http.ProxyFromEnvironment
dialer, err := DialerFromEnvironment(&net.Dialer{ dialer, err := DialerFromEnvironment(&net.Dialer{

View File

@ -3,11 +3,31 @@
package sockets package sockets
import ( import (
"fmt"
"net" "net"
"net/http"
"syscall" "syscall"
"time" "time"
) )
const maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path)
func configureUnixTransport(tr *http.Transport, proto, addr string) error {
if len(addr) > maxUnixSocketPathSize {
return fmt.Errorf("Unix socket path %q is too long", addr)
}
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return net.DialTimeout(proto, addr, defaultTimeout)
}
return nil
}
func configureNpipeTransport(tr *http.Transport, proto, addr string) error {
return ErrProtocolNotAvailable
}
// DialPipe connects to a Windows named pipe. // DialPipe connects to a Windows named pipe.
// This is not supported on other OSes. // This is not supported on other OSes.
func DialPipe(_ string, _ time.Duration) (net.Conn, error) { func DialPipe(_ string, _ time.Duration) (net.Conn, error) {

View File

@ -2,11 +2,25 @@ package sockets
import ( import (
"net" "net"
"net/http"
"time" "time"
"github.com/Microsoft/go-winio" "github.com/Microsoft/go-winio"
) )
func configureUnixTransport(tr *http.Transport, proto, addr string) error {
return ErrProtocolNotAvailable
}
func configureNpipeTransport(tr *http.Transport, proto, addr string) error {
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return DialPipe(addr, defaultTimeout)
}
return nil
}
// DialPipe connects to a Windows named pipe. // DialPipe connects to a Windows named pipe.
func DialPipe(addr string, timeout time.Duration) (net.Conn, error) { func DialPipe(addr string, timeout time.Duration) (net.Conn, error) {
return winio.DialPipe(addr, &timeout) return winio.DialPipe(addr, &timeout)

View File

@ -7,7 +7,7 @@ import (
) )
// NewTCPSocket creates a TCP socket listener with the specified address and // NewTCPSocket creates a TCP socket listener with the specified address and
// and the specified tls configuration. If TLSConfig is set, will encapsulate the // the specified tls configuration. If TLSConfig is set, will encapsulate the
// TCP listener inside a TLS one. // TCP listener inside a TLS one.
func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) { func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) {
l, err := net.Listen("tcp", addr) l, err := net.Listen("tcp", addr)

View File

@ -0,0 +1,21 @@
// +build go1.7
package tlsconfig
import (
"crypto/x509"
"runtime"
"github.com/Sirupsen/logrus"
)
// SystemCertPool returns a copy of the system cert pool,
// returns an error if failed to load or empty pool on windows.
func SystemCertPool() (*x509.CertPool, error) {
certpool, err := x509.SystemCertPool()
if err != nil && runtime.GOOS == "windows" {
logrus.Warnf("Unable to use system certificate pool: %v", err)
return x509.NewCertPool(), nil
}
return certpool, err
}

View File

@ -0,0 +1,16 @@
// +build !go1.7
package tlsconfig
import (
"crypto/x509"
"github.com/Sirupsen/logrus"
)
// SystemCertPool returns an new empty cert pool,
// accessing system cert pool is supported in go 1.7
func SystemCertPool() (*x509.CertPool, error) {
logrus.Warn("Unable to use system certificate pool: requires building with go 1.7 or later")
return x509.NewCertPool(), nil
}

View File

@ -46,44 +46,46 @@ var acceptedCBCCiphers = []uint16{
// known weak algorithms removed. // known weak algorithms removed.
var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...) var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...)
// ServerDefault is a secure-enough TLS configuration for the server TLS configuration. // ServerDefault returns a secure-enough TLS configuration for the server TLS configuration.
var ServerDefault = tls.Config{ func ServerDefault() *tls.Config {
return &tls.Config{
// Avoid fallback to SSL protocols < TLS1.0 // Avoid fallback to SSL protocols < TLS1.0
MinVersion: tls.VersionTLS10, MinVersion: tls.VersionTLS10,
PreferServerCipherSuites: true, PreferServerCipherSuites: true,
CipherSuites: DefaultServerAcceptedCiphers, CipherSuites: DefaultServerAcceptedCiphers,
}
} }
// ClientDefault is a secure-enough TLS configuration for the client TLS configuration. // ClientDefault returns a secure-enough TLS configuration for the client TLS configuration.
var ClientDefault = tls.Config{ func ClientDefault() *tls.Config {
return &tls.Config{
// Prefer TLS1.2 as the client minimum // Prefer TLS1.2 as the client minimum
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
CipherSuites: clientCipherSuites, CipherSuites: clientCipherSuites,
}
} }
// certPool returns an X.509 certificate pool from `caFile`, the certificate file. // certPool returns an X.509 certificate pool from `caFile`, the certificate file.
func certPool(caFile string) (*x509.CertPool, error) { func certPool(caFile string) (*x509.CertPool, error) {
// If we should verify the server, we need to load a trusted ca // If we should verify the server, we need to load a trusted ca
certPool := x509.NewCertPool() certPool, err := SystemCertPool()
if err != nil {
return nil, fmt.Errorf("failed to read system certificates: %v", err)
}
pem, err := ioutil.ReadFile(caFile) pem, err := ioutil.ReadFile(caFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("Could not read CA certificate %q: %v", caFile, err) return nil, fmt.Errorf("could not read CA certificate %q: %v", caFile, err)
} }
if !certPool.AppendCertsFromPEM(pem) { if !certPool.AppendCertsFromPEM(pem) {
return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile) return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile)
} }
s := certPool.Subjects() logrus.Debugf("Trusting %d certs", len(certPool.Subjects()))
subjects := make([]string, len(s))
for i, subject := range s {
subjects[i] = string(subject)
}
logrus.Debugf("Trusting certs with subjects: %v", subjects)
return certPool, nil return certPool, nil
} }
// Client returns a TLS configuration meant to be used by a client. // Client returns a TLS configuration meant to be used by a client.
func Client(options Options) (*tls.Config, error) { func Client(options Options) (*tls.Config, error) {
tlsConfig := ClientDefault tlsConfig := ClientDefault()
tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
if !options.InsecureSkipVerify && options.CAFile != "" { if !options.InsecureSkipVerify && options.CAFile != "" {
CAs, err := certPool(options.CAFile) CAs, err := certPool(options.CAFile)
@ -101,12 +103,12 @@ func Client(options Options) (*tls.Config, error) {
tlsConfig.Certificates = []tls.Certificate{tlsCert} tlsConfig.Certificates = []tls.Certificate{tlsCert}
} }
return &tlsConfig, nil return tlsConfig, nil
} }
// Server returns a TLS configuration meant to be used by a server. // Server returns a TLS configuration meant to be used by a server.
func Server(options Options) (*tls.Config, error) { func Server(options Options) (*tls.Config, error) {
tlsConfig := ServerDefault tlsConfig := ServerDefault()
tlsConfig.ClientAuth = options.ClientAuth tlsConfig.ClientAuth = options.ClientAuth
tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile) tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
if err != nil { if err != nil {
@ -123,5 +125,5 @@ func Server(options Options) (*tls.Config, error) {
} }
tlsConfig.ClientCAs = CAs tlsConfig.ClientCAs = CAs
} }
return &tlsConfig, nil return tlsConfig, nil
} }