Skip to content

Commit 8d0ec81

Browse files
committed
fix(rot): Allow a cert to be deployed to multiple stores rather than just one
1 parent fb1de18 commit 8d0ec81

File tree

2 files changed

+112
-60
lines changed

2 files changed

+112
-60
lines changed

cmd/rot.go

Lines changed: 62 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func templateTypeCompletion(cmd *cobra.Command, args []string, toComplete string
8585
}, cobra.ShellCompDirectiveDefault
8686
}
8787

88-
func generateAuditReport(addCerts map[string]string, removeCerts map[string]string, stores map[string]StoreCSVEntry, kfClient *api.Client) ([][]string, map[string]ROTAction, error) {
88+
func generateAuditReport(addCerts map[string]string, removeCerts map[string]string, stores map[string]StoreCSVEntry, kfClient *api.Client) ([][]string, map[string][]ROTAction, error) {
8989
log.Println("[DEBUG] generateAuditReport called")
9090
var (
9191
data [][]string
@@ -103,7 +103,7 @@ func generateAuditReport(addCerts map[string]string, removeCerts map[string]stri
103103
fmt.Printf("%s", cErr)
104104
log.Fatalf("[ERROR] Error writing audit header: %s", cErr)
105105
}
106-
actions := make(map[string]ROTAction)
106+
actions := make(map[string][]ROTAction)
107107

108108
for _, cert := range addCerts {
109109
certLookupReq := api.GetCertificateContextArgs{
@@ -139,15 +139,15 @@ func generateAuditReport(addCerts map[string]string, removeCerts map[string]stri
139139
fmt.Printf("%s", wErr)
140140
log.Printf("[ERROR] Error writing audit row: %s", wErr)
141141
}
142-
actions[cert] = ROTAction{
142+
actions[cert] = append(actions[cert], ROTAction{
143143
Thumbprint: cert,
144144
CertID: certID,
145145
StoreID: store.ID,
146146
StoreType: store.Type,
147147
StorePath: store.Path,
148148
AddCert: true,
149149
RemoveCert: false,
150-
}
150+
})
151151
}
152152
}
153153
}
@@ -176,15 +176,15 @@ func generateAuditReport(addCerts map[string]string, removeCerts map[string]stri
176176
fmt.Printf("%s", wErr)
177177
log.Printf("[ERROR] Error writing row to CSV: %s", wErr)
178178
}
179-
actions[cert] = ROTAction{
179+
actions[cert] = append(actions[cert], ROTAction{
180180
Thumbprint: cert,
181181
CertID: certID,
182182
StoreID: store.ID,
183183
StoreType: store.Type,
184184
StorePath: store.Path,
185185
AddCert: false,
186186
RemoveCert: true,
187-
}
187+
})
188188
} else {
189189
// Cert is not deployed to this store do nothing
190190
row := []string{cert, certIDStr, store.ID, store.Type, store.Machine, store.Path, "false", "false", "false"}
@@ -203,66 +203,68 @@ func generateAuditReport(addCerts map[string]string, removeCerts map[string]stri
203203
fmt.Println(ioErr)
204204
log.Printf("[ERROR] Error closing audit file: %s", ioErr)
205205
}
206-
fmt.Printf("Audit report written to %s", reconcileDefaultFileName)
206+
fmt.Printf("Audit report written to %s\n", reconcileDefaultFileName)
207207
return data, actions, nil
208208
}
209209

210-
func reconcileRoots(actions map[string]ROTAction, kfClient *api.Client, dryRun bool) error {
210+
func reconcileRoots(actions map[string][]ROTAction, kfClient *api.Client, dryRun bool) error {
211211
log.Printf("[DEBUG] Reconciling roots")
212212
if len(actions) == 0 {
213213
log.Printf("[INFO] No actions to take, roots are up-to-date.")
214214
return nil
215215
}
216216
for thumbprint, action := range actions {
217-
if action.AddCert {
218-
log.Printf("[INFO] Adding cert %s to store %s(%s)", thumbprint, action.StoreID, action.StorePath)
219-
if !dryRun {
220-
cStore := api.CertificateStore{
221-
CertificateStoreId: action.StoreID,
222-
Overwrite: true,
223-
}
224-
var stores []api.CertificateStore
225-
stores = append(stores, cStore)
226-
schedule := &api.InventorySchedule{
227-
Immediate: boolToPointer(true),
228-
}
229-
addReq := api.AddCertificateToStore{
230-
CertificateId: action.CertID,
231-
CertificateStores: &stores,
232-
InventorySchedule: schedule,
233-
}
234-
_, err := kfClient.AddCertificateToStores(&addReq)
235-
if err != nil {
236-
fmt.Println("Error adding cert to store: ", err)
237-
log.Fatalf("[ERROR] Error adding cert to store: %s", err)
238-
}
239-
} else {
240-
log.Printf("[INFO] DRY RUN: Would have added cert %s from store %s", thumbprint, action.StoreID)
241-
}
242-
} else if action.RemoveCert {
243-
if !dryRun {
244-
log.Printf("[INFO] Removing cert from store %s", action.StoreID)
245-
cStore := api.CertificateStore{
246-
CertificateStoreId: action.StoreID,
247-
Alias: action.Thumbprint,
248-
}
249-
var stores []api.CertificateStore
250-
stores = append(stores, cStore)
251-
schedule := &api.InventorySchedule{
252-
Immediate: boolToPointer(true),
253-
}
254-
removeReq := api.RemoveCertificateFromStore{
255-
CertificateId: action.CertID,
256-
CertificateStores: &stores,
257-
InventorySchedule: schedule,
258-
}
259-
_, err := kfClient.RemoveCertificateFromStores(&removeReq)
260-
if err != nil {
261-
fmt.Printf("[ERROR] Error removing cert from store: %s", err)
262-
log.Fatalf("[ERROR] Error removing cert from store: %s", err)
217+
for _, a := range action {
218+
if a.AddCert {
219+
log.Printf("[INFO] Adding cert %s to store %s(%s)", thumbprint, a.StoreID, a.StorePath)
220+
if !dryRun {
221+
cStore := api.CertificateStore{
222+
CertificateStoreId: a.StoreID,
223+
Overwrite: true,
224+
}
225+
var stores []api.CertificateStore
226+
stores = append(stores, cStore)
227+
schedule := &api.InventorySchedule{
228+
Immediate: boolToPointer(true),
229+
}
230+
addReq := api.AddCertificateToStore{
231+
CertificateId: a.CertID,
232+
CertificateStores: &stores,
233+
InventorySchedule: schedule,
234+
}
235+
_, err := kfClient.AddCertificateToStores(&addReq)
236+
if err != nil {
237+
fmt.Printf("Error adding cert %s (%d) to store %s (%s): %s\n", a.Thumbprint, a.CertID, a.StoreID, a.StorePath, err)
238+
continue
239+
}
240+
} else {
241+
log.Printf("[INFO] DRY RUN: Would have added cert %s from store %s", thumbprint, a.StoreID)
242+
}
243+
} else if a.RemoveCert {
244+
if !dryRun {
245+
log.Printf("[INFO] Removing cert from store %s", a.StoreID)
246+
cStore := api.CertificateStore{
247+
CertificateStoreId: a.StoreID,
248+
Alias: a.Thumbprint,
249+
}
250+
var stores []api.CertificateStore
251+
stores = append(stores, cStore)
252+
schedule := &api.InventorySchedule{
253+
Immediate: boolToPointer(true),
254+
}
255+
removeReq := api.RemoveCertificateFromStore{
256+
CertificateId: a.CertID,
257+
CertificateStores: &stores,
258+
InventorySchedule: schedule,
259+
}
260+
_, err := kfClient.RemoveCertificateFromStores(&removeReq)
261+
if err != nil {
262+
fmt.Printf("Error removing cert %s (%d) from store %s (%s): %s", a.Thumbprint, a.CertID, a.StoreID, a.StorePath, err)
263+
log.Fatalf("[ERROR] Error removing cert from store: %s", err)
264+
}
265+
} else {
266+
log.Printf("[INFO] DRY RUN: Would have removed cert %s from store %s", thumbprint, a.StoreID)
263267
}
264-
} else {
265-
log.Printf("[INFO] DRY RUN: Would have removed cert %s from store %s", thumbprint, action.StoreID)
266268
}
267269
}
268270
}
@@ -441,8 +443,7 @@ var (
441443
log.Fatalf("Error reading removeCerts file: %s", rErr)
442444
}
443445
removeCertsJSON, _ := json.Marshal(certsToRemove)
444-
fmt.Println(string(removeCertsJSON))
445-
fmt.Println("remove rot called")
446+
log.Printf("[DEBUG] remove certs JSON: %s", string(removeCertsJSON))
446447
} else {
447448
log.Printf("[DEBUG] No removeCerts file specified")
448449
log.Printf("[DEBUG] No removeCerts = %s", certsToRemove)
@@ -520,7 +521,7 @@ var (
520521
if cErr != nil {
521522
log.Fatalf("Error reading CSV file: %s", cErr)
522523
}
523-
actions := make(map[string]ROTAction)
524+
actions := make(map[string][]ROTAction)
524525
fieldMap := make(map[int]string)
525526
for i, field := range AuditHeader {
526527
fieldMap[i] = field
@@ -559,7 +560,7 @@ var (
559560
RemoveCert: removeCert,
560561
}
561562

562-
actions[a.Thumbprint] = a
563+
actions[a.Thumbprint] = append(actions[a.Thumbprint], a)
563564

564565
//actions[cert] = ROTAction{
565566
// Thumbprint: cert,
@@ -674,6 +675,7 @@ var (
674675
if lookupFailures != nil {
675676
fmt.Printf("The following stores could not be found: %s", strings.Join(lookupFailures, ","))
676677
}
678+
fmt.Println("Reconciliation completed. Check orchestrator jobs for details.")
677679
}
678680

679681
},

rot_demo.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env bash
2+
# ROT Help
3+
kfutil stores rot --help
4+
5+
# Create a report of actions the utility will take based on inputs from addCerts.csv and removeCerts.csv. Actions added
6+
# to the report will also fit the criteria of a max of 1 private key, a minimum of 3 certs in the inventory and a
7+
# maximum of 1 leaf cert.
8+
kfutil stores reconcile audit \
9+
--add-certs addCerts.csv \
10+
--remove-certs removeCerts.csv \
11+
--max-keys 1 \
12+
--min-certs 3 \
13+
--max-leaf-certs 1 \
14+
--stores stores.csv
15+
16+
# Add certs listed in addCerts.csv to stores listed if they meet the criteria of a max of 1 private key, a minimum of 3
17+
# certs in the inventory and a maximum of 1 leaf cert. Then remove the certs listed in removeCerts.csv from the stores.
18+
kfutil stores rot reconcile \
19+
--add-certs addCerts.csv \
20+
--remove-certs removeCerts.csv \
21+
--max-keys 1 \
22+
--min-certs 3 \
23+
--max-leaf-certs 1 \
24+
--stores stores.csv
25+
26+
# Add certs listed in addCerts.csv to stores listed with no criteria. Then remove the certs listed in removeCerts.csv \
27+
# from the stores.
28+
kfutil stores rot reconcile \
29+
--add-certs addCerts.csv \
30+
--remove-certs removeCerts.csv \
31+
--stores stores.csv
32+
33+
# Remove all added certs from the demo
34+
kfutil stores rot reconcile \
35+
--remove-certs removeCerts.csv \
36+
--stores stores.csv
37+
kfutil stores rot reconcile \
38+
--remove-certs removeCerts2.csv \
39+
--stores stores.csv
40+
kfutil stores rot reconcile \
41+
--remove-certs addCerts.csv \
42+
--stores stores.csv
43+
kfutil stores rot reconcile \
44+
--remove-certs addCerts2.csv \
45+
--stores stores.csv
46+
47+
# List stores and convert to CSV into stores.csv format
48+
echo '"StoreId","StoreType","StoreMachine","StorePath"' > meow.csv
49+
kfutil stores list | jq -r '.[] | [.Id, .cert_store_type, .ClientMachine, .Storepath] | @csv' >> meow.csv
50+

0 commit comments

Comments
 (0)