mirror of https://github.com/k3s-io/k3s
Fix renewing certificates via the API
Poll for events, print CSR name, and parse PEM instead of CSRpull/58/head
parent
8bc0447d8c
commit
6afb9a117b
|
@ -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",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue