Fix the certificate rotation threshold and add jitter.

pull/6/head
Jacob Simpson 2017-03-03 10:40:37 -08:00
parent 6d8aa2b15f
commit e7666648bf
2 changed files with 38 additions and 52 deletions

View File

@ -75,7 +75,6 @@ type manager struct {
certStore Store certStore Store
certAccessLock sync.RWMutex certAccessLock sync.RWMutex
cert *tls.Certificate cert *tls.Certificate
shouldRotatePercent uint
} }
// NewManager returns a new certificate manager. A certificate manager is // NewManager returns a new certificate manager. A certificate manager is
@ -85,25 +84,19 @@ func NewManager(
certSigningRequestClient certificatesclient.CertificateSigningRequestInterface, certSigningRequestClient certificatesclient.CertificateSigningRequestInterface,
template *x509.CertificateRequest, template *x509.CertificateRequest,
usages []certificates.KeyUsage, usages []certificates.KeyUsage,
certificateStore Store, certificateStore Store) (Manager, error) {
certRotationPercent uint) (Manager, error) {
cert, err := certificateStore.Current() cert, err := certificateStore.Current()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if certRotationPercent > 100 {
certRotationPercent = 100
}
m := manager{ m := manager{
certSigningRequestClient: certSigningRequestClient, certSigningRequestClient: certSigningRequestClient,
template: template, template: template,
usages: usages, usages: usages,
certStore: certificateStore, certStore: certificateStore,
cert: cert, cert: cert,
shouldRotatePercent: certRotationPercent,
} }
return &m, nil return &m, nil
@ -129,15 +122,10 @@ func (m *manager) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certifi
// Start will start the background work of rotating the certificates. // Start will start the background work of rotating the certificates.
func (m *manager) Start() { func (m *manager) Start() {
if m.shouldRotatePercent < 1 {
glog.V(2).Infof("Certificate rotation is not enabled.")
return
}
// Certificate rotation depends on access to the API server certificate // Certificate rotation depends on access to the API server certificate
// signing API, so don't start the certificate manager if we don't have a // signing API, so don't start the certificate manager if we don't have a
// client. This will happen on the master, where the kubelet is responsible // client. This will happen on the cluster master, where the kubelet is
// for bootstrapping the pods of the master components. // responsible for bootstrapping the pods of the master components.
if m.certSigningRequestClient == nil { if m.certSigningRequestClient == nil {
glog.V(2).Infof("Certificate rotation is not enabled, no connection to the apiserver.") glog.V(2).Infof("Certificate rotation is not enabled, no connection to the apiserver.")
return return
@ -160,9 +148,17 @@ func (m *manager) shouldRotate() bool {
m.certAccessLock.RLock() m.certAccessLock.RLock()
defer m.certAccessLock.RUnlock() defer m.certAccessLock.RUnlock()
notAfter := m.cert.Leaf.NotAfter notAfter := m.cert.Leaf.NotAfter
total := notAfter.Sub(m.cert.Leaf.NotBefore) totalDuration := float64(notAfter.Sub(m.cert.Leaf.NotBefore))
remaining := notAfter.Sub(time.Now())
return remaining < 0 || uint(remaining*100/total) < m.shouldRotatePercent // Use some jitter to set the rotation threshold so each node will rotate
// at approximately 70-90% of the total lifetime of the certificate. With
// jitter, if a number of nodes are added to a cluster at approximately the
// same time (such as cluster creation time), they won't all try to rotate
// certificates at the same time for the rest of the life of the cluster.
jitteryDuration := wait.Jitter(time.Duration(totalDuration), 0.2) - time.Duration(totalDuration*0.3)
rotationThreshold := m.cert.Leaf.NotBefore.Add(jitteryDuration)
return time.Now().After(rotationThreshold)
} }
func (m *manager) rotateCerts() error { func (m *manager) rotateCerts() error {

View File

@ -78,18 +78,6 @@ O1eRCsCGPAnUCviFgNeH15ug+6N54DTTR6ZV/TTV64FDOcsox9nrhYcmH9sYuITi
-----END CERTIFICATE-----` -----END CERTIFICATE-----`
) )
func TestNewManagerNoRotation(t *testing.T) {
cert, err := tls.X509KeyPair([]byte(certificateData), []byte(privateKeyData))
if err != nil {
t.Fatalf("Unable to initialize a certificate: %v", err)
}
store := &fakeStore{cert: &cert}
if _, err := NewManager(nil, &x509.CertificateRequest{}, []certificates.KeyUsage{}, store, 0); err != nil {
t.Fatalf("Failed to initialize the certificate manager: %v", err)
}
}
func TestShouldRotate(t *testing.T) { func TestShouldRotate(t *testing.T) {
now := time.Now() now := time.Now()
tests := []struct { tests := []struct {
@ -98,12 +86,16 @@ func TestShouldRotate(t *testing.T) {
notAfter time.Time notAfter time.Time
shouldRotate bool shouldRotate bool
}{ }{
{"half way", now.Add(-24 * time.Hour), now.Add(24 * time.Hour), false}, {"just issued, still good", now.Add(-1 * time.Hour), now.Add(99 * time.Hour), false},
{"nearly there", now.Add(-100 * time.Hour), now.Add(1 * time.Hour), true}, {"half way expired, still good", now.Add(-24 * time.Hour), now.Add(24 * time.Hour), false},
{"just started", now.Add(-1 * time.Hour), now.Add(100 * time.Hour), false}, {"mostly expired, still good", now.Add(-69 * time.Hour), now.Add(31 * time.Hour), false},
{"just about expired, should rotate", now.Add(-91 * time.Hour), now.Add(9 * time.Hour), true},
{"nearly expired, should rotate", now.Add(-99 * time.Hour), now.Add(1 * time.Hour), true},
{"already expired, should rotate", now.Add(-10 * time.Hour), now.Add(-1 * time.Hour), true},
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
m := manager{ m := manager{
cert: &tls.Certificate{ cert: &tls.Certificate{
Leaf: &x509.Certificate{ Leaf: &x509.Certificate{
@ -113,17 +105,15 @@ func TestShouldRotate(t *testing.T) {
}, },
template: &x509.CertificateRequest{}, template: &x509.CertificateRequest{},
usages: []certificates.KeyUsage{}, usages: []certificates.KeyUsage{},
shouldRotatePercent: 10,
} }
if m.shouldRotate() != test.shouldRotate { if m.shouldRotate() != test.shouldRotate {
t.Errorf("For test case %s, time %v, a certificate issued for (%v, %v) should rotate should be %t.", t.Errorf("For time %v, a certificate issued for (%v, %v) should rotate should be %t.",
test.name,
now, now,
m.cert.Leaf.NotBefore, m.cert.Leaf.NotBefore,
m.cert.Leaf.NotAfter, m.cert.Leaf.NotAfter,
test.shouldRotate) test.shouldRotate)
} }
})
} }
} }