From 7b10847c27423b1f27e7621a91c22450b3ff452d Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Fri, 6 Feb 2015 11:26:35 -0500 Subject: [PATCH] Generate client configs for nodes, provider-qualify and add groups to certs --- .../authenticator/request/x509request/x509.go | 26 +++++++++++++++++ pkg/cmd/server/crypto/crypto.go | 29 +++++++++++-------- pkg/cmd/server/start.go | 25 ++++++++++++---- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/pkg/auth/authenticator/request/x509request/x509.go b/pkg/auth/authenticator/request/x509request/x509.go index 521bfe209080..060389a46b70 100644 --- a/pkg/auth/authenticator/request/x509request/x509.go +++ b/pkg/auth/authenticator/request/x509request/x509.go @@ -2,6 +2,7 @@ package x509request import ( "crypto/x509" + "crypto/x509/pkix" "net/http" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" @@ -70,6 +71,16 @@ func DefaultVerifyOptions() x509.VerifyOptions { } } +// SubjectToUserConversion calls SubjectToUser on the subject of the first certificate in the chain. +// If the resulting user has no name, it returns nil, false, nil +var SubjectToUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) { + user := SubjectToUser(chain[0].Subject) + if len(user.GetName()) == 0 { + return nil, false, nil + } + return user, true, nil +}) + // CommonNameUserConversion builds user info from a certificate chain using the subject's CommonName var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) { if len(chain[0].Subject.CommonName) == 0 { @@ -93,3 +104,18 @@ var EmailAddressUserConversion = UserConversionFunc(func(chain []*x509.Certifica } return &user.DefaultInfo{Name: chain[0].EmailAddresses[0]}, true, nil }) + +func UserToSubject(u user.Info) pkix.Name { + return pkix.Name{ + CommonName: u.GetName(), + SerialNumber: u.GetUID(), + Organization: u.GetGroups(), + } +} +func SubjectToUser(subject pkix.Name) user.Info { + return &user.DefaultInfo{ + Name: subject.CommonName, + UID: subject.SerialNumber, + Groups: subject.Organization, + } +} diff --git a/pkg/cmd/server/crypto/crypto.go b/pkg/cmd/server/crypto/crypto.go index 3f76f1fb0207..89c1e4bd6927 100644 --- a/pkg/cmd/server/crypto/crypto.go +++ b/pkg/cmd/server/crypto/crypto.go @@ -20,10 +20,12 @@ import ( "strconv" "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" clientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" "github.com/golang/glog" + "github.com/openshift/origin/pkg/auth/authenticator/request/x509request" ) func filenamesFromDir(dir string) (string, string, string) { @@ -342,22 +344,25 @@ func (ca *CA) MakeServerCert(name string, hostnames []string) (*TLSCertificateCo } // MakeClientConfig creates a folder containing certificates for the given client: -// / -// / -// root.crt - Root certificate bundle. -// cert.crt - Client certificate -// key.key - Private key +// / +// / +// root.crt - Root certificate bundle. +// cert.crt - Client certificate +// key.key - Private key // The generated certificate has the following attributes: -// CommonName: username -// ExtKeyUsage: ExtKeyUsageClientAuth -func (ca *CA) MakeClientConfig(provider, username string, groups []string, defaults kclient.Config) (kclient.Config, error) { - clientDir := filepath.Join(ca.Dir, username) +// Subject: +// SerialNumber: user.GetUID() +// CommonName: user.GetName() +// Organization: user.GetGroups() +// ExtKeyUsage: ExtKeyUsageClientAuth +func (ca *CA) MakeClientConfig(clientId string, u user.Info, defaults kclient.Config) (kclient.Config, error) { + clientDir := filepath.Join(ca.Dir, clientId) kubeConfig := filepath.Join(clientDir, ".kubeconfig") client, err := readClientConfigFromDir(clientDir, defaults) if err == nil { // Always write .kubeconfig to pick up hostname changes - if err := writeKubeConfigToDir(&client, username, clientDir); err != nil { + if err := writeKubeConfigToDir(&client, clientId, clientDir); err != nil { return client, err } glog.Infof("Using existing client config in %s", kubeConfig) @@ -371,7 +376,7 @@ func (ca *CA) MakeClientConfig(provider, username string, groups []string, defau // Create cert for system components to use to talk to the API clientPublicKey, clientPrivateKey, _ := NewKeyPair() - clientTemplate, _ := newClientCertificateTemplate(pkix.Name{CommonName: provider + ":" + username}) + clientTemplate, _ := newClientCertificateTemplate(x509request.UserToSubject(u)) clientCrt, _ := ca.signCertificate(clientTemplate, clientPublicKey) caData, err := encodeCertificates(ca.Config.Roots...) @@ -394,7 +399,7 @@ func (ca *CA) MakeClientConfig(provider, username string, groups []string, defau if err := writeClientCertsToDir(&client, clientDir); err != nil { return client, err } - if err := writeKubeConfigToDir(&client, username, clientDir); err != nil { + if err := writeKubeConfigToDir(&client, clientId, clientDir); err != nil { return client, err } diff --git a/pkg/cmd/server/start.go b/pkg/cmd/server/start.go index 73577e6addcf..332e74035b5d 100644 --- a/pkg/cmd/server/start.go +++ b/pkg/cmd/server/start.go @@ -430,21 +430,35 @@ func start(cfg *config, args []string) error { osClientConfigTemplate := kclient.Config{Host: cfg.MasterAddr.URL.String(), Version: latest.Version} // Openshift client - if osmaster.OSClientConfig, err = ca.MakeClientConfig("system", "openshift-client", nil, osClientConfigTemplate); err != nil { + openshiftClientUser := &user.DefaultInfo{Name: "system:openshift-client"} + if osmaster.OSClientConfig, err = ca.MakeClientConfig("openshift-client", openshiftClientUser, osClientConfigTemplate); err != nil { return err } // Openshift deployer client - if osmaster.DeployerOSClientConfig, err = ca.MakeClientConfig("system", "openshift-deployer", nil, osClientConfigTemplate); err != nil { + openshiftDeployerUser := &user.DefaultInfo{Name: "system:openshift-deployer", Groups: []string{"system:deployers"}} + if osmaster.DeployerOSClientConfig, err = ca.MakeClientConfig("openshift-deployer", openshiftDeployerUser, osClientConfigTemplate); err != nil { return err } // Admin config (creates files on disk for osc) - if _, err = ca.MakeClientConfig("system", "admin", nil, osClientConfigTemplate); err != nil { + adminUser := &user.DefaultInfo{Name: "system:admin", Groups: []string{"system:cluster-admins"}} + if _, err = ca.MakeClientConfig("admin", adminUser, osClientConfigTemplate); err != nil { return err } + // One client config per node + for _, node := range cfg.NodeList { + nodeIdentityName := fmt.Sprintf("node-%s", node) + nodeUserName := fmt.Sprintf("system:%s", nodeIdentityName) + nodeUser := &user.DefaultInfo{Name: nodeUserName, Groups: []string{"system:nodes"}} + if _, err = ca.MakeClientConfig(nodeIdentityName, nodeUser, osClientConfigTemplate); err != nil { + return err + } + } + // If we're running our own Kubernetes, build client credentials if startKube { - if osmaster.KubeClientConfig, err = ca.MakeClientConfig("system", "kube-client", nil, osmaster.KubeClientConfig); err != nil { + kubeClientUser := &user.DefaultInfo{Name: "system:kube-client"} + if osmaster.KubeClientConfig, err = ca.MakeClientConfig("kube-client", kubeClientUser, osmaster.KubeClientConfig); err != nil { return err } } @@ -458,10 +472,9 @@ func start(cfg *config, args []string) error { // build cert authenticator // TODO: add cert users to etcd? - // TODO: provider-qualify cert users? opts := x509request.DefaultVerifyOptions() opts.Roots = roots - certauth := x509request.New(opts, x509request.CommonNameUserConversion) + certauth := x509request.New(opts, x509request.SubjectToUserConversion) authenticators = append(authenticators, certauth) } else { // No security, use the same client config for all OpenShift clients