Skip to content

Commit a7b3b37

Browse files
authored
Merge pull request #30 from ovotech/add-self-sa-for-providers
Add isSelf() func to determine if current key is one used by ckr
2 parents bbb0e34 + 9dd7a4c commit a7b3b37

File tree

3 files changed

+93
-21
lines changed

3 files changed

+93
-21
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
ckr.yaml
22
config.*
3+
*.asc

cmd/rotate.go

+66-21
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type keyWriter interface {
3131
type cloudProvider struct {
3232
Name string
3333
Project string
34+
Self string
3435
}
3536

3637
//datadog type
@@ -92,6 +93,7 @@ type googleAuthProvider struct {
9293
tokenSource oauth2.TokenSource
9394
}
9495

96+
//rotationCandidate type
9597
type rotationCandidate struct {
9698
key keys.Key
9799
keyLocation keyLocations
@@ -160,7 +162,7 @@ func keyProviders(c config) (keyProviders []keys.Provider) {
160162
//validateFlags returns an error that's not nil if provided string values fail
161163
// a set of validation rules
162164
func validateFlags(account, provider, project string) (err error) {
163-
if (len(account) > 0 && len(provider) == 0) || (len(account) == 0 && len(provider) > 0) {
165+
if len(account) > 0 && len(provider) == 0 {
164166
err = errors.New("Both account AND provider flags must be set")
165167
return
166168
}
@@ -171,6 +173,16 @@ func validateFlags(account, provider, project string) (err error) {
171173
return
172174
}
173175

176+
//keysOfProviders returns keys from all the configured providers that have passed
177+
// through filtering
178+
func keysOfProviders(c config) (accountKeys []keys.Key, err error) {
179+
if accountKeys, err = keys.Keys(keyProviders(c)); err != nil {
180+
return
181+
}
182+
logger.Infof("Found %d keys in total", len(accountKeys))
183+
return filterKeys(accountKeys, c, account)
184+
}
185+
174186
func rotate() (err error) {
175187
defer logger.Sync()
176188
var c config
@@ -180,21 +192,18 @@ func rotate() (err error) {
180192
if err = validateFlags(account, provider, project); err != nil {
181193
return
182194
}
183-
var accountKeys []keys.Key
184-
if accountKeys, err = keys.Keys(keyProviders(c)); err != nil {
195+
var providerKeys []keys.Key
196+
if providerKeys, err = keysOfProviders(c); err != nil {
185197
return
186198
}
187-
logger.Infof("Found %d keys in total", len(accountKeys))
188-
if accountKeys, err = filterKeys(accountKeys, c, account); err != nil {
189-
return
190-
}
191-
logger.Infof("Filtered down to %d keys based on current app config", len(accountKeys))
199+
logger.Infof("Filtered down to %d keys based on current app config", len(providerKeys))
192200
if !c.RotationMode {
193-
postMetric(accountKeys, c.DatadogAPIKey, c.Datadog)
201+
postMetric(providerKeys, c.DatadogAPIKey, c.Datadog)
194202
return
195203
}
196204
var rc []rotationCandidate
197-
if rc, err = rotationCandidates(accountKeys, c.AccountKeyLocations, c.Credentials, c.DefaultRotationAgeThresholdMins); err != nil {
205+
if rc, err = rotationCandidates(providerKeys, c.AccountKeyLocations,
206+
c.Credentials, c.DefaultRotationAgeThresholdMins); err != nil {
198207
return
199208
}
200209
logger.Infof("Finalised %d keys that are candidates for rotation", len(rc))
@@ -350,6 +359,8 @@ func accountKeyLocation(account string,
350359
return
351360
}
352361

362+
//locationsToUpdate return a slice of structs that implement the keyWriter
363+
// interface, based on the keyLocations supplied
353364
func locationsToUpdate(keyLocation keyLocations) (kws []keyWriter) {
354365

355366
// read locations
@@ -395,6 +406,8 @@ func updateKeyLocation(keyLocations keyLocations, keyID, key, keyProvider string
395406
return
396407
}
397408

409+
//encryptedServiceAccountKey uses github.com/ovotech/mantle to encrypt the
410+
// key string that's passed in
398411
func encryptedServiceAccountKey(key, kmsKey string) (encKey []byte, err error) {
399412
const singleLine = false
400413
const disableValidation = true
@@ -407,27 +420,62 @@ func encryptedServiceAccountKey(key, kmsKey string) (encKey []byte, err error) {
407420
return enc.CipherBytesFromPrimitives([]byte(decodedKey), singleLine, disableValidation, "", "", "", "", kmsKey), nil
408421
}
409422

423+
//validKey returns a bool reflecting whether the key is deemed to be valid, based
424+
// on a number of provider-specific rules. E.g., if the provider is AWS, and
425+
// not configured to include user keys, is the key a user key (and hence invalid)?
426+
func validKey(key keys.Key, config config) bool {
427+
if key.Provider.Provider == "aws" {
428+
return validAwsKey(key, config)
429+
}
430+
return true
431+
}
432+
410433
//filterKeys returns a keys.Key slice created by filtering the provided
411434
// keys.Key slice based on specific rules for each provider
412-
func filterKeys(keys []keys.Key, config config, account string) (filteredKeys []keys.Key, err error) {
413-
for _, key := range keys {
435+
func filterKeys(keysToFilter []keys.Key, config config, account string) (filteredKeys []keys.Key, err error) {
436+
var selfKeys []keys.Key
437+
for _, key := range keysToFilter {
414438
//valid bool is used to filter out keys early, e.g. if config says don't
415439
//include AWS user keys, and the current key happens to be a user key
416-
valid := true
417-
if key.Provider.Provider == "aws" {
418-
valid = validAwsKey(key, config)
440+
if !validKey(key, config) {
441+
continue
419442
}
420443
var eligible bool
421444
if eligible, err = filterKey(account, config, key); err != nil {
422445
return
423446
}
424-
if valid && eligible {
425-
filteredKeys = append(filteredKeys, key)
447+
if eligible {
448+
//don't add the key to filteredKeys yet if it's deemed to be a 'self' key
449+
// (i.e. the key belongs to the process performing this rotation)
450+
if isSelf(config, key) {
451+
logger.Infow("Key has been identified as a cloud-rotator key, so will be processed last",
452+
"keyProvider", key.Provider,
453+
"account", key.Account)
454+
selfKeys = append(selfKeys, key)
455+
} else {
456+
filteredKeys = append(filteredKeys, key)
457+
}
426458
}
427459
}
460+
//now add the 'self' keys
461+
filteredKeys = append(filteredKeys, selfKeys...)
428462
return
429463
}
430464

465+
//isSelf returns true iff the key provided matches the 'self' defined in the
466+
// config.cloudProvider. This means the key is the one being used in the
467+
// rotation process, and should probably be rotated last.
468+
func isSelf(config config, key keys.Key) bool {
469+
for _, cloudProvider := range config.CloudProviders {
470+
if cloudProvider.Name == key.Provider.Provider &&
471+
cloudProvider.Project == key.Provider.GcpProject &&
472+
cloudProvider.Self == key.Account {
473+
return true
474+
}
475+
}
476+
return false
477+
}
478+
431479
//filterKey returns a bool indicating whether the key is eligible for 'use'
432480
func filterKey(account string, config config, key keys.Key) (eligible bool, err error) {
433481
if len(account) > 0 {
@@ -548,10 +596,7 @@ func commitSignKey(name, email, passphrase string) (entity *openpgp.Entity,
548596
return
549597
}
550598
var reader *os.File
551-
// if reader, err = os.Open("/etc/cloud-key-rotator/akr.asc"); err != nil {
552-
// return
553-
// }
554-
if reader, err = os.Open("./akr.asc"); err != nil {
599+
if reader, err = os.Open("/etc/cloud-key-rotator/akr.asc"); err != nil {
555600
return
556601
}
557602
var entityList openpgp.EntityList

cmd/rotate_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,32 @@ func TestFilterKeysExclude(t *testing.T) {
114114
}
115115
}
116116

117+
var validKeyTests = []struct {
118+
provider string
119+
project string
120+
includeUserKeys bool
121+
keyName string
122+
valid bool
123+
}{
124+
{"aws", "", true, "first.last", true},
125+
{"aws", "", true, "firstlast", true},
126+
{"aws", "", false, "first.last", false},
127+
{"aws", "", false, "firstlast", true},
128+
}
129+
130+
func TestValidKey(t *testing.T) {
131+
for _, validKeyTest := range validKeyTests {
132+
appConfig := config{IncludeAwsUserKeys: validKeyTest.includeUserKeys}
133+
key := keys.Key{Provider: keys.Provider{
134+
Provider: validKeyTest.provider, GcpProject: validKeyTest.project}, Name: validKeyTest.keyName}
135+
expected := validKeyTest.valid
136+
actual := validKey(key, appConfig)
137+
if actual != expected {
138+
t.Errorf("Incorrect bool returned, want: %t, got: %t", expected, actual)
139+
}
140+
}
141+
}
142+
117143
var validAwsKeyTests = []struct {
118144
includeUserKeys bool
119145
keyName string

0 commit comments

Comments
 (0)