diff --git a/cmd/kubecfg/kubecfg.go b/cmd/kubecfg/kubecfg.go index efe3c7a732b..79c4ef1c4ed 100644 --- a/cmd/kubecfg/kubecfg.go +++ b/cmd/kubecfg/kubecfg.go @@ -59,6 +59,9 @@ var ( templateStr = flag.String("template", "", "If present, parse this string as a golang template and use it for output printing") imageName = flag.String("image", "", "Image used when updating a replicationController. Will apply to the first container in the pod template.") apiVersion = flag.String("api_version", latest.Version, "The version of the API to use against this server.") + caFile = flag.String("certificate_authority", "", "Path to a cert. file for the certificate authority") + certFile = flag.String("client_certificate", "", "Path to a client certificate for TLS.") + keyFile = flag.String("client_key", "", "Path to a client key file for TLS.") ) var parser = kubecfg.NewParser(map[string]runtime.Object{ @@ -173,6 +176,15 @@ func main() { if err != nil { glog.Fatalf("Error loading auth: %v", err) } + if *caFile != "" { + auth.CAFile = *caFile + } + if *certFile != "" { + auth.CertFile = *certFile + } + if *keyFile != "" { + auth.KeyFile = *keyFile + } kubeClient, err = client.New(masterServer, *apiVersion, auth) if err != nil { glog.Fatalf("Can't configure client: %v", err) diff --git a/pkg/client/client.go b/pkg/client/client.go index b79cd29a9f5..6cecf36cc69 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -18,6 +18,7 @@ package client import ( "crypto/tls" + "crypto/x509" "encoding/json" "fmt" "io/ioutil" @@ -141,6 +142,9 @@ func (s *StatusErr) Error() string { type AuthInfo struct { User string Password string + CAFile string + CertFile string + KeyFile string } // RESTClient holds common code used to work with API resources that follow the @@ -169,6 +173,33 @@ func NewRESTClient(host string, auth *AuthInfo, path string, c runtime.Codec) (* base.Path = "" base.RawQuery = "" base.Fragment = "" + + var config *tls.Config + if auth != nil && len(auth.CertFile) != 0 { + cert, err := tls.LoadX509KeyPair(auth.CertFile, auth.KeyFile) + if err != nil { + return nil, err + } + data, err := ioutil.ReadFile(auth.CAFile) + if err != nil { + return nil, err + } + certPool := x509.NewCertPool() + certPool.AppendCertsFromPEM(data) + config = &tls.Config{ + Certificates: []tls.Certificate{ + cert, + }, + RootCAs: certPool, + ClientCAs: certPool, + ClientAuth: tls.RequireAndVerifyClientCert, + } + } else { + config = &tls.Config{ + InsecureSkipVerify: true, + } + } + return &RESTClient{ host: base.String(), prefix: prefix.Path, @@ -176,9 +207,7 @@ func NewRESTClient(host string, auth *AuthInfo, path string, c runtime.Codec) (* auth: auth, httpClient: &http.Client{ Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, + TLSClientConfig: config, }, }, Sync: false, diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 3ac74156776..abfdaa241e3 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -546,7 +546,7 @@ func TestDoRequest(t *testing.T) { testClients := []testClient{ {Request: testRequest{Method: "GET", Path: "good"}, Response: Response{StatusCode: 200}}, {Request: testRequest{Method: "GET", Path: "bad%ZZ"}, Error: true}, - {Client: NewOrDie("localhost", "v1beta1", &AuthInfo{"foo", "bar"}), Request: testRequest{Method: "GET", Path: "auth", Header: "Authorization"}, Response: Response{StatusCode: 200}}, + {Client: NewOrDie("localhost", "v1beta1", &AuthInfo{"foo", "bar", "", "", ""}), Request: testRequest{Method: "GET", Path: "auth", Header: "Authorization"}, Response: Response{StatusCode: 200}}, {Client: &Client{&RESTClient{httpClient: http.DefaultClient}}, Request: testRequest{Method: "GET", Path: "nocertificate"}, Error: true}, {Request: testRequest{Method: "GET", Path: "error"}, Response: Response{StatusCode: 500}, Error: true}, {Request: testRequest{Method: "POST", Path: "faildecode"}, Response: Response{StatusCode: 200, RawBody: &invalid}},