Skip to content

Commit 9ced412

Browse files
authored
Refactor installer to surface certificate rotation (#2775)
Signed-off-by: perdasilva <[email protected]>
1 parent 1672739 commit 9ced412

File tree

6 files changed

+201
-58
lines changed

6 files changed

+201
-58
lines changed

pkg/controller/install/certresources.go

+25-11
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (i *StrategyDeploymentInstaller) getCertResources() []certResource {
160160
}
161161

162162
func (i *StrategyDeploymentInstaller) certResourcesForDeployment(deploymentName string) []certResource {
163-
result := []certResource{}
163+
var result []certResource
164164
for _, desc := range i.getCertResources() {
165165
if desc.getDeploymentName() == deploymentName {
166166
result = append(result, desc)
@@ -185,8 +185,8 @@ func (i *StrategyDeploymentInstaller) installCertRequirements(strategy Strategy)
185185
}
186186

187187
// Create the CA
188-
expiration, _ := CalculateCertExpirationAndRotateAt()
189-
ca, err := certs.GenerateCA(expiration, Organization)
188+
i.certificateExpirationTime = CalculateCertExpiration(time.Now())
189+
ca, err := certs.GenerateCA(i.certificateExpirationTime, Organization)
190190
if err != nil {
191191
logger.Debug("failed to generate CA")
192192
return nil, err
@@ -201,7 +201,7 @@ func (i *StrategyDeploymentInstaller) installCertRequirements(strategy Strategy)
201201
}
202202

203203
// Update the deployment for each certResource
204-
newDepSpec, caPEM, err := i.installCertRequirementsForDeployment(sddSpec.Name, ca, expiration, sddSpec.Spec, getServicePorts(certResources))
204+
newDepSpec, caPEM, err := i.installCertRequirementsForDeployment(sddSpec.Name, ca, i.certificateExpirationTime, sddSpec.Spec, getServicePorts(certResources))
205205
if err != nil {
206206
return nil, err
207207
}
@@ -213,6 +213,14 @@ func (i *StrategyDeploymentInstaller) installCertRequirements(strategy Strategy)
213213
return strategyDetailsDeployment, nil
214214
}
215215

216+
func (i *StrategyDeploymentInstaller) CertsRotateAt() time.Time {
217+
return CalculateCertRotatesAt(i.certificateExpirationTime)
218+
}
219+
220+
func (i *StrategyDeploymentInstaller) CertsRotated() bool {
221+
return i.certificatesRotated
222+
}
223+
216224
func ShouldRotateCerts(csv *v1alpha1.ClusterServiceVersion) bool {
217225
now := metav1.Now()
218226
if !csv.Status.CertsRotateAt.IsZero() && csv.Status.CertsRotateAt.Before(&now) {
@@ -222,10 +230,12 @@ func ShouldRotateCerts(csv *v1alpha1.ClusterServiceVersion) bool {
222230
return false
223231
}
224232

225-
func CalculateCertExpirationAndRotateAt() (expiration time.Time, rotateAt time.Time) {
226-
expiration = time.Now().Add(DefaultCertValidFor)
227-
rotateAt = expiration.Add(-1 * DefaultCertMinFresh)
228-
return
233+
func CalculateCertExpiration(startingFrom time.Time) time.Time {
234+
return startingFrom.Add(DefaultCertValidFor)
235+
}
236+
237+
func CalculateCertRotatesAt(certExpirationTime time.Time) time.Time {
238+
return certExpirationTime.Add(-1 * DefaultCertMinFresh)
229239
}
230240

231241
func (i *StrategyDeploymentInstaller) installCertRequirementsForDeployment(deploymentName string, ca *certs.KeyPair, expiration time.Time, depSpec appsv1.DeploymentSpec, ports []corev1.ServicePort) (*appsv1.DeploymentSpec, []byte, error) {
@@ -316,9 +326,12 @@ func (i *StrategyDeploymentInstaller) installCertRequirementsForDeployment(deplo
316326
secret = existingSecret
317327
caPEM = existingCAPEM
318328
caHash = certs.PEMSHA256(caPEM)
319-
} else if _, err := i.strategyClient.GetOpClient().UpdateSecret(secret); err != nil {
320-
logger.Warnf("could not update secret %s", secret.GetName())
321-
return nil, nil, err
329+
} else {
330+
if _, err := i.strategyClient.GetOpClient().UpdateSecret(secret); err != nil {
331+
logger.Warnf("could not update secret %s", secret.GetName())
332+
return nil, nil, err
333+
}
334+
i.certificatesRotated = true
322335
}
323336
} else if apierrors.IsNotFound(err) {
324337
// Create the secret
@@ -335,6 +348,7 @@ func (i *StrategyDeploymentInstaller) installCertRequirementsForDeployment(deplo
335348
return nil, nil, err
336349
}
337350
}
351+
i.certificatesRotated = true
338352
} else {
339353
return nil, nil, err
340354
}

pkg/controller/install/deployment.go

+19-14
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package install
33
import (
44
"fmt"
55
"hash/fnv"
6+
"time"
67

78
log "github.com/sirupsen/logrus"
89
appsv1 "k8s.io/api/apps/v1"
@@ -22,13 +23,15 @@ import (
2223
const DeploymentSpecHashLabelKey = "olm.deployment-spec-hash"
2324

2425
type StrategyDeploymentInstaller struct {
25-
strategyClient wrappers.InstallStrategyDeploymentInterface
26-
owner ownerutil.Owner
27-
previousStrategy Strategy
28-
templateAnnotations map[string]string
29-
initializers DeploymentInitializerFuncChain
30-
apiServiceDescriptions []certResource
31-
webhookDescriptions []certResource
26+
strategyClient wrappers.InstallStrategyDeploymentInterface
27+
owner ownerutil.Owner
28+
previousStrategy Strategy
29+
templateAnnotations map[string]string
30+
initializers DeploymentInitializerFuncChain
31+
apiServiceDescriptions []certResource
32+
webhookDescriptions []certResource
33+
certificateExpirationTime time.Time
34+
certificatesRotated bool
3235
}
3336

3437
var _ Strategy = &v1alpha1.StrategyDetailsDeployment{}
@@ -77,13 +80,15 @@ func NewStrategyDeploymentInstaller(strategyClient wrappers.InstallStrategyDeplo
7780
}
7881

7982
return &StrategyDeploymentInstaller{
80-
strategyClient: strategyClient,
81-
owner: owner,
82-
previousStrategy: previousStrategy,
83-
templateAnnotations: templateAnnotations,
84-
initializers: initializers,
85-
apiServiceDescriptions: apiDescs,
86-
webhookDescriptions: webhookDescs,
83+
strategyClient: strategyClient,
84+
owner: owner,
85+
previousStrategy: previousStrategy,
86+
templateAnnotations: templateAnnotations,
87+
initializers: initializers,
88+
apiServiceDescriptions: apiDescs,
89+
webhookDescriptions: webhookDescs,
90+
certificatesRotated: false,
91+
certificateExpirationTime: time.Time{},
8792
}
8893
}
8994

pkg/controller/install/resolver.go

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package install
55

66
import (
77
"fmt"
8+
"time"
89

910
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1011
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/wrappers"
@@ -20,6 +21,8 @@ type Strategy interface {
2021
type StrategyInstaller interface {
2122
Install(strategy Strategy) error
2223
CheckInstalled(strategy Strategy) (bool, error)
24+
CertsRotateAt() time.Time
25+
CertsRotated() bool
2326
}
2427

2528
type StrategyResolverInterface interface {
@@ -68,3 +71,11 @@ func (i *NullStrategyInstaller) Install(s Strategy) error {
6871
func (i *NullStrategyInstaller) CheckInstalled(s Strategy) (bool, error) {
6972
return true, nil
7073
}
74+
75+
func (i *NullStrategyInstaller) CertsRotateAt() time.Time {
76+
return time.Time{}
77+
}
78+
79+
func (i *NullStrategyInstaller) CertsRotated() bool {
80+
return false
81+
}

pkg/controller/operators/olm/operator.go

+2-21
Original file line numberDiff line numberDiff line change
@@ -1888,20 +1888,9 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v
18881888
return
18891889
}
18901890

1891-
// Only update certificate status if:
1892-
// - the CSV has CAResources; and
1893-
// - the certificate lastUpdated and rotateAt timestamps have not already been set, or the certificate should be rotated
1894-
// Note: the code here is a bit wonky and it wasn't clear how to clean it up without some major refactoring:
1895-
// the installer is in charge of generating and rotating the certificates. It detects whether a certificate should be rotated by
1896-
// looking at the csv.status.RotateAt value. But, it does not update this value or surface
1897-
// the certificate expiry information. So, the rotatedAt value is to be re-calculated here. This is bad because you have
1898-
// two different components doing the same thing (installer and operator are both calculating RotateAt). If we're not careful
1899-
// there could be skew
1900-
// See pkg/controller/install/certresources.go
1901-
if shouldUpdateCertificateDates(out) {
1891+
if installer.CertsRotated() {
19021892
now := metav1.Now()
1903-
_, rotateAt := install.CalculateCertExpirationAndRotateAt()
1904-
rotateTime := metav1.NewTime(rotateAt)
1893+
rotateTime := metav1.NewTime(installer.CertsRotateAt())
19051894
out.Status.CertsLastUpdated = &now
19061895
out.Status.CertsRotateAt = &rotateTime
19071896
}
@@ -2531,11 +2520,3 @@ func (a *Operator) ensureLabels(in *v1alpha1.ClusterServiceVersion, labelSets ..
25312520
out, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(out.GetNamespace()).Update(context.TODO(), out, metav1.UpdateOptions{})
25322521
return out, err
25332522
}
2534-
2535-
// shouldUpdateCertificateDates checks the csv status to decide whether
2536-
// status.CertsLastUpdated and status.CertsRotateAt should be updated
2537-
// returns true if the CSV has CAResources and status.RotatedAt is not set OR its time to rotate the certificates
2538-
func shouldUpdateCertificateDates(csv *v1alpha1.ClusterServiceVersion) bool {
2539-
isNotSet := csv.Status.CertsRotateAt == nil || csv.Status.CertsRotateAt.IsZero()
2540-
return csv.HasCAResources() && (isNotSet || install.ShouldRotateCerts(csv))
2541-
}

pkg/controller/operators/olm/operator_test.go

+15-12
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ func (i *TestInstaller) CheckInstalled(s install.Strategy) (bool, error) {
9595
return true, nil
9696
}
9797

98+
func (i *TestInstaller) CertsRotateAt() time.Time {
99+
return time.Time{}
100+
}
101+
102+
func (i *TestInstaller) CertsRotated() bool {
103+
return false
104+
}
105+
98106
func ownerLabelFromCSV(name, namespace string) map[string]string {
99107
return map[string]string{
100108
ownerutil.OwnerKey: name,
@@ -5112,9 +5120,6 @@ func TestCARotation(t *testing.T) {
51125120
logrus.SetLevel(logrus.DebugLevel)
51135121
namespace := "ns"
51145122

5115-
//apiHash, err := resolvercache.APIKeyToGVKHash(opregistry.APIKey{Group: "g1", Version: "v1", Kind: "c1"})
5116-
//require.NoError(t, err)
5117-
51185123
defaultOperatorGroup := &operatorsv1.OperatorGroup{
51195124
TypeMeta: metav1.TypeMeta{
51205125
Kind: "OperatorGroup",
@@ -5137,9 +5142,8 @@ func TestCARotation(t *testing.T) {
51375142
}
51385143

51395144
// Generate valid and expired CA fixtures
5140-
expirationTime, rotationTime := install.CalculateCertExpirationAndRotateAt()
5141-
expiresAt := metav1.Time{Time: expirationTime}
5142-
rotateAt := metav1.Time{Time: rotationTime}
5145+
expiresAt := metav1.NewTime(install.CalculateCertExpiration(time.Now()))
5146+
rotateAt := metav1.NewTime(install.CalculateCertRotatesAt(expiresAt.Time))
51435147

51445148
lastUpdate := metav1.Time{Time: time.Now().UTC()}
51455149

@@ -5358,18 +5362,17 @@ func TestCARotation(t *testing.T) {
53585362
require.NoError(t, err)
53595363
require.NotNil(t, serviceSecret)
53605364

5361-
// Extract certificate
5365+
// Extract certificate validity period
53625366
start, end, err := GetServiceCertificaValidityPeriod(serviceSecret)
53635367
require.NoError(t, err)
53645368
require.NotNil(t, start)
53655369
require.NotNil(t, end)
53665370

5367-
// Compare csv status timestamps with certificate timestamps
5368-
// NOTE: These values (csv.Status.Certs* and the certificate expiry and rotation are calculated
5369-
// with the same method but independently, therefore a second granularity will need to suffice.
5370-
// See https://github.com/operator-framework/operator-lifecycle-manager/issues/2764 for more info.
53715371
rotationTime := end.Add(-1 * install.DefaultCertMinFresh)
5372-
require.Equal(t, start.Unix(), outCSV.Status.CertsLastUpdated.Unix())
5372+
// The csv status is updated after the certificate is created/rotated
5373+
require.LessOrEqual(t, start.Unix(), outCSV.Status.CertsLastUpdated.Unix())
5374+
5375+
// Rotation time should always be the same between the certificate and the status
53735376
require.Equal(t, rotationTime.Unix(), outCSV.Status.CertsRotateAt.Unix())
53745377
}
53755378
}

0 commit comments

Comments
 (0)