mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 12:15:52 +00:00
Merge pull request #3564 from liggitt/client_cert_data
Allow client and kubelet configs to hold cert/key/ca data directly
This commit is contained in:
commit
474212106f
@ -18,6 +18,7 @@ package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
@ -62,8 +63,20 @@ type Config struct {
|
||||
|
||||
// Server requires TLS client certificate authentication
|
||||
CertFile string
|
||||
KeyFile string
|
||||
CAFile 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
|
||||
|
||||
// Server should be accessed without verifying the TLS
|
||||
// certificate. For testing only.
|
||||
@ -85,6 +98,16 @@ type KubeletConfig struct {
|
||||
KeyFile string
|
||||
// TLS Configuration, only applies if EnableHttps is true.
|
||||
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
|
||||
}
|
||||
|
||||
// New creates a Kubernetes client for the given config. This client works with pods,
|
||||
@ -185,29 +208,48 @@ func RESTClientFor(config *Config) (*RESTClient, error) {
|
||||
// or transport level security defined by the provided Config. Will return the
|
||||
// default http.DefaultTransport if no special case behavior is needed.
|
||||
func TransportFor(config *Config) (http.RoundTripper, error) {
|
||||
hasCA := len(config.CAFile) > 0 || len(config.CAData) > 0
|
||||
hasCert := len(config.CertFile) > 0 || len(config.CertData) > 0
|
||||
|
||||
// Set transport level security
|
||||
if config.Transport != nil && (config.CAFile != "" || config.CertFile != "" || config.Insecure) {
|
||||
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 config.CAFile != "" && config.Insecure {
|
||||
if hasCA && config.Insecure {
|
||||
return nil, fmt.Errorf("specifying a root certificates file with the insecure flag is not allowed")
|
||||
}
|
||||
var transport http.RoundTripper
|
||||
switch {
|
||||
case config.Transport != nil:
|
||||
transport = config.Transport
|
||||
case config.CertFile != "":
|
||||
t, err := NewClientCertTLSTransport(config.CertFile, config.KeyFile, config.CAFile)
|
||||
if err != nil {
|
||||
case hasCert:
|
||||
var (
|
||||
certData, keyData, caData []byte
|
||||
err error
|
||||
)
|
||||
if certData, err = dataFromSliceOrFile(config.CertData, config.CertFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transport = t
|
||||
case config.CAFile != "":
|
||||
t, err := NewTLSTransport(config.CAFile)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
transport = t
|
||||
case config.Insecure:
|
||||
transport = NewUnsafeTLSTransport()
|
||||
default:
|
||||
@ -231,6 +273,19 @@ func TransportFor(config *Config) (http.RoundTripper, error) {
|
||||
return transport, nil
|
||||
}
|
||||
|
||||
// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
|
||||
// or an error if an error occurred reading the file
|
||||
func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
|
||||
if len(data) > 0 {
|
||||
return data, nil
|
||||
}
|
||||
fileData, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return fileData, nil
|
||||
}
|
||||
|
||||
// DefaultServerURL converts a host, host:port, or URL string to the default base server API path
|
||||
// to use with a Client at a given API version following the standard conventions for a
|
||||
// Kubernetes API.
|
||||
|
@ -21,14 +21,138 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
rootCACert = `-----BEGIN CERTIFICATE-----
|
||||
MIIC4DCCAcqgAwIBAgIBATALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu
|
||||
MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIxNTczN1oXDTE2MDExNTIxNTcz
|
||||
OFowIzEhMB8GA1UEAwwYMTAuMTMuMTI5LjEwNkAxNDIxMzU5MDU4MIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunDRXGwsiYWGFDlWH6kjGun+PshDGeZX
|
||||
xtx9lUnL8pIRWH3wX6f13PO9sktaOWW0T0mlo6k2bMlSLlSZgG9H6og0W6gLS3vq
|
||||
s4VavZ6DbXIwemZG2vbRwsvR+t4G6Nbwelm6F8RFnA1Fwt428pavmNQ/wgYzo+T1
|
||||
1eS+HiN4ACnSoDSx3QRWcgBkB1g6VReofVjx63i0J+w8Q/41L9GUuLqquFxu6ZnH
|
||||
60vTB55lHgFiDLjA1FkEz2dGvGh/wtnFlRvjaPC54JH2K1mPYAUXTreoeJtLJKX0
|
||||
ycoiyB24+zGCniUmgIsmQWRPaOPircexCp1BOeze82BT1LCZNTVaxQIDAQABoyMw
|
||||
ITAOBgNVHQ8BAf8EBAMCAKQwDwYDVR0TAQH/BAUwAwEB/zALBgkqhkiG9w0BAQsD
|
||||
ggEBADMxsUuAFlsYDpF4fRCzXXwrhbtj4oQwcHpbu+rnOPHCZupiafzZpDu+rw4x
|
||||
YGPnCb594bRTQn4pAu3Ac18NbLD5pV3uioAkv8oPkgr8aUhXqiv7KdDiaWm6sbAL
|
||||
EHiXVBBAFvQws10HMqMoKtO8f1XDNAUkWduakR/U6yMgvOPwS7xl0eUTqyRB6zGb
|
||||
K55q2dejiFWaFqB/y78txzvz6UlOZKE44g2JAVoJVM6kGaxh33q8/FmrL4kuN3ut
|
||||
W+MmJCVDvd4eEqPwbp7146ZWTqpIJ8lvA6wuChtqV8lhAPka2hD/LMqY8iXNmfXD
|
||||
uml0obOEy+ON91k+SWTJ3ggmF/U=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
certData = `-----BEGIN CERTIFICATE-----
|
||||
MIIC6jCCAdSgAwIBAgIBCzALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu
|
||||
MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIyMDEzMVoXDTE2MDExNTIyMDEz
|
||||
MlowGzEZMBcGA1UEAxMQb3BlbnNoaWZ0LWNsaWVudDCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAKtdhz0+uCLXw5cSYns9rU/XifFSpb/x24WDdrm72S/v
|
||||
b9BPYsAStiP148buylr1SOuNi8sTAZmlVDDIpIVwMLff+o2rKYDicn9fjbrTxTOj
|
||||
lI4pHJBH+JU3AJ0tbajupioh70jwFS0oYpwtneg2zcnE2Z4l6mhrj2okrc5Q1/X2
|
||||
I2HChtIU4JYTisObtin10QKJX01CLfYXJLa8upWzKZ4/GOcHG+eAV3jXWoXidtjb
|
||||
1Usw70amoTZ6mIVCkiu1QwCoa8+ycojGfZhvqMsAp1536ZcCul+Na+AbCv4zKS7F
|
||||
kQQaImVrXdUiFansIoofGlw/JNuoKK6ssVpS5Ic3pgcCAwEAAaM1MDMwDgYDVR0P
|
||||
AQH/BAQDAgCgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwCwYJ
|
||||
KoZIhvcNAQELA4IBAQCKLREH7bXtXtZ+8vI6cjD7W3QikiArGqbl36bAhhWsJLp/
|
||||
p/ndKz39iFNaiZ3GlwIURWOOKx3y3GA0x9m8FR+Llthf0EQ8sUjnwaknWs0Y6DQ3
|
||||
jjPFZOpV3KPCFrdMJ3++E3MgwFC/Ih/N2ebFX9EcV9Vcc6oVWMdwT0fsrhu683rq
|
||||
6GSR/3iVX1G/pmOiuaR0fNUaCyCfYrnI4zHBDgSfnlm3vIvN2lrsR/DQBakNL8DJ
|
||||
HBgKxMGeUPoneBv+c8DMXIL0EhaFXRlBv9QW45/GiAIOuyFJ0i6hCtGZpJjq4OpQ
|
||||
BRjCI+izPzFTjsxD4aORE+WOkyWFCGPWKfNejfw0
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
keyData = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAq12HPT64ItfDlxJiez2tT9eJ8VKlv/HbhYN2ubvZL+9v0E9i
|
||||
wBK2I/Xjxu7KWvVI642LyxMBmaVUMMikhXAwt9/6jaspgOJyf1+NutPFM6OUjikc
|
||||
kEf4lTcAnS1tqO6mKiHvSPAVLShinC2d6DbNycTZniXqaGuPaiStzlDX9fYjYcKG
|
||||
0hTglhOKw5u2KfXRAolfTUIt9hcktry6lbMpnj8Y5wcb54BXeNdaheJ22NvVSzDv
|
||||
RqahNnqYhUKSK7VDAKhrz7JyiMZ9mG+oywCnXnfplwK6X41r4BsK/jMpLsWRBBoi
|
||||
ZWtd1SIVqewiih8aXD8k26gorqyxWlLkhzemBwIDAQABAoIBAD2XYRs3JrGHQUpU
|
||||
FkdbVKZkvrSY0vAZOqBTLuH0zUv4UATb8487anGkWBjRDLQCgxH+jucPTrztekQK
|
||||
aW94clo0S3aNtV4YhbSYIHWs1a0It0UdK6ID7CmdWkAj6s0T8W8lQT7C46mWYVLm
|
||||
5mFnCTHi6aB42jZrqmEpC7sivWwuU0xqj3Ml8kkxQCGmyc9JjmCB4OrFFC8NNt6M
|
||||
ObvQkUI6Z3nO4phTbpxkE1/9dT0MmPIF7GhHVzJMS+EyyRYUDllZ0wvVSOM3qZT0
|
||||
JMUaBerkNwm9foKJ1+dv2nMKZZbJajv7suUDCfU44mVeaEO+4kmTKSGCGjjTBGkr
|
||||
7L1ySDECgYEA5ElIMhpdBzIivCuBIH8LlUeuzd93pqssO1G2Xg0jHtfM4tz7fyeI
|
||||
cr90dc8gpli24dkSxzLeg3Tn3wIj/Bu64m2TpZPZEIlukYvgdgArmRIPQVxerYey
|
||||
OkrfTNkxU1HXsYjLCdGcGXs5lmb+K/kuTcFxaMOs7jZi7La+jEONwf8CgYEAwCs/
|
||||
rUOOA0klDsWWisbivOiNPII79c9McZCNBqncCBfMUoiGe8uWDEO4TFHN60vFuVk9
|
||||
8PkwpCfvaBUX+ajvbafIfHxsnfk1M04WLGCeqQ/ym5Q4sQoQOcC1b1y9qc/xEWfg
|
||||
nIUuia0ukYRpl7qQa3tNg+BNFyjypW8zukUAC/kCgYB1/Kojuxx5q5/oQVPrx73k
|
||||
2bevD+B3c+DYh9MJqSCNwFtUpYIWpggPxoQan4LwdsmO0PKzocb/ilyNFj4i/vII
|
||||
NToqSc/WjDFpaDIKyuu9oWfhECye45NqLWhb/6VOuu4QA/Nsj7luMhIBehnEAHW+
|
||||
GkzTKM8oD1PxpEG3nPKXYQKBgQC6AuMPRt3XBl1NkCrpSBy/uObFlFaP2Enpf39S
|
||||
3OZ0Gv0XQrnSaL1kP8TMcz68rMrGX8DaWYsgytstR4W+jyy7WvZwsUu+GjTJ5aMG
|
||||
77uEcEBpIi9CBzivfn7hPccE8ZgqPf+n4i6q66yxBJflW5xhvafJqDtW2LcPNbW/
|
||||
bvzdmQKBgExALRUXpq+5dbmkdXBHtvXdRDZ6rVmrnjy4nI5bPw+1GqQqk6uAR6B/
|
||||
F6NmLCQOO4PDG/cuatNHIr2FrwTmGdEL6ObLUGWn9Oer9gJhHVqqsY5I4sEPo4XX
|
||||
stR0Yiw0buV6DL/moUO0HIM9Bjh96HJp+LxiIS6UCdIhMPp5HoQa
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
)
|
||||
|
||||
func TestTransportFor(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
Config *Config
|
||||
Err bool
|
||||
TLS bool
|
||||
Default bool
|
||||
}{
|
||||
"default transport": {
|
||||
Config: &Config{},
|
||||
Default: true,
|
||||
Config: &Config{},
|
||||
},
|
||||
|
||||
"ca transport": {
|
||||
TLS: true,
|
||||
Config: &Config{
|
||||
CAData: []byte(rootCACert),
|
||||
},
|
||||
},
|
||||
"bad ca file transport": {
|
||||
Err: true,
|
||||
Config: &Config{
|
||||
CAFile: "invalid file",
|
||||
},
|
||||
},
|
||||
"ca data overriding bad ca file transport": {
|
||||
TLS: true,
|
||||
Config: &Config{
|
||||
CAData: []byte(rootCACert),
|
||||
CAFile: "invalid file",
|
||||
},
|
||||
},
|
||||
|
||||
"cert transport": {
|
||||
TLS: true,
|
||||
Config: &Config{
|
||||
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),
|
||||
},
|
||||
},
|
||||
"bad file cert transport": {
|
||||
Err: true,
|
||||
Config: &Config{
|
||||
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),
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, testCase := range testCases {
|
||||
@ -41,8 +165,26 @@ func TestTransportFor(t *testing.T) {
|
||||
t.Errorf("%s: unexpected error: %v", k, err)
|
||||
continue
|
||||
}
|
||||
if testCase.Default && transport != http.DefaultTransport {
|
||||
|
||||
switch {
|
||||
case testCase.Default && transport != http.DefaultTransport:
|
||||
t.Errorf("%s: expected the default transport, got %#v", k, transport)
|
||||
continue
|
||||
case !testCase.Default && transport == http.DefaultTransport:
|
||||
t.Errorf("%s: expected non-default transport, got %#v", k, transport)
|
||||
continue
|
||||
}
|
||||
|
||||
// We only know how to check TLSConfig on http.Transports
|
||||
if transport, ok := transport.(*http.Transport); ok {
|
||||
switch {
|
||||
case testCase.TLS && transport.TLSClientConfig == nil:
|
||||
t.Errorf("%s: expected TLSClientConfig, got %#v", k, transport)
|
||||
continue
|
||||
case !testCase.TLS && transport.TLSClientConfig != nil:
|
||||
t.Errorf("%s: expected no TLSClientConfig, got %#v", k, transport)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,17 +61,33 @@ type HTTPKubeletClient struct {
|
||||
func NewKubeletClient(config *KubeletConfig) (KubeletClient, error) {
|
||||
transport := http.DefaultTransport
|
||||
if config.CertFile != "" {
|
||||
t, err := NewClientCertTLSTransport(config.CertFile, config.KeyFile, config.CAFile)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
transport = t
|
||||
} else if config.CAFile != "" {
|
||||
t, err := NewTLSTransport(config.CAFile)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
transport = t
|
||||
}
|
||||
|
||||
c := &http.Client{Transport: transport}
|
||||
|
@ -102,7 +102,7 @@ func TestSetDefaults(t *testing.T) {
|
||||
case err != nil:
|
||||
continue
|
||||
}
|
||||
if *val != testCase.After {
|
||||
if !reflect.DeepEqual(*val, testCase.After) {
|
||||
t.Errorf("unexpected result object: %#v", val)
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@ -55,17 +54,13 @@ func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response,
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
func NewClientCertTLSTransport(certFile, keyFile, caFile string) (*http.Transport, error) {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadFile(caFile)
|
||||
func NewClientCertTLSTransport(certData, keyData, caData []byte) (*http.Transport, error) {
|
||||
cert, err := tls.X509KeyPair(certData, keyData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AppendCertsFromPEM(data)
|
||||
certPool.AppendCertsFromPEM(caData)
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
// Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability)
|
||||
@ -80,13 +75,9 @@ func NewClientCertTLSTransport(certFile, keyFile, caFile string) (*http.Transpor
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewTLSTransport(caFile string) (*http.Transport, error) {
|
||||
data, err := ioutil.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func NewTLSTransport(caData []byte) (*http.Transport, error) {
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AppendCertsFromPEM(data)
|
||||
certPool.AppendCertsFromPEM(caData)
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
// Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability)
|
||||
|
Loading…
Reference in New Issue
Block a user