diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 077c97ee..fa14a0ef 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -344,11 +344,11 @@ }, { "ImportPath": "k8s.io/api", - "Rev": "63b8484e5766" + "Rev": "c43c3e1d5a79" }, { "ImportPath": "k8s.io/apimachinery", - "Rev": "e709f673dfd9" + "Rev": "f1cbc0c8ff07" }, { "ImportPath": "k8s.io/gengo", diff --git a/go.mod b/go.mod index ccd913dd..79db87f4 100644 --- a/go.mod +++ b/go.mod @@ -26,8 +26,8 @@ require ( golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c google.golang.org/appengine v1.5.0 // indirect - k8s.io/api v0.0.0-20190830034750-63b8484e5766 - k8s.io/apimachinery v0.0.0-20190830034626-e709f673dfd9 + k8s.io/api v0.0.0-20190830074751-c43c3e1d5a79 + k8s.io/apimachinery v0.0.0-20190830154629-f1cbc0c8ff07 k8s.io/klog v0.4.0 k8s.io/utils v0.0.0-20190801114015-581e00157fb1 sigs.k8s.io/yaml v1.1.0 @@ -41,6 +41,6 @@ replace ( golang.org/x/sys => golang.org/x/sys v0.0.0-20190209173611-3b5209105503 golang.org/x/text => golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db golang.org/x/time => golang.org/x/time v0.0.0-20161028155119-f51c12702a4d - k8s.io/api => k8s.io/api v0.0.0-20190830034750-63b8484e5766 - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190830034626-e709f673dfd9 + k8s.io/api => k8s.io/api v0.0.0-20190830074751-c43c3e1d5a79 + k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190830154629-f1cbc0c8ff07 ) diff --git a/go.sum b/go.sum index 2ea68075..c0b4b308 100644 --- a/go.sum +++ b/go.sum @@ -178,8 +178,8 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.0.0-20190830034750-63b8484e5766/go.mod h1:ZPzpiABYnAONRNqslkGU1hppe1VEXgsEJlFRIbP75G8= -k8s.io/apimachinery v0.0.0-20190830034626-e709f673dfd9/go.mod h1:OOC7vMsHyCzra5xu6r1bRXkXMH76iww9BeHOAAJof4U= +k8s.io/api v0.0.0-20190830074751-c43c3e1d5a79/go.mod h1:ZPzpiABYnAONRNqslkGU1hppe1VEXgsEJlFRIbP75G8= +k8s.io/apimachinery v0.0.0-20190830154629-f1cbc0c8ff07/go.mod h1:OOC7vMsHyCzra5xu6r1bRXkXMH76iww9BeHOAAJof4U= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= diff --git a/rest/config.go b/rest/config.go index e2599472..fb81fb7b 100644 --- a/rest/config.go +++ b/rest/config.go @@ -211,6 +211,12 @@ type TLSClientConfig struct { // CAData holds PEM-encoded bytes (typically read from a root certificates bundle). // CAData takes precedence over CAFile CAData []byte + + // NextProtos is a list of supported application level protocols, in order of preference. + // Used to populate tls.Config.NextProtos. + // To indicate to the server http/1.1 is preferred over http/2, set to ["http/1.1", "h2"] (though the server is free to ignore that preference). + // To use only http/1.1, set to ["http/1.1"]. + NextProtos []string } var _ fmt.Stringer = TLSClientConfig{} @@ -236,6 +242,7 @@ func (c TLSClientConfig) String() string { CertData: c.CertData, KeyData: c.KeyData, CAData: c.CAData, + NextProtos: c.NextProtos, } // Explicitly mark non-empty credential fields as redacted. if len(cc.CertData) != 0 { @@ -503,6 +510,7 @@ func AnonymousClientConfig(config *Config) *Config { ServerName: config.ServerName, CAFile: config.TLSClientConfig.CAFile, CAData: config.TLSClientConfig.CAData, + NextProtos: config.TLSClientConfig.NextProtos, }, RateLimiter: config.RateLimiter, UserAgent: config.UserAgent, @@ -541,6 +549,7 @@ func CopyConfig(config *Config) *Config { CertData: config.TLSClientConfig.CertData, KeyData: config.TLSClientConfig.KeyData, CAData: config.TLSClientConfig.CAData, + NextProtos: config.TLSClientConfig.NextProtos, }, UserAgent: config.UserAgent, DisableCompression: config.DisableCompression, diff --git a/rest/config_test.go b/rest/config_test.go index f4ba5f00..df5778ce 100644 --- a/rest/config_test.go +++ b/rest/config_test.go @@ -493,10 +493,11 @@ func TestConfigSprint(t *testing.T) { Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}}, }, TLSClientConfig: TLSClientConfig{ - CertFile: "a.crt", - KeyFile: "a.key", - CertData: []byte("fake cert"), - KeyData: []byte("fake key"), + CertFile: "a.crt", + KeyFile: "a.key", + CertData: []byte("fake cert"), + KeyData: []byte("fake key"), + NextProtos: []string{"h2", "http/1.1"}, }, UserAgent: "gobot", Transport: &fakeRoundTripper{}, @@ -508,7 +509,7 @@ func TestConfigSprint(t *testing.T) { Dial: fakeDialFunc, } want := fmt.Sprintf( - `&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), ExecProvider:api.AuthProviderConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: ""}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil)}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p)}`, + `&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), ExecProvider:api.AuthProviderConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: ""}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil), NextProtos:[]string{"h2", "http/1.1"}}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p)}`, c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc, ) diff --git a/rest/transport.go b/rest/transport.go index 81b9dfde..0800e4ec 100644 --- a/rest/transport.go +++ b/rest/transport.go @@ -74,6 +74,7 @@ func (c *Config) TransportConfig() (*transport.Config, error) { CertData: c.CertData, KeyFile: c.KeyFile, KeyData: c.KeyData, + NextProtos: c.NextProtos, }, Username: c.Username, Password: c.Password, diff --git a/rest/zz_generated.deepcopy.go b/rest/zz_generated.deepcopy.go index c1ab45f3..da4a1624 100644 --- a/rest/zz_generated.deepcopy.go +++ b/rest/zz_generated.deepcopy.go @@ -38,6 +38,11 @@ func (in *TLSClientConfig) DeepCopyInto(out *TLSClientConfig) { *out = make([]byte, len(*in)) copy(*out, *in) } + if in.NextProtos != nil { + in, out := &in.NextProtos, &out.NextProtos + *out = make([]string, len(*in)) + copy(*out, *in) + } return } diff --git a/transport/cache.go b/transport/cache.go index 12c66903..980d36ae 100644 --- a/transport/cache.go +++ b/transport/cache.go @@ -20,6 +20,7 @@ import ( "fmt" "net" "net/http" + "strings" "sync" "time" @@ -45,6 +46,7 @@ type tlsCacheKey struct { keyData string getCert string serverName string + nextProtos string dial string disableCompression bool } @@ -114,6 +116,7 @@ func tlsConfigKey(c *Config) (tlsCacheKey, error) { keyData: string(c.TLS.KeyData), getCert: fmt.Sprintf("%p", c.TLS.GetCert), serverName: c.TLS.ServerName, + nextProtos: strings.Join(c.TLS.NextProtos, ","), dial: fmt.Sprintf("%p", c.Dial), disableCompression: c.DisableCompression, }, nil diff --git a/transport/cache_test.go b/transport/cache_test.go index 9b740cde..8b9779e6 100644 --- a/transport/cache_test.go +++ b/transport/cache_test.go @@ -126,6 +126,8 @@ func TestTLSConfigKey(t *testing.T) { GetCert: getCert, }, }, + "http2, http1.1": {TLS: TLSConfig{NextProtos: []string{"h2", "http/1.1"}}}, + "http1.1-only": {TLS: TLSConfig{NextProtos: []string{"http/1.1"}}}, } for nameA, valueA := range uniqueConfigurations { for nameB, valueB := range uniqueConfigurations { diff --git a/transport/config.go b/transport/config.go index 8a73589e..9e18d11d 100644 --- a/transport/config.go +++ b/transport/config.go @@ -126,5 +126,11 @@ type TLSConfig struct { CertData []byte // Bytes of the PEM-encoded client certificate. Supercedes CertFile. KeyData []byte // Bytes of the PEM-encoded client key. Supercedes KeyFile. + // NextProtos is a list of supported application level protocols, in order of preference. + // Used to populate tls.Config.NextProtos. + // To indicate to the server http/1.1 is preferred over http/2, set to ["http/1.1", "h2"] (though the server is free to ignore that preference). + // To use only http/1.1, set to ["http/1.1"]. + NextProtos []string + GetCert func() (*tls.Certificate, error) // Callback that returns a TLS client certificate. CertData, CertFile, KeyData and KeyFile supercede this field. } diff --git a/transport/transport.go b/transport/transport.go index 1815c11f..cd8de982 100644 --- a/transport/transport.go +++ b/transport/transport.go @@ -56,7 +56,7 @@ func New(config *Config) (http.RoundTripper, error) { // TLSConfigFor returns a tls.Config that will provide the transport level security defined // by the provided Config. Will return nil if no transport level security is requested. func TLSConfigFor(c *Config) (*tls.Config, error) { - if !(c.HasCA() || c.HasCertAuth() || c.HasCertCallback() || c.TLS.Insecure || len(c.TLS.ServerName) > 0) { + if !(c.HasCA() || c.HasCertAuth() || c.HasCertCallback() || c.TLS.Insecure || len(c.TLS.ServerName) > 0 || len(c.TLS.NextProtos) > 0) { return nil, nil } if c.HasCA() && c.TLS.Insecure { @@ -73,6 +73,7 @@ func TLSConfigFor(c *Config) (*tls.Config, error) { MinVersion: tls.VersionTLS12, InsecureSkipVerify: c.TLS.Insecure, ServerName: c.TLS.ServerName, + NextProtos: c.TLS.NextProtos, } if c.HasCA() {