summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Potter <tpot@hpe.com>2016-12-19 08:31:22 +1100
committerTim Potter <tpot@hpe.com>2016-12-19 08:31:22 +1100
commitcc6cd82e56351b62663c108921fd44c2285cd73c (patch)
tree5d2c3c9c965cc90422710d6f81ec7871418e31ab
parent33633fe4fe3b7a212a5ff66a8ddfea0f87489b7c (diff)
New upstream version 0.2.1+git20161115.12.4ccf312
-rw-r--r--nat/nat.go57
-rw-r--r--nat/nat_test.go43
-rw-r--r--nat/parse.go1
-rw-r--r--sockets/sockets.go16
-rw-r--r--sockets/sockets_unix.go20
-rw-r--r--sockets/sockets_windows.go14
-rw-r--r--sockets/tcp_socket.go2
-rw-r--r--tlsconfig/certpool_go17.go21
-rw-r--r--tlsconfig/certpool_other.go16
-rw-r--r--tlsconfig/config.go41
10 files changed, 164 insertions, 67 deletions
diff --git a/nat/nat.go b/nat/nat.go
index bca3c2c..4d5f5ae 100644
--- a/nat/nat.go
+++ b/nat/nat.go
@@ -85,14 +85,10 @@ func (p Port) Port() string {
// Int returns the port number of a Port as an int
func (p Port) Int() int {
portStr := p.Port()
- if len(portStr) == 0 {
- return 0
- }
-
// 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()
- port, _ := strconv.ParseUint(portStr, 10, 16)
- return int(port)
+ port, _ := ParsePort(portStr)
+ return port
}
// Range returns the start/end port numbers of a Port range as ints
@@ -159,33 +155,36 @@ type PortMapping struct {
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) {
- proto := "tcp"
+ var proto string
+ rawIP, hostPort, containerPort := splitParts(rawPort)
+ proto, containerPort = SplitProtoPort(containerPort)
- 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)
+ // Strip [] from IPV6 addresses
+ ip, _, err := net.SplitHostPort(rawIP + ":")
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("Invalid ip address %v: %s", rawIP, err)
}
-
- var (
- containerPort = parts["containerPort"]
- rawIP = parts["ip"]
- hostPort = parts["hostPort"]
- )
-
- if rawIP != "" && net.ParseIP(rawIP) == nil {
- return nil, fmt.Errorf("Invalid ip address: %s", rawIP)
+ if ip != "" && net.ParseIP(ip) == nil {
+ return nil, fmt.Errorf("Invalid ip address: %s", ip)
}
if containerPort == "" {
return nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
@@ -234,7 +233,7 @@ func ParsePortSpec(rawPort string) ([]PortMapping, error) {
}
binding := PortBinding{
- HostIP: rawIP,
+ HostIP: ip,
HostPort: hostPort,
}
ports = append(ports, PortMapping{Port: port, Binding: binding})
diff --git a/nat/nat_test.go b/nat/nat_test.go
index 2253a6c..787d5ac 100644
--- a/nat/nat_test.go
+++ b/nat/nat_test.go
@@ -2,6 +2,8 @@ package nat
import (
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func TestParsePort(t *testing.T) {
@@ -173,10 +175,7 @@ func TestSplitProtoPort(t *testing.T) {
func TestParsePortSpecFull(t *testing.T) {
portMappings, err := ParsePortSpec("0.0.0.0:1234-1235:3333-3334/tcp")
-
- if err != nil {
- t.Fatalf("Error while parsing port spec: %s", err)
- }
+ assert.Nil(t, err)
expected := []PortMapping{
{
@@ -195,15 +194,39 @@ func TestParsePortSpecFull(t *testing.T) {
},
}
- if len(portMappings) != len(expected) {
- t.Fatalf("Expected slice of size: %d, got size %d", len(expected), len(portMappings))
+ assert.Equal(t, expected, portMappings)
+}
+
+func TestPartPortSpecIPV6(t *testing.T) {
+ portMappings, err := ParsePortSpec("[2001:4860:0:2001::68]::333")
+ assert.Nil(t, err)
+
+ expected := []PortMapping{
+ {
+ Port: "333/tcp",
+ Binding: PortBinding{
+ HostIP: "2001:4860:0:2001::68",
+ HostPort: "",
+ },
+ },
}
+ assert.Equal(t, expected, portMappings)
+}
- for i := 0; i < len(expected); i++ {
- if portMappings[i] != expected[i] {
- t.Fatalf("Expected: %v\nActual: %v", expected, portMappings)
- }
+func TestPartPortSpecIPV6WithHostPort(t *testing.T) {
+ portMappings, err := ParsePortSpec("[::1]:80:80")
+ assert.Nil(t, err)
+
+ expected := []PortMapping{
+ {
+ Port: "80/tcp",
+ Binding: PortBinding{
+ HostIP: "::1",
+ HostPort: "80",
+ },
+ },
}
+ assert.Equal(t, expected, portMappings)
}
func TestParsePortSpecs(t *testing.T) {
diff --git a/nat/parse.go b/nat/parse.go
index 8720502..892adf8 100644
--- a/nat/parse.go
+++ b/nat/parse.go
@@ -8,6 +8,7 @@ import (
// PartParser parses and validates the specified string (data) using the specified template
// 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) {
// ip:public:private
var (
diff --git a/sockets/sockets.go b/sockets/sockets.go
index 1739cec..a1d7beb 100644
--- a/sockets/sockets.go
+++ b/sockets/sockets.go
@@ -2,6 +2,7 @@
package sockets
import (
+ "errors"
"net"
"net/http"
"time"
@@ -10,6 +11,9 @@ import (
// Why 32? See https://github.com/docker/docker/pull/8035.
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
// specified proto and addr.
// 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 {
switch proto {
case "unix":
- // No need for compression in local communications.
- tr.DisableCompression = true
- tr.Dial = func(_, _ string) (net.Conn, error) {
- return net.DialTimeout(proto, addr, defaultTimeout)
- }
+ return configureUnixTransport(tr, proto, addr)
case "npipe":
- // No need for compression in local communications.
- tr.DisableCompression = true
- tr.Dial = func(_, _ string) (net.Conn, error) {
- return DialPipe(addr, defaultTimeout)
- }
+ return configureNpipeTransport(tr, proto, addr)
default:
tr.Proxy = http.ProxyFromEnvironment
dialer, err := DialerFromEnvironment(&net.Dialer{
diff --git a/sockets/sockets_unix.go b/sockets/sockets_unix.go
index b255ac9..386cf0d 100644
--- a/sockets/sockets_unix.go
+++ b/sockets/sockets_unix.go
@@ -3,11 +3,31 @@
package sockets
import (
+ "fmt"
"net"
+ "net/http"
"syscall"
"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.
// This is not supported on other OSes.
func DialPipe(_ string, _ time.Duration) (net.Conn, error) {
diff --git a/sockets/sockets_windows.go b/sockets/sockets_windows.go
index 1f3540b..5c21644 100644
--- a/sockets/sockets_windows.go
+++ b/sockets/sockets_windows.go
@@ -2,11 +2,25 @@ package sockets
import (
"net"
+ "net/http"
"time"
"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.
func DialPipe(addr string, timeout time.Duration) (net.Conn, error) {
return winio.DialPipe(addr, &timeout)
diff --git a/sockets/tcp_socket.go b/sockets/tcp_socket.go
index 8a82727..53cbb6c 100644
--- a/sockets/tcp_socket.go
+++ b/sockets/tcp_socket.go
@@ -7,7 +7,7 @@ import (
)
// 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.
func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) {
l, err := net.Listen("tcp", addr)
diff --git a/tlsconfig/certpool_go17.go b/tlsconfig/certpool_go17.go
new file mode 100644
index 0000000..352d342
--- /dev/null
+++ b/tlsconfig/certpool_go17.go
@@ -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
+}
diff --git a/tlsconfig/certpool_other.go b/tlsconfig/certpool_other.go
new file mode 100644
index 0000000..262c95e
--- /dev/null
+++ b/tlsconfig/certpool_other.go
@@ -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
+}
diff --git a/tlsconfig/config.go b/tlsconfig/config.go
index 1ba0439..8bbffcf 100644
--- a/tlsconfig/config.go
+++ b/tlsconfig/config.go
@@ -46,28 +46,35 @@ var acceptedCBCCiphers = []uint16{
// known weak algorithms removed.
var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...)
-// ServerDefault is a secure-enough TLS configuration for the server TLS configuration.
-var ServerDefault = tls.Config{
- // Avoid fallback to SSL protocols < TLS1.0
- MinVersion: tls.VersionTLS10,
- PreferServerCipherSuites: true,
- CipherSuites: DefaultServerAcceptedCiphers,
+// ServerDefault returns a secure-enough TLS configuration for the server TLS configuration.
+func ServerDefault() *tls.Config {
+ return &tls.Config{
+ // Avoid fallback to SSL protocols < TLS1.0
+ MinVersion: tls.VersionTLS10,
+ PreferServerCipherSuites: true,
+ CipherSuites: DefaultServerAcceptedCiphers,
+ }
}
-// ClientDefault is a secure-enough TLS configuration for the client TLS configuration.
-var ClientDefault = tls.Config{
- // Prefer TLS1.2 as the client minimum
- MinVersion: tls.VersionTLS12,
- CipherSuites: clientCipherSuites,
+// ClientDefault returns a secure-enough TLS configuration for the client TLS configuration.
+func ClientDefault() *tls.Config {
+ return &tls.Config{
+ // Prefer TLS1.2 as the client minimum
+ MinVersion: tls.VersionTLS12,
+ CipherSuites: clientCipherSuites,
+ }
}
// certPool returns an X.509 certificate pool from `caFile`, the certificate file.
func certPool(caFile string) (*x509.CertPool, error) {
// 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)
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) {
return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile)
@@ -78,7 +85,7 @@ func certPool(caFile string) (*x509.CertPool, error) {
// Client returns a TLS configuration meant to be used by a client.
func Client(options Options) (*tls.Config, error) {
- tlsConfig := ClientDefault
+ tlsConfig := ClientDefault()
tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
if !options.InsecureSkipVerify && options.CAFile != "" {
CAs, err := certPool(options.CAFile)
@@ -96,12 +103,12 @@ func Client(options Options) (*tls.Config, error) {
tlsConfig.Certificates = []tls.Certificate{tlsCert}
}
- return &tlsConfig, nil
+ return tlsConfig, nil
}
// Server returns a TLS configuration meant to be used by a server.
func Server(options Options) (*tls.Config, error) {
- tlsConfig := ServerDefault
+ tlsConfig := ServerDefault()
tlsConfig.ClientAuth = options.ClientAuth
tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
if err != nil {
@@ -118,5 +125,5 @@ func Server(options Options) (*tls.Config, error) {
}
tlsConfig.ClientCAs = CAs
}
- return &tlsConfig, nil
+ return tlsConfig, nil
}