Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow client.Config to be used for HTTP2 and WebSocket connections #3959

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 42 additions & 60 deletions pkg/client/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,8 @@ type Config struct {
// TODO: demonstrate an OAuth2 compatible client.
BearerToken string

// Server requires TLS client certificate authentication
CertFile string
// Server requires TLS client certificate authentication
KeyFile string
// Trusted root certificates for server
CAFile string

// CertData holds PEM-encoded bytes (typically read from a client certificate file).
// CertData takes precedence over CertFile
CertData []byte
// KeyData holds PEM-encoded bytes (typically read from a client certificate key file).
// KeyData takes precedence over KeyFile
KeyData []byte
// CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
// CAData takes precedence over CAFile
CAData []byte
// TLSClientConfig contains settings to enable transport layer security
TLSClientConfig

// Server should be accessed without verifying the TLS
// certificate. For testing only.
Expand All @@ -92,11 +78,17 @@ type KubeletConfig struct {
Port uint
EnableHttps bool

// TLS Configuration, only applies if EnableHttps is true.
// TLSClientConfig contains settings to enable transport layer security
TLSClientConfig
}

// TLSClientConfig contains settings to enable transport layer security
type TLSClientConfig struct {
// Server requires TLS client certificate authentication
CertFile string
// TLS Configuration, only applies if EnableHttps is true.
// Server requires TLS client certificate authentication
KeyFile string
// TLS Configuration, only applies if EnableHttps is true.
// Trusted root certificates for server
CAFile string

// CertData holds PEM-encoded bytes (typically read from a client certificate file).
Expand Down Expand Up @@ -215,62 +207,52 @@ func TransportFor(config *Config) (http.RoundTripper, error) {
if config.Transport != nil && (hasCA || hasCert || config.Insecure) {
return nil, fmt.Errorf("using a custom transport with TLS certificate options or the insecure flag is not allowed")
}
if hasCA && config.Insecure {
return nil, fmt.Errorf("specifying a root certificates file with the insecure flag is not allowed")

tlsConfig, err := TLSConfigFor(config)
if err != nil {
return nil, err
}

var transport http.RoundTripper
switch {
case config.Transport != nil:
if config.Transport != nil {
transport = config.Transport
case hasCert:
var (
certData, keyData, caData []byte
err error
)
if certData, err = dataFromSliceOrFile(config.CertData, config.CertFile); err != nil {
return nil, err
}
if keyData, err = dataFromSliceOrFile(config.KeyData, config.KeyFile); err != nil {
return nil, err
} else {
if tlsConfig != nil {
transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
} else {
transport = http.DefaultTransport
}
if caData, err = dataFromSliceOrFile(config.CAData, config.CAFile); err != nil {
return nil, err
}
if transport, err = NewClientCertTLSTransport(certData, keyData, caData); err != nil {
return nil, err
}
case hasCA:
var (
caData []byte
err error
)
if caData, err = dataFromSliceOrFile(config.CAData, config.CAFile); err != nil {
return nil, err
}
if transport, err = NewTLSTransport(caData); err != nil {
return nil, err
}
case config.Insecure:
transport = NewUnsafeTLSTransport()
default:
transport = http.DefaultTransport
}

transport, err = HTTPWrappersForConfig(config, transport)
if err != nil {
return nil, err
}

// TODO: use the config context to wrap a transport

return transport, nil
}

// HTTPWrappersForConfig wraps a round tripper with any relevant layered behavior from the
// config. Exposed to allow more clients that need HTTP-like behavior but then must hijack
// the underlying connection (like WebSocket or HTTP2 clients). Pure HTTP clients should use
// the higher level TransportFor or RESTClientFor methods.
func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTripper, error) {
// Set authentication wrappers
hasBasicAuth := config.Username != "" || config.Password != ""
if hasBasicAuth && config.BearerToken != "" {
return nil, fmt.Errorf("username/password or bearer token may be set, but not both")
}
switch {
case config.BearerToken != "":
transport = NewBearerAuthRoundTripper(config.BearerToken, transport)
rt = NewBearerAuthRoundTripper(config.BearerToken, rt)
case hasBasicAuth:
transport = NewBasicAuthRoundTripper(config.Username, config.Password, transport)
rt = NewBasicAuthRoundTripper(config.Username, config.Password, rt)
}

// TODO: use the config context to wrap a transport

return transport, nil
return rt, nil
}

// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
Expand Down
60 changes: 39 additions & 21 deletions pkg/client/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,54 +104,68 @@ func TestTransportFor(t *testing.T) {
"ca transport": {
TLS: true,
Config: &Config{
CAData: []byte(rootCACert),
TLSClientConfig: TLSClientConfig{
CAData: []byte(rootCACert),
},
},
},
"bad ca file transport": {
Err: true,
Config: &Config{
CAFile: "invalid file",
TLSClientConfig: TLSClientConfig{
CAFile: "invalid file",
},
},
},
"ca data overriding bad ca file transport": {
TLS: true,
Config: &Config{
CAData: []byte(rootCACert),
CAFile: "invalid file",
TLSClientConfig: TLSClientConfig{
CAData: []byte(rootCACert),
CAFile: "invalid file",
},
},
},

"cert transport": {
TLS: true,
Config: &Config{
CertData: []byte(certData),
KeyData: []byte(keyData),
CAData: []byte(rootCACert),
TLSClientConfig: TLSClientConfig{
CertData: []byte(certData),
KeyData: []byte(keyData),
CAData: []byte(rootCACert),
},
},
},
"bad cert data transport": {
Err: true,
Config: &Config{
CertData: []byte(certData),
KeyData: []byte("bad key data"),
CAData: []byte(rootCACert),
TLSClientConfig: TLSClientConfig{
CertData: []byte(certData),
KeyData: []byte("bad key data"),
CAData: []byte(rootCACert),
},
},
},
"bad file cert transport": {
Err: true,
Config: &Config{
CertData: []byte(certData),
KeyFile: "invalid file",
CAData: []byte(rootCACert),
TLSClientConfig: TLSClientConfig{
CertData: []byte(certData),
KeyFile: "invalid file",
CAData: []byte(rootCACert),
},
},
},
"key data overriding bad file cert transport": {
TLS: true,
Config: &Config{
CertData: []byte(certData),
KeyData: []byte(keyData),
KeyFile: "invalid file",
CAData: []byte(rootCACert),
TLSClientConfig: TLSClientConfig{
CertData: []byte(certData),
KeyData: []byte(keyData),
KeyFile: "invalid file",
CAData: []byte(rootCACert),
},
},
},
}
Expand Down Expand Up @@ -206,15 +220,19 @@ func TestIsConfigTransportTLS(t *testing.T) {
},
{
Config: &Config{
Host: "localhost",
CertFile: "foo",
Host: "localhost",
TLSClientConfig: TLSClientConfig{
CertFile: "foo",
},
},
TransportTLS: true,
},
{
Config: &Config{
Host: "///:://localhost",
CertFile: "foo",
Host: "///:://localhost",
TLSClientConfig: TLSClientConfig{
CertFile: "foo",
},
},
TransportTLS: false,
},
Expand Down
44 changes: 14 additions & 30 deletions pkg/client/kubelet.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,41 +59,25 @@ type HTTPKubeletClient struct {
EnableHttps bool
}

// TODO: this structure is questionable, it should be using client.Config and overriding defaults.
func NewKubeletClient(config *KubeletConfig) (KubeletClient, error) {
transport := http.DefaultTransport
hasCA := len(config.CAFile) > 0 || len(config.CAData) > 0
hasCert := len(config.CertFile) > 0 || len(config.CertData) > 0
if hasCert {
var (
certData, keyData, caData []byte
err error
)
if certData, err = dataFromSliceOrFile(config.CertData, config.CertFile); err != nil {
return nil, err
}
if keyData, err = dataFromSliceOrFile(config.KeyData, config.KeyFile); err != nil {
return nil, err
}
if caData, err = dataFromSliceOrFile(config.CAData, config.CAFile); err != nil {
return nil, err
}
if transport, err = NewClientCertTLSTransport(certData, keyData, caData); err != nil {
return nil, err
}
} else if hasCA {
var (
caData []byte
err error
)
if caData, err = dataFromSliceOrFile(config.CAData, config.CAFile); err != nil {
return nil, err
}
if transport, err = NewTLSTransport(caData); err != nil {
return nil, err

tlsConfig, err := TLSConfigFor(&Config{
TLSClientConfig: config.TLSClientConfig,
})
if err != nil {
return nil, err
}
if tlsConfig != nil {
transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
}

c := &http.Client{Transport: transport}
c := &http.Client{
Transport: transport,
}
return &HTTPKubeletClient{
Client: c,
Port: config.Port,
Expand Down
20 changes: 12 additions & 8 deletions pkg/client/kubelet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,11 @@ func TestNewKubeletClientTLSInvalid(t *testing.T) {
Port: 9000,
EnableHttps: true,
//Invalid certificate and key path
CertFile: "./testdata/mycertinvalid.cer",
KeyFile: "./testdata/mycertinvalid.key",
CAFile: "./testdata/myCA.cer",
TLSClientConfig: TLSClientConfig{
CertFile: "./testdata/mycertinvalid.cer",
KeyFile: "./testdata/mycertinvalid.key",
CAFile: "./testdata/myCA.cer",
},
}

client, err := NewKubeletClient(config)
Expand All @@ -165,11 +167,13 @@ func TestNewKubeletClientTLSValid(t *testing.T) {
config := &KubeletConfig{
Port: 9000,
EnableHttps: true,
CertFile: "./testdata/mycertvalid.cer",
// TLS Configuration, only applies if EnableHttps is true.
KeyFile: "./testdata/mycertvalid.key",
// TLS Configuration, only applies if EnableHttps is true.
CAFile: "./testdata/myCA.cer",
TLSClientConfig: TLSClientConfig{
CertFile: "./testdata/mycertvalid.cer",
// TLS Configuration, only applies if EnableHttps is true.
KeyFile: "./testdata/mycertvalid.key",
// TLS Configuration, only applies if EnableHttps is true.
CAFile: "./testdata/myCA.cer",
},
}

client, err := NewKubeletClient(config)
Expand Down