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

Add authenticated TLS support to the client. #1386

Merged
merged 1 commit into from
Sep 19, 2014
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
12 changes: 12 additions & 0 deletions cmd/kubecfg/kubecfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add this stuff to the ~/.kubernetes_auth file instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will work in both places. (since the auth file is just a serialized AuthInfo struct) I'm in favor of flexibility, since you may generate different user/password for each cluster, but use a shared CA file.

But if you feel strongly, I'll remove the flags.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel super strongly, but I think it'll be a huge pain to use these flags if you want to do much of anything. Your choice, I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that's my point. If you want to use .kubernetes_auth you can just put the relevant data in there and it will work as it stands in this PR, since it will deserialize.

If you then want to override using the flags, you can do that too.

So it works either way you want it.

)

var parser = kubecfg.NewParser(map[string]runtime.Object{
Expand Down Expand Up @@ -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)
Expand Down
35 changes: 32 additions & 3 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package client

import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -169,16 +173,41 @@ 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,
secure: prefix.Scheme == "https",
auth: auth,
httpClient: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
TLSClientConfig: config,
},
},
Sync: false,
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}},
Expand Down