Skip to content

Commit 7596c64

Browse files
ystrosjaristiz
authored andcommitted
Add rotation procedure to expiring-certificates
An upcoming version of Ops Manager 2.10 will start including two new fields with the `/api/v0/deployed/certificates` endpoint; rotation_procedure_name and rotation_procedure_url. These fields will be on every certificate returned, and represent the rotation procedure used to rotate that particular certificate. The text output for the `om expiring-certificates` command has been reworked when this data is available to group certificates by procedure because in most cases, following a single procedure will rotate all certificates for that procedure at once; that is, instead of running that procedure once for each certificate separately. The new output looks similar to the following: ``` Getting expiring certificates... Found expiring certificates in the foundation: One or more certificates will expire in 89 days. Please refer to the certificate rotation procedures below. To optimize deployment time, please rotate expiring CA certificates prior to any leaf certificates. Services TLS CA Procedure (https://docs.pivotal.io/ops-manager/2-10/security/pcf-infrastructure/advanced-certificate-rotation.html#services-rotation) credhub: /services/tls_ca: expiring on 28 Feb 23 13:57 UTC Identity Provider SAML Procedure (https://docs.pivotal.io/ops-manager/2-10/security/pcf-infrastructure/rotate-saml-ca.html) cf-625e965c186c7b029061: .uaa.service_provider_key_credentials: expiring on 29 May 22 12:57 UTC Standard CA Procedure (https://docs.pivotal.io/ops-manager/2-10/security/pcf-infrastructure/rotate-cas-and-leaf-certs.html) ops_manager: .properties.nats_client_ca.c8b520555b0bc0a9f9f7: expiring on 27 Feb 26 13:57 UTC .properties.root_ca.c8b520555b0bc0a9f9f7: expiring on 28 Feb 23 13:57 UTC cf-625e965c186c7b029061: /opsmgr/bosh_dns/tls_ca: expiring on 27 Feb 26 14:40 UTC /p-bosh/cf-625e965c186c7b029061/diego-instance-identity-intermediate-ca-2-7: expiring on 28 Feb 24 14:40 UTC /cf/diego-instance-identity-root-ca-2-6: expiring on 27 Feb 25 14:40 UTC Standard Configurable Leaf Procedure (https://docs.pivotal.io/ops-manager/2-10/security/pcf-infrastructure/rotate-configurable-certs.html) cf-625e965c186c7b029061: .properties.networking_poe_ssl_certs[0].certificate: expiring on 29 May 22 12:57 UTC Standard Non-Configurable Leaf Procedure (https://docs.pivotal.io/ops-manager/2-10/security/pcf-infrastructure/rotate-non-configurable-certs.html) p-bosh-38683bbbab412b152fad: .properties.director_ssl: expiring on 28 Feb 24 14:06 UTC .properties.uaa_ssl: expiring on 28 Feb 24 14:06 UTC ... cf-625e965c186c7b029061: .properties.auctioneer_client_cert: expiring on 28 Feb 24 14:06 UTC .properties.auctioneer_server_cert: expiring on 28 Feb 24 14:06 UTC ... 2022/02/28 14:47:46 found expiring certificates in the foundation ``` If the new API fields are blank, then it is assumed that `om` is targeted at an older version of Ops Manager and the previous output format is used instead. [#181158588] Update om CLI to output rotation procedures Signed-off-by: Brian Upton <[email protected]> Signed-off-by: Camila Londoño <[email protected]> Signed-off-by: Long Nguyen <[email protected]>
1 parent 75da144 commit 7596c64

3 files changed

+333
-123
lines changed

api/expiring_certificates_service.go

+11-9
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ type ExpiringCertificatesResponse struct {
1313
}
1414

1515
type ExpiringCertificate struct {
16-
Issuer string `json:"issuer"`
17-
ValidFrom time.Time `json:"valid_from"`
18-
ValidUntil time.Time `json:"valid_until"`
19-
Configurable bool `json:"configurable"`
20-
PropertyReference string `json:"property_reference"`
21-
PropertyType string `json:"property_type"`
22-
ProductGUID string `json:"product_guid"`
23-
Location string `json:"location"`
24-
VariablePath string `json:"variable_path"`
16+
Issuer string `json:"issuer"`
17+
ValidFrom time.Time `json:"valid_from"`
18+
ValidUntil time.Time `json:"valid_until"`
19+
Configurable bool `json:"configurable"`
20+
PropertyReference string `json:"property_reference"`
21+
PropertyType string `json:"property_type"`
22+
ProductGUID string `json:"product_guid"`
23+
Location string `json:"location"`
24+
VariablePath string `json:"variable_path"`
25+
RotationProcedureName string `json:"rotation_procedure_name"`
26+
RotationProcedureUrl string `json:"rotation_procedure_url"`
2527
}
2628

2729
func (a Api) ListExpiringCertificates(expiresWithin string) ([]ExpiringCertificate, error) {

commands/expiring_certificates.go

+80-15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"regexp"
7+
"sort"
78
"strings"
89
"time"
910

@@ -54,28 +55,92 @@ func (e *ExpiringCerts) Execute(args []string) error {
5455

5556
e.logger.Println(color.RedString("Found expiring certificates in the foundation:\n"))
5657

57-
expiringCertsWithVariablePath, expiringCertsWithProductGUID := e.groupByLocation(expiringCerts)
58-
for location, certs := range expiringCertsWithVariablePath {
59-
e.logger.Printf(color.RedString("[X] %s", location))
58+
if len(expiringCerts[0].RotationProcedureName) == 0 {
59+
expiringCertsWithVariablePath, expiringCertsWithProductGUID := e.groupByLocation(expiringCerts)
60+
for location, certs := range expiringCertsWithVariablePath {
61+
e.logger.Printf(color.RedString("[X] %s", location))
6062

61-
for _, cert := range certs {
62-
e.printExpiringCertInfo(cert)
63+
for _, cert := range certs {
64+
e.printExpiringCertInfo(cert, 4)
65+
}
6366
}
64-
}
6567

66-
for location, productGUIDs := range expiringCertsWithProductGUID {
67-
e.logger.Printf(color.RedString("[X] %s", location))
68-
for guid, certs := range productGUIDs {
69-
e.logger.Printf(color.RedString(" %s:", guid))
70-
for _, cert := range certs {
71-
e.printExpiringCertInfo(cert)
68+
for location, productGUIDs := range expiringCertsWithProductGUID {
69+
e.logger.Printf(color.RedString("[X] %s", location))
70+
for guid, certs := range productGUIDs {
71+
e.logger.Printf(color.RedString(" %s:", guid))
72+
for _, cert := range certs {
73+
e.printExpiringCertInfo(cert, 8)
74+
}
7275
}
7376
}
77+
} else {
78+
remainingDuration := e.earliestExpiryDate(expiringCerts).Sub(time.Now())
79+
remainingDays := int(remainingDuration.Hours() / 24)
80+
expiringCertsByProcedure, procedures := e.groupByProcedure(expiringCerts)
81+
82+
e.logger.Printf(color.RedString("One or more certificates will expire in %d days. Please refer to the certificate rotation procedures below. To optimize deployment time, please rotate expiring CA certificates prior to any leaf certificates."), remainingDays)
83+
e.logger.Println()
84+
for _, procedure := range procedures {
85+
certsByTile := expiringCertsByProcedure[procedure]
86+
e.logger.Printf(color.RedString(procedure))
87+
for tile, certs := range certsByTile {
88+
e.logger.Printf(color.RedString(" %s:", tile))
89+
for _, cert := range certs {
90+
e.printExpiringCertInfo(cert, 8)
91+
}
92+
}
93+
e.logger.Println()
94+
}
7495
}
7596

7697
return errors.New("found expiring certificates in the foundation")
7798
}
7899

100+
func (e *ExpiringCerts) earliestExpiryDate(certs []api.ExpiringCertificate) time.Time {
101+
earliestExpiry := certs[0].ValidUntil
102+
for _, cert := range certs {
103+
if cert.ValidUntil.Before(earliestExpiry) {
104+
earliestExpiry = cert.ValidUntil
105+
}
106+
}
107+
return earliestExpiry
108+
}
109+
110+
func (e *ExpiringCerts) groupByProcedure(certs []api.ExpiringCertificate) (map[string]map[string][]api.ExpiringCertificate, []string) {
111+
expiringCertsByProcedure := make(map[string]map[string][]api.ExpiringCertificate)
112+
for _, cert := range certs {
113+
procedureKey := fmt.Sprintf("%v (%v)", cert.RotationProcedureName, cert.RotationProcedureUrl)
114+
tileKey := cert.ProductGUID
115+
116+
// Only CredHub-base certificates may be missing the tile / product GUID
117+
if len(tileKey) == 0 {
118+
tileKey = "credhub"
119+
}
120+
121+
if expiringCertsByProcedure[procedureKey] == nil {
122+
expiringCertsByProcedure[procedureKey] = make(map[string][]api.ExpiringCertificate)
123+
}
124+
125+
expiringCertsByProcedure[procedureKey][tileKey] = append(expiringCertsByProcedure[procedureKey][tileKey], cert)
126+
}
127+
128+
procedureNames := []string{}
129+
for key := range expiringCertsByProcedure {
130+
procedureNames = append(procedureNames, key)
131+
}
132+
133+
sort.SliceStable(procedureNames, func(i, j int) bool {
134+
if strings.Contains(procedureNames[i], "CA") {
135+
return true
136+
}
137+
138+
return procedureNames[i] < procedureNames[j]
139+
})
140+
141+
return expiringCertsByProcedure, procedureNames
142+
}
143+
79144
func (e *ExpiringCerts) groupByLocation(certs []api.ExpiringCertificate) (map[string][]api.ExpiringCertificate, map[string]map[string][]api.ExpiringCertificate) {
80145
expiringCertsWithVariablePath := make(map[string][]api.ExpiringCertificate)
81146
expiringCertsWithProductGUID := make(map[string]map[string][]api.ExpiringCertificate)
@@ -96,7 +161,7 @@ func (e *ExpiringCerts) groupByLocation(certs []api.ExpiringCertificate) (map[st
96161
return expiringCertsWithVariablePath, expiringCertsWithProductGUID
97162
}
98163

99-
func (e *ExpiringCerts) printExpiringCertInfo(cert api.ExpiringCertificate) {
164+
func (e *ExpiringCerts) printExpiringCertInfo(cert api.ExpiringCertificate, indent int) {
100165
expiringStr := "expiring"
101166
if time.Now().After(cert.ValidUntil) {
102167
expiringStr = "expired"
@@ -105,11 +170,11 @@ func (e *ExpiringCerts) printExpiringCertInfo(cert api.ExpiringCertificate) {
105170
validUntil := cert.ValidUntil.Format(time.RFC822)
106171

107172
if cert.VariablePath != "" {
108-
e.logger.Printf(color.RedString(" %s: %s on %s"), cert.VariablePath, expiringStr, validUntil)
173+
e.logger.Printf(color.RedString("%s%s: %s on %s"), strings.Repeat(" ", indent), cert.VariablePath, expiringStr, validUntil)
109174
return
110175
}
111176

112-
e.logger.Printf(color.RedString(" %s: %s on %s"), cert.PropertyReference, expiringStr, validUntil)
177+
e.logger.Printf(color.RedString("%s%s: %s on %s"), strings.Repeat(" ", indent), cert.PropertyReference, expiringStr, validUntil)
113178
}
114179

115180
func (e ExpiringCerts) validateConfig() error {

0 commit comments

Comments
 (0)