Fix renewing certificates via the API

Poll for events, print CSR name, and parse PEM instead of CSR
pull/58/head
liz 2018-11-14 14:10:34 -08:00
parent 8bc0447d8c
commit 6afb9a117b
No known key found for this signature in database
GPG Key ID: F95E0D7E9E2AD497
3 changed files with 22 additions and 43 deletions

View File

@ -14,11 +14,10 @@ go_library(
"//cmd/kubeadm/app/util/pkiutil:go_default_library", "//cmd/kubeadm/app/util/pkiutil:go_default_library",
"//staging/src/k8s.io/api/certificates/v1beta1:go_default_library", "//staging/src/k8s.io/api/certificates/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1:go_default_library",
"//staging/src/k8s.io/client-go/util/cert:go_default_library", "//staging/src/k8s.io/client-go/util/cert:go_default_library",
"//staging/src/k8s.io/client-go/util/certificate/csr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library", "//vendor/github.com/pkg/errors:go_default_library",
], ],
) )

View File

@ -20,26 +20,22 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"fmt"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
certsapi "k8s.io/api/certificates/v1beta1" certsapi "k8s.io/api/certificates/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
certstype "k8s.io/client-go/kubernetes/typed/certificates/v1beta1" certstype "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
certutil "k8s.io/client-go/util/cert" certutil "k8s.io/client-go/util/cert"
csrutil "k8s.io/client-go/util/certificate/csr"
) )
const ( const certAPIPrefixName = "kubeadm-cert"
certAPIPrefixName = "kubeadm-cert"
)
var ( var watchTimeout = 5 * time.Minute
watchTimeout = 5 * time.Minute
)
// CertsAPIRenewal creates new certificates using the certs API // CertsAPIRenewal creates new certificates using the certs API
type CertsAPIRenewal struct { type CertsAPIRenewal struct {
@ -85,7 +81,7 @@ func (r *CertsAPIRenewal) Renew(cfg *certutil.Config) (*x509.Certificate, *rsa.P
k8sCSR := &certsapi.CertificateSigningRequest{ k8sCSR := &certsapi.CertificateSigningRequest{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: certAPIPrefixName, GenerateName: fmt.Sprintf("%s-%s-", certAPIPrefixName, cfg.CommonName),
}, },
Spec: certsapi.CertificateSigningRequestSpec{ Spec: certsapi.CertificateSigningRequestSpec{
Request: csr, Request: csr,
@ -98,43 +94,23 @@ func (r *CertsAPIRenewal) Renew(cfg *certutil.Config) (*x509.Certificate, *rsa.P
return nil, nil, errors.Wrap(err, "couldn't create certificate signing request") return nil, nil, errors.Wrap(err, "couldn't create certificate signing request")
} }
watcher, err := r.client.CertificateSigningRequests().Watch(metav1.ListOptions{ fmt.Printf("[certs] certificate request %q created\n", req.Name)
Watch: true,
FieldSelector: fields.Set{"metadata.name": req.Name}.String(), certData, err := csrutil.WaitForCertificate(r.client.CertificateSigningRequests(), req, watchTimeout)
})
if err != nil { if err != nil {
return nil, nil, errors.Wrap(err, "couldn't watch for certificate creation") return nil, nil, errors.Wrap(err, "certificate failed to appear")
}
defer watcher.Stop()
select {
case ev := <-watcher.ResultChan():
if ev.Type != watch.Modified {
return nil, nil, errors.Errorf("unexpected event received: %q", ev.Type)
}
case <-time.After(watchTimeout):
return nil, nil, errors.New("timeout trying to sign certificate")
} }
req, err = r.client.CertificateSigningRequests().Get(req.Name, metav1.GetOptions{}) cert, err := certutil.ParseCertsPEM(certData)
if err != nil {
return nil, nil, errors.Wrap(err, "couldn't get certificate signing request")
}
if len(req.Status.Conditions) < 1 {
return nil, nil, errors.New("certificate signing request has no statuses")
}
// TODO: under what circumstances are there more than one?
if status := req.Status.Conditions[0].Type; status != certsapi.CertificateApproved {
return nil, nil, errors.Errorf("unexpected certificate status: %v", status)
}
cert, err := x509.ParseCertificate(req.Status.Certificate)
if err != nil { if err != nil {
return nil, nil, errors.Wrap(err, "couldn't parse issued certificate") return nil, nil, errors.Wrap(err, "couldn't parse issued certificate")
} }
return cert, key, nil if len(cert) != 1 {
return nil, nil, errors.Errorf("certificate request %q has %d certificates, wanted exactly 1", req.Name, len(cert))
}
return cert[0], key, nil
} }
var usageMap = map[x509.ExtKeyUsage]certsapi.KeyUsage{ var usageMap = map[x509.ExtKeyUsage]certsapi.KeyUsage{

View File

@ -49,8 +49,12 @@ func TestRenewImplementations(t *testing.T) {
Fake: &k8stesting.Fake{}, Fake: &k8stesting.Fake{},
} }
certReq := getCertReq(t, caCert, caKey) certReq := getCertReq(t, caCert, caKey)
certReqNoCert := certReq.DeepCopy()
certReqNoCert.Status.Certificate = nil
client.AddReactor("get", "certificatesigningrequests", defaultReactionFunc(certReq)) client.AddReactor("get", "certificatesigningrequests", defaultReactionFunc(certReq))
watcher := watch.NewFakeWithChanSize(1, false) watcher := watch.NewFakeWithChanSize(3, false)
watcher.Add(certReqNoCert)
watcher.Modify(certReqNoCert)
watcher.Modify(certReq) watcher.Modify(certReq)
client.AddWatchReactor("certificatesigningrequests", k8stesting.DefaultWatchReactor(watcher, nil)) client.AddWatchReactor("certificatesigningrequests", k8stesting.DefaultWatchReactor(watcher, nil))
@ -132,7 +136,7 @@ func getCertReq(t *testing.T, caCert *x509.Certificate, caKey *rsa.PrivateKey) *
Type: certsapi.CertificateApproved, Type: certsapi.CertificateApproved,
}, },
}, },
Certificate: cert.Raw, Certificate: certutil.EncodeCertPEM(cert),
}, },
} }
} }