Skip to content

Commit

Permalink
Move auth refresh metadata from CA to client cert
Browse files Browse the repository at this point in the history
  • Loading branch information
databus23 committed Nov 16, 2017
1 parent ea2d18b commit 9dc20f5
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 64 deletions.
4 changes: 3 additions & 1 deletion glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions pkg/api/handlers/get_cluster_credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import (
"fmt"
"time"

"github.com/databus23/requestutil"
"github.com/ghodss/yaml"
"github.com/go-openapi/runtime/middleware"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1"
certutil "k8s.io/client-go/util/cert"

"github.com/sapcc/kubernikus/pkg/api"
"github.com/sapcc/kubernikus/pkg/api/models"
"github.com/sapcc/kubernikus/pkg/api/rest/operations"
"github.com/sapcc/kubernikus/pkg/client/kubernetes"
"github.com/sapcc/kubernikus/pkg/util"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1"
certutil "k8s.io/client-go/util/cert"
)

func NewGetClusterCredentials(rt *api.Runtime) operations.GetClusterCredentialsHandler {
Expand Down Expand Up @@ -69,6 +71,8 @@ func (d *getClusterCredentials) Handle(params operations.GetClusterCredentialsPa
cert := bundle.Sign(util.Config{
Sign: fmt.Sprintf("%s@%s", principal.Name, principal.Domain),
Organization: organizations,
Province: []string{principal.AuthURL, kluster.Spec.Openstack.ProjectID},
Locality: []string{fmt.Sprintf("%s://%s", requestutil.Scheme(params.HTTPRequest), requestutil.HostWithPort(params.HTTPRequest))},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
ValidFor: 24 * time.Hour,
})
Expand Down
54 changes: 20 additions & 34 deletions pkg/cmd/kubernikusctl/auth/refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
type RefreshOptions struct {
kubeconfigPath string
context string
force bool

url *url.URL

Expand Down Expand Up @@ -55,6 +56,7 @@ func (o *RefreshOptions) BindFlags(flags *pflag.FlagSet) {
flags.StringVar(&o.openstack.Password, "password", o.openstack.Password, "User password [OS_PASSWORD]")
flags.StringVar(&o.kubeconfigPath, "kubeconfig", o.kubeconfigPath, "Overwrites kubeconfig auto-detection with explicit path")
flags.StringVar(&o.context, "context", o.context, "Overwrites current-context in kubeconfig")
flags.BoolVar(&o.force, "force", o.force, "Force refresh")
}

func (o *RefreshOptions) Validate(c *cobra.Command, args []string) (err error) {
Expand Down Expand Up @@ -103,28 +105,23 @@ func (o *RefreshOptions) Run(c *cobra.Command) error {
if ok, err := o.isCertificateValid(); err != nil {
return errors.Wrap(err, "Verification of certifcates failed.")
} else {
if ok {
if ok && !o.force {
glog.V(2).Infof("Certificates are good. Doing nothing.")
return nil
}
}

if identityEndpoint, err := o.autoDetectAuthURL(); err != nil {
errors.Wrap(err, "Auto-Detection of auth-url caused an error")
if identityEndpoint, projectID, err := o.autoDetectKubernikusOpenstackMetadata(); err != nil {
return errors.Wrap(err, "Auto-Detection of Openstack auth endpoint failed.")
} else {
glog.V(2).Infof("Detected auth-url: %v", identityEndpoint)
o.openstack.IdentityEndpoint = identityEndpoint
}

if projectID, err := o.autoDetectProjectID(); err != nil {
errors.Wrap(err, "Auto-Detection of project scope caused an error")
} else {
glog.V(2).Infof("Detected authentication scope for project-id: %v", projectID)
o.openstack.Scope.ProjectID = projectID
}

if kurl, err := o.autoDetectKubernikusURL(); err != nil {
errors.Wrap(err, "Auto-Detection of Kubernikus URL caused an error")
return errors.Wrap(err, "Auto-Detection of Kubernikus URL caused an error")
} else {
glog.V(2).Infof("Detected Kubernikus URL: %v", kurl)
_url, err := url.Parse(kurl)
Expand All @@ -135,14 +132,14 @@ func (o *RefreshOptions) Run(c *cobra.Command) error {
}

if username, err := o.autoDetectUsername(); err != nil {
errors.Wrap(err, "Auto-Detection of Username failed")
return errors.Wrap(err, "Auto-Detection of Username failed")
} else {
glog.V(2).Infof("Detected username: %v", username)
o.openstack.Username = username
}

if domainName, err := o.autoDetectUserDomainName(); err != nil {
errors.Wrap(err, "Auto-Detection of user-domain-name failed")
return errors.Wrap(err, "Auto-Detection of user domain failed")
} else {
glog.V(2).Infof("Detected domain-name: %v", domainName)
o.openstack.DomainName = domainName
Expand Down Expand Up @@ -207,26 +204,23 @@ func (o *RefreshOptions) isKubernikusContext() (bool, error) {
return false, err
}

if len(caCert.Issuer.OrganizationalUnit) < 2 {
if len(caCert.Subject.OrganizationalUnit) < 2 {
return false, nil
}

return caCert.Issuer.OrganizationalUnit[0] == util.CA_ISSUER_KUBERNIKUS_IDENTIFIER_0 &&
caCert.Issuer.OrganizationalUnit[1] == util.CA_ISSUER_KUBERNIKUS_IDENTIFIER_1, nil
return caCert.Subject.OrganizationalUnit[0] == util.CA_ISSUER_KUBERNIKUS_IDENTIFIER_0 &&
caCert.Subject.OrganizationalUnit[1] == util.CA_ISSUER_KUBERNIKUS_IDENTIFIER_1, nil
}

func (o *RefreshOptions) autoDetectKubernikusCAMetadata(index int) (string, error) {
cert, err := o.getCACertifciate()
func (o *RefreshOptions) autoDetectKubernikusOpenstackMetadata() (string, string, error) {
cert, err := o.getClientCertificate()
if err != nil {
return "", err
}
if len(cert.Issuer.Province) < 1 {
return "", errors.Errorf("CA certificate didn't contain Kubernikus metadata")
return "", "", err
}
if index > 1 {
return "", errors.Errorf("Invalid Metadata")
if len(cert.Subject.Province) < 2 {
return "", "", errors.Errorf("Client certificate didn't contain Kubernikus metadata")
}
return cert.Issuer.Province[index], nil
return cert.Subject.Province[0], cert.Subject.Province[1], nil
}

func (o *RefreshOptions) autoDetectKubernikusClientMetadata() (string, string, error) {
Expand All @@ -246,24 +240,16 @@ func (o *RefreshOptions) autoDetectKubernikusClientMetadata() (string, string, e
return parts[0], parts[1], nil
}

func (o *RefreshOptions) autoDetectAuthURL() (string, error) {
return o.autoDetectKubernikusCAMetadata(0)
}

func (o *RefreshOptions) autoDetectProjectID() (string, error) {
return o.autoDetectKubernikusCAMetadata(1)
}

func (o *RefreshOptions) autoDetectKubernikusURL() (string, error) {
cert, err := o.getCACertifciate()
cert, err := o.getClientCertificate()
if err != nil {
return "", err
}

if len(cert.Issuer.Locality) == 0 {
if len(cert.Subject.Locality) == 0 {
return "", errors.Errorf("CA certificate didn't contain Kubernikus metadata")
}
return cert.Issuer.Locality[0], nil
return cert.Subject.Locality[0], nil
}

func (o *RefreshOptions) autoDetectUsername() (string, error) {
Expand Down
32 changes: 6 additions & 26 deletions pkg/util/certificates.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ type Config struct {
Sign string
Organization []string
OrganizationalUnit []string
Province []string
Locality []string
AltNames AltNames
Usages []x509.ExtKeyUsage
ValidFor time.Duration
Expand Down Expand Up @@ -176,7 +178,6 @@ func CreateCertificates(kluster *v1.Kluster, apiURL, authURL, domain string) (ma
createCA(kluster.Name, "ApiServer Nodes", &certs.ApiServer.Nodes.CA)
createCA(kluster.Name, "Kubelet Clients", &certs.Kubelet.Clients.CA)
createCA(kluster.Name, "TLS", &certs.TLS.CA)
createEnrichedCA(kluster, apiURL, authURL, "TLS", &certs.TLS.CA)

certs.Etcd.Clients.ApiServer = certs.signEtcdClient("apiserver")
certs.Etcd.Peers.Universal = certs.signEtcdPeer("universal")
Expand Down Expand Up @@ -250,38 +251,15 @@ func (c Certificates) signTLS(name string, dnsNames []string, ips []net.IP) Bund
return c.TLS.CA.Sign(config)
}

func createCA(satellite, name string, bundle *Bundle) {
func createCA(klusterName, name string, bundle *Bundle) {
bundle.PrivateKey, _ = certutil.NewPrivateKey()

now := time.Now()
tmpl := x509.Certificate{
SerialNumber: new(big.Int).SetInt64(0),
Subject: pkix.Name{
CommonName: name,
OrganizationalUnit: []string{"SAP Converged Cloud", "Kubernikus", satellite},
},
NotBefore: now.UTC(),
NotAfter: now.Add(caValidity).UTC(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
IsCA: true,
}

certDERBytes, _ := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, bundle.PrivateKey.Public(), bundle.PrivateKey)
bundle.Certificate, _ = x509.ParseCertificate(certDERBytes)
}

func createEnrichedCA(kluster *v1.Kluster, kubernikusAPIURL, authURL, common_name string, bundle *Bundle) {
bundle.PrivateKey, _ = certutil.NewPrivateKey()

now := time.Now()
tmpl := x509.Certificate{
SerialNumber: new(big.Int).SetInt64(0),
Subject: pkix.Name{
CommonName: common_name,
OrganizationalUnit: []string{CA_ISSUER_KUBERNIKUS_IDENTIFIER_0, CA_ISSUER_KUBERNIKUS_IDENTIFIER_1, kluster.Name},
Province: []string{authURL, kluster.Spec.Openstack.ProjectID},
Locality: []string{kubernikusAPIURL},
OrganizationalUnit: []string{CA_ISSUER_KUBERNIKUS_IDENTIFIER_0, CA_ISSUER_KUBERNIKUS_IDENTIFIER_1, klusterName},
},
NotBefore: now.UTC(),
NotAfter: now.Add(caValidity).UTC(),
Expand Down Expand Up @@ -311,6 +289,8 @@ func (ca Bundle) Sign(config Config) Bundle {
CommonName: config.Sign,
Organization: config.Organization,
OrganizationalUnit: config.OrganizationalUnit,
Province: config.Province,
Locality: config.Locality,
},
DNSNames: config.AltNames.DNSNames,
IPAddresses: config.AltNames.IPs,
Expand Down
Loading

0 comments on commit 9dc20f5

Please sign in to comment.