From e63491d20a463f14366f68be46ea79d027ddbc5c Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Mon, 1 Aug 2022 11:27:09 +0200 Subject: [PATCH 01/13] zhars/use_kms_per_client Implementation of KMS pre client --- cmd/acra-keymaker/acra-keymaker.go | 39 +++- cmd/acra-server/acra-server.go | 35 +++- keystore/filesystem/filesystem_backup.go | 46 +++-- keystore/filesystem/key_export.go | 62 +++--- keystore/filesystem/server_keystore.go | 206 +++++++++++++------- keystore/filesystem/server_keystore_test.go | 50 +++-- keystore/filesystem/translator_keystore.go | 5 +- keystore/keystore.go | 77 +++++++- keystore/kms/key_encryptor.go | 70 +++++++ keystore/kms/key_making_wrapper.go | 115 +++++++++++ keystore/v2/keystore/filesystem/export.go | 8 +- keystore/v2/keystore/filesystem/keyStore.go | 11 +- keystore/v2/keystore/importV1.go | 17 +- 13 files changed, 551 insertions(+), 190 deletions(-) create mode 100644 keystore/kms/key_encryptor.go create mode 100644 keystore/kms/key_making_wrapper.go diff --git a/cmd/acra-keymaker/acra-keymaker.go b/cmd/acra-keymaker/acra-keymaker.go index 8565d829b..a07d220d5 100644 --- a/cmd/acra-keymaker/acra-keymaker.go +++ b/cmd/acra-keymaker/acra-keymaker.go @@ -198,6 +198,11 @@ func main() { switch *keystoreVersion { case "v1": store = openKeyStoreV1(*outputDir, *outputPublicKey, keyLoader) + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { + keyManager, _ := kmsOptions.NewKeyManager() + store = baseKMS.NewKeyMakingWrapper(store, keyManager) + } + case "v2": store = openKeyStoreV2(*outputDir, keyLoader) case "": @@ -209,6 +214,8 @@ func main() { } if *poisonRecord { + //TODO: ask if we want to create KMS key per each poison key (private and symmetric) or just one + // Generate poison record symmetric key if err = store.GeneratePoisonSymmetricKey(); err != nil { panic(err) @@ -287,23 +294,35 @@ func main() { } func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoader) keystore.KeyMaking { - masterKey, err := loader.LoadMasterKey() - if err != nil { - log.WithError(err).Errorln("Cannot load master key") - os.Exit(1) - } - scellEncryptor, err := keystore.NewSCellKeyEncryptor(masterKey) - if err != nil { - log.WithError(err).Errorln("Can't init scell encryptor") - os.Exit(1) + var keyStoreEncryptor keystore.KeyEncryptor + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { + keyManager, err := kmsOptions.NewKeyManager() + if err != nil { + log.WithError(err).Errorln("Failed to initializer kms KeyManager") + os.Exit(1) + } + + keyStoreEncryptor = baseKMS.NewKeyEncryptor(keyManager) + } else { + masterKey, err := loader.LoadMasterKey() + if err != nil { + log.WithError(err).Errorln("Cannot load master key") + os.Exit(1) + } + keyStoreEncryptor, err = keystore.NewSCellKeyEncryptor(masterKey) + if err != nil { + log.WithError(err).Errorln("Can't init scell encryptor") + os.Exit(1) + } } + keyStore := filesystem.NewCustomFilesystemKeyStore() if outputPublic != output { keyStore.KeyDirectories(output, outputPublic) } else { keyStore.KeyDirectory(output) } - keyStore.Encryptor(scellEncryptor) + keyStore.Encryptor(keyStoreEncryptor) redis := cmd.GetRedisParameters() if redis.KeysConfigured() { keyStorage, err := filesystem.NewRedisStorage(redis.HostPort, redis.Password, redis.DBKeys, nil) diff --git a/cmd/acra-server/acra-server.go b/cmd/acra-server/acra-server.go index 54fcaa241..99eedfbaa 100644 --- a/cmd/acra-server/acra-server.go +++ b/cmd/acra-server/acra-server.go @@ -37,6 +37,7 @@ import ( "errors" "flag" "fmt" + baseKMS "github.com/cossacklabs/acra/keystore/kms" "net/http" _ "net/http/pprof" "os" @@ -870,20 +871,34 @@ func waitReadPipe(timeoutDuration time.Duration) error { } func openKeyStoreV1(output string, cacheSize int, loader keyloader.MasterKeyLoader) (keystore.ServerKeyStore, error) { - masterKey, err := loader.LoadMasterKey() - if err != nil { - log.WithError(err).Errorln("Cannot load master key") - return nil, err - } - scellEncryptor, err := keystore.NewSCellKeyEncryptor(masterKey) - if err != nil { - log.WithError(err).Errorln("Can't init scell encryptor") - return nil, err + var keyStoreEncryptor keystore.KeyEncryptor + // TODO: consider creating new flag exactly for KMS keystore + + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { + keyManager, err := kmsOptions.NewKeyManager() + if err != nil { + log.WithError(err).Errorln("Failed to initializer kms KeyManager") + return nil, err + } + + keyStoreEncryptor = baseKMS.NewKeyEncryptor(keyManager) + } else { + masterKey, err := loader.LoadMasterKey() + if err != nil { + log.WithError(err).Errorln("Cannot load master key") + return nil, err + } + keyStoreEncryptor, err = keystore.NewSCellKeyEncryptor(masterKey) + if err != nil { + log.WithError(err).Errorln("Can't init scell encryptor") + return nil, err + } } + keyStore := filesystem.NewCustomFilesystemKeyStore() keyStore.KeyDirectory(output) keyStore.CacheSize(cacheSize) - keyStore.Encryptor(scellEncryptor) + keyStore.Encryptor(keyStoreEncryptor) redis := cmd.GetRedisParameters() if redis.KeysConfigured() { diff --git a/keystore/filesystem/filesystem_backup.go b/keystore/filesystem/filesystem_backup.go index eee01b63c..1e5fd57f0 100644 --- a/keystore/filesystem/filesystem_backup.go +++ b/keystore/filesystem/filesystem_backup.go @@ -18,6 +18,7 @@ package filesystem import ( "bytes" + "context" "encoding/gob" "path/filepath" "strings" @@ -82,57 +83,58 @@ func isPrivate(fname string) bool { return true } -func getIDFromFilename(fname string) []byte { +func getIDFromFilename(fname string) (keystore.KeyPurpose, []byte) { if isHistoricalFilename(fname) { fname = filepath.Dir(fname) } if fname == PoisonKeyFilename { - return []byte(fname) + return keystore.PurposePoisonRecordKeyPair, []byte(fname) } if fname == getSymmetricKeyName(PoisonKeyFilename) { - return []byte(fname[:len(fname)-len("_sym")]) + return keystore.PurposePoisonRecordSymmetricKey, []byte(fname[:len(fname)-len("_sym")]) } fname = filepath.Base(fname) if strings.HasSuffix(fname, ".old") { fname = fname[:len(fname)-len(".old")] } if strings.HasSuffix(fname, "_hmac") { - return []byte(fname[:len(fname)-len("_hmac")]) + return keystore.PurposeSearchHMAC, []byte(fname[:len(fname)-len("_hmac")]) } if strings.HasSuffix(fname, "_server") { - return []byte(fname[:len(fname)-len("_server")]) + return keystore.PurposeLegacy, []byte(fname[:len(fname)-len("_server")]) } if strings.HasSuffix(fname, "_translator") { - return []byte(fname[:len(fname)-len("_translator")]) + return keystore.PurposeLegacy, []byte(fname[:len(fname)-len("_translator")]) } if strings.HasSuffix(fname, "_storage") { - return []byte(fname[:len(fname)-len("_storage")]) + return keystore.PurposeStorageClientPrivateKey, []byte(fname[:len(fname)-len("_storage")]) } if strings.HasSuffix(fname, "_storage_sym") { - return []byte(fname[:len(fname)-len("_storage_sym")]) + return keystore.PurposeStorageClientSymmetricKey, []byte(fname[:len(fname)-len("_storage_sym")]) } if strings.HasSuffix(fname, "_zone") { - return []byte(fname[:len(fname)-len("_zone")]) + return keystore.PurposeStorageZonePrivateKey, []byte(fname[:len(fname)-len("_zone")]) } if strings.HasSuffix(fname, "_zone_sym") { - return []byte(fname[:len(fname)-len("_zone_sym")]) + return keystore.PurposeStorageZoneSymmetricKey, []byte(fname[:len(fname)-len("_zone_sym")]) } if strings.HasSuffix(fname, "_sym") { - return []byte(fname[:len(fname)-len("_sym")]) + //TODO: what it the sym key here? + return "", []byte(fname[:len(fname)-len("_sym")]) } - return []byte(fname) + return "", []byte(fname) } type dummyEncryptor struct{} // Encrypt return data as is, used for tests -func (d dummyEncryptor) Encrypt(key, context []byte) ([]byte, error) { +func (d dummyEncryptor) Encrypt(ctx context.Context, key []byte, keyContext keystore.KeyContext) ([]byte, error) { return key, nil } // Decrypt return data as is, used for tests -func (d dummyEncryptor) Decrypt(key, context []byte) ([]byte, error) { +func (d dummyEncryptor) Decrypt(ctx context.Context, key []byte, keyContext keystore.KeyContext) ([]byte, error) { return key, nil } @@ -151,7 +153,10 @@ func readFilesAsKeys(files []string, basePath string, encryptor keystore2.KeyEnc // remove absolute first part, leave only relative to path relativeName := strings.Replace(f, basePath+"/", "", -1) if isPrivate(relativeName) { - content, err = encryptor.Decrypt(content, getIDFromFilename(relativeName)) + purpose, id := getIDFromFilename(relativeName) + + keyContext := keystore.NewKeyContext(purpose).WithContext(id).WithZoneID(id).WithClientID(id) + content, err = encryptor.Decrypt(context.Background(), content, *keyContext) if err != nil { return nil, err } @@ -210,7 +215,8 @@ func (store *KeyBackuper) Export() (*keystore.KeysBackup, error) { if err != nil { return nil, err } - encryptedKeys, err := encryptor.Encrypt(buf.Bytes(), nil) + + encryptedKeys, err := encryptor.Encrypt(context.Background(), buf.Bytes(), *keystore.NewEmptyKeyContext()) if err != nil { return nil, err } @@ -223,7 +229,8 @@ func (store *KeyBackuper) Import(backup *keystore.KeysBackup) error { if err != nil { return err } - decryptedData, err := decryptor.Decrypt(backup.Keys, nil) + + decryptedData, err := decryptor.Decrypt(context.Background(), backup.Keys, *keystore.NewEmptyKeyContext()) if err != nil { return err } @@ -240,7 +247,10 @@ func (store *KeyBackuper) Import(backup *keystore.KeysBackup) error { fullName := filepath.Join(store.privateFolder, key.Name) content := key.Content if isPrivateKey { - content, err = store.currentDecryptor.Encrypt(key.Content, getIDFromFilename(key.Name)) + + purpose, id := getIDFromFilename(key.Name) + keyContext := keystore.NewKeyContext(purpose).WithContext(id).WithClientID(id).WithZoneID(id) + content, err = store.currentDecryptor.Encrypt(context.Background(), key.Content, *keyContext) // anyway fill with zeros utils.ZeroizeBytes(key.Content) if err != nil { diff --git a/keystore/filesystem/key_export.go b/keystore/filesystem/key_export.go index be0b5075d..48381bdf3 100644 --- a/keystore/filesystem/key_export.go +++ b/keystore/filesystem/key_export.go @@ -17,6 +17,8 @@ package filesystem import ( + "context" + "github.com/cossacklabs/acra/keystore" "path/filepath" "strings" @@ -58,30 +60,14 @@ type KeyFileClassifier interface { // For example, symmetric keys will not have public or private parts, // and only public or private key of a key pair may be present. type ExportedKey struct { - Purpose string - ID []byte - + Purpose keystore.KeyPurpose PublicPath string PrivatePath string SymmetricPath string + ID []byte } // Exported key purpose constants: -const ( - PurposeSearchHMAC = "search_hmac" - PurposeAuditLog = "audit_log" - PurposePoisonRecordSymmetricKey = "poison_sym_key" - PurposeStorageClientSymmetricKey = "storage_sym_key" - PurposeStorageZoneSymmetricKey = "zone_sym_key" - PurposePoisonRecordKeyPair = "poison_key" - PurposeStorageClientKeyPair = "storage" - PurposeStorageClientPublicKey = "public_storage" - PurposeStorageClientPrivateKey = "private_storage" - PurposeStorageZoneKeyPair = "zone" - PurposeStorageZonePrivateKey = "private_zone" - PurposeStorageZonePublicKey = "public_zone" - PurposeLegacy = "legacy" -) // ExportPublicKey loads a public key for export. func (store *KeyStore) ExportPublicKey(key ExportedKey) (*keys.PublicKey, error) { @@ -102,7 +88,9 @@ func (store *KeyStore) ExportPrivateKey(key ExportedKey) (*keys.PrivateKey, erro if err != nil { return nil, err } - decryptedKey, err := store.encryptor.Decrypt(privateKey.Value, key.ID) + + keyContext := keystore.NewKeyContext(key.Purpose).WithContext(key.ID).WithClientID(key.ID).WithZoneID(key.ID) + decryptedKey, err := store.encryptor.Decrypt(context.Background(), privateKey.Value, *keyContext) if err != nil { return nil, err } @@ -132,7 +120,9 @@ func (store *KeyStore) ExportSymmetricKey(key ExportedKey) ([]byte, error) { if err != nil { return nil, err } - keyValue, err := store.encryptor.Decrypt(encrypted, key.ID) + + keyContext := keystore.NewKeyContext(key.Purpose).WithContext(key.ID).WithClientID(key.ID).WithZoneID(key.ID) + keyValue, err := store.encryptor.Decrypt(context.Background(), encrypted, *keyContext) if err != nil { return nil, err } @@ -222,7 +212,7 @@ func (store *KeyStore) EnumerateExportedKeyPaths() ([]string, error) { // map keys, we have to get a bit creative. func (key *ExportedKey) fusedID() string { - return key.Purpose + string(key.ID) + return key.Purpose.String() + string(key.ID) } func (key *ExportedKey) addPathFrom(other *ExportedKey) { @@ -238,7 +228,7 @@ func (key *ExportedKey) addPathFrom(other *ExportedKey) { } // NewExportedSymmetricKey makes an ExportedKey for an unencrypted symmetric key file. -func NewExportedSymmetricKey(symmetricPath string, context []byte, purpose string) *ExportedKey { +func NewExportedSymmetricKey(symmetricPath string, context []byte, purpose keystore.KeyPurpose) *ExportedKey { return &ExportedKey{ Purpose: purpose, ID: context, @@ -247,7 +237,7 @@ func NewExportedSymmetricKey(symmetricPath string, context []byte, purpose strin } // NewExportedPlaintextSymmetricKey makes an ExportedKey for an unencrypted symmetric key file. -func NewExportedPlaintextSymmetricKey(symmetricPath string, purpose string) *ExportedKey { +func NewExportedPlaintextSymmetricKey(symmetricPath string, purpose keystore.KeyPurpose) *ExportedKey { return &ExportedKey{ Purpose: purpose, SymmetricPath: symmetricPath, @@ -255,7 +245,7 @@ func NewExportedPlaintextSymmetricKey(symmetricPath string, purpose string) *Exp } // NewExportedPublicKey makes an ExportedKey for a public key file. -func NewExportedPublicKey(publicPath string, id []byte, purpose string) *ExportedKey { +func NewExportedPublicKey(publicPath string, id []byte, purpose keystore.KeyPurpose) *ExportedKey { return &ExportedKey{ Purpose: purpose, ID: id, @@ -264,7 +254,7 @@ func NewExportedPublicKey(publicPath string, id []byte, purpose string) *Exporte } // NewExportedPrivateKey makes an ExportedKey for a private key file. -func NewExportedPrivateKey(privatePath string, id []byte, purpose string) *ExportedKey { +func NewExportedPrivateKey(privatePath string, id []byte, purpose keystore.KeyPurpose) *ExportedKey { return &ExportedKey{ Purpose: purpose, ID: id, @@ -277,54 +267,54 @@ func (*DefaultKeyFileClassifier) ClassifyExportedKey(path string) *ExportedKey { filename := filepath.Base(path) if filename == SecureLogKeyFilename { - return NewExportedSymmetricKey(path, []byte(SecureLogKeyFilename), PurposeAuditLog) + return NewExportedSymmetricKey(path, []byte(SecureLogKeyFilename), keystore.PurposeAuditLog) } // Poison key is in ".poison_key" subdirectory, we can't look at filename alone. if strings.HasSuffix(path, "/"+getSymmetricKeyName(PoisonKeyFilename)) { - return NewExportedSymmetricKey(path, []byte(PoisonKeyFilename), PurposePoisonRecordSymmetricKey) + return NewExportedSymmetricKey(path, []byte(PoisonKeyFilename), keystore.PurposePoisonRecordSymmetricKey) } if strings.HasSuffix(filename, "_hmac") { id := []byte(strings.TrimSuffix(filename, "_hmac")) - return NewExportedSymmetricKey(path, id, PurposeSearchHMAC) + return NewExportedSymmetricKey(path, id, keystore.PurposeSearchHMAC) } if strings.HasSuffix(filename, "_storage_sym") { id := []byte(strings.TrimSuffix(filename, "_storage_sym")) - return NewExportedSymmetricKey(path, id, PurposeStorageClientSymmetricKey) + return NewExportedSymmetricKey(path, id, keystore.PurposeStorageClientSymmetricKey) } if strings.HasSuffix(filename, "_zone_sym") { id := []byte(strings.TrimSuffix(filename, "_zone_sym")) - return NewExportedSymmetricKey(path, id, PurposeStorageZoneSymmetricKey) + return NewExportedSymmetricKey(path, id, keystore.PurposeStorageZoneSymmetricKey) } // Poison key pairs use PoisonKeyFilename as context for encryption. if strings.HasSuffix(path, poisonKeyFilenamePublic) { id := []byte(PoisonKeyFilename) - return NewExportedPublicKey(path, id, PurposePoisonRecordKeyPair) + return NewExportedPublicKey(path, id, keystore.PurposePoisonRecordKeyPair) } if strings.HasSuffix(path, PoisonKeyFilename) { id := []byte(PoisonKeyFilename) - return NewExportedPrivateKey(path, id, PurposePoisonRecordKeyPair) + return NewExportedPrivateKey(path, id, keystore.PurposePoisonRecordKeyPair) } if strings.HasSuffix(filename, "_storage.pub") { id := []byte(strings.TrimSuffix(filename, "_storage.pub")) - return NewExportedPublicKey(path, id, PurposeStorageClientKeyPair) + return NewExportedPublicKey(path, id, keystore.PurposeStorageClientKeyPair) } if strings.HasSuffix(filename, "_storage") { id := []byte(strings.TrimSuffix(filename, "_storage")) - return NewExportedPrivateKey(path, id, PurposeStorageClientKeyPair) + return NewExportedPrivateKey(path, id, keystore.PurposeStorageClientKeyPair) } if strings.HasSuffix(filename, "_zone.pub") { id := []byte(strings.TrimSuffix(filename, "_zone.pub")) - return NewExportedPublicKey(path, id, PurposeStorageZoneKeyPair) + return NewExportedPublicKey(path, id, keystore.PurposeStorageZoneKeyPair) } id := []byte(strings.TrimSuffix(filename, "_zone")) - return NewExportedPrivateKey(path, id, PurposeStorageZoneKeyPair) + return NewExportedPrivateKey(path, id, keystore.PurposeStorageZoneKeyPair) } diff --git a/keystore/filesystem/server_keystore.go b/keystore/filesystem/server_keystore.go index 9f493fe95..c315ce9a8 100644 --- a/keystore/filesystem/server_keystore.go +++ b/keystore/filesystem/server_keystore.go @@ -25,9 +25,11 @@ limitations under the License. package filesystem import ( + "context" "crypto/rand" "errors" "fmt" + "github.com/cossacklabs/acra/network" "os" "path/filepath" "runtime" @@ -71,6 +73,7 @@ type KeyStore struct { fs Storage lock *sync.RWMutex encryptor keystore.KeyEncryptor + encryptorCtx context.Context } // NewFileSystemKeyStoreWithCacheSize represents keystore that reads keys from key folders, and stores them in cache. @@ -223,8 +226,10 @@ func newFilesystemKeyStore(privateKeyFolder, publicKeyFolder string, storage Sto return nil, err } } + + ctx, _ := context.WithTimeout(context.Background(), network.DefaultNetworkTimeout) store := &KeyStore{privateKeyDirectory: privateKeyFolder, publicKeyDirectory: publicKeyFolder, - cache: cache, lock: &sync.RWMutex{}, encryptor: encryptor, fs: storage} + cache: cache, lock: &sync.RWMutex{}, encryptor: encryptor, fs: storage, encryptorCtx: ctx} // set callback on cache value removing return store, nil @@ -239,43 +244,43 @@ func (store *KeyStore) CacheOnStart() error { for _, desc := range descriptions { switch desc.Purpose { - case PurposePoisonRecordSymmetricKey: + case keystore.PurposePoisonRecordSymmetricKey: if _, err = store.GetPoisonSymmetricKeys(); err != nil { return err } - case PurposePoisonRecordKeyPair: + case keystore.PurposePoisonRecordKeyPair: if _, err = store.GetPoisonKeyPair(); err != nil { return err } - case PurposeSearchHMAC: + case keystore.PurposeSearchHMAC: if _, err = store.GetHMACSecretKey(desc.ClientID); err != nil { return err } - case PurposeAuditLog: + case keystore.PurposeAuditLog: if _, err = store.GetLogSecretKey(); err != nil { return err } - case PurposeStorageClientSymmetricKey: + case keystore.PurposeStorageClientSymmetricKey: if _, err = store.GetClientIDSymmetricKeys(desc.ClientID); err != nil { return err } - case PurposeStorageClientPrivateKey: + case keystore.PurposeStorageClientPrivateKey: if _, err = store.GetServerDecryptionPrivateKey(desc.ClientID); err != nil { return err } - case PurposeStorageClientPublicKey: + case keystore.PurposeStorageClientPublicKey: if _, err = store.GetClientIDEncryptionPublicKey(desc.ClientID); err != nil { return err } - case PurposeStorageZonePrivateKey: + case keystore.PurposeStorageZonePrivateKey: if _, err = store.GetZonePrivateKey(desc.ZoneID); err != nil { return err } - case PurposeStorageZonePublicKey: + case keystore.PurposeStorageZonePublicKey: if _, err = store.GetZonePublicKey(desc.ZoneID); err != nil { return err } - case PurposeStorageZoneSymmetricKey: + case keystore.PurposeStorageZoneSymmetricKey: if _, err = store.GetZoneIDSymmetricKeys(desc.ZoneID); err != nil { return err } @@ -286,19 +291,19 @@ func (store *KeyStore) CacheOnStart() error { return nil } -func (store *KeyStore) generateKeyPair(filename string, clientID []byte) (*keys.Keypair, error) { +func (store *KeyStore) generateKeyPair(filename string, keyContext keystore.KeyContext) (*keys.Keypair, error) { keypair, err := keys.New(keys.TypeEC) if err != nil { return nil, err } - if err := store.SaveKeyPairWithFilename(keypair, filename, clientID); err != nil { + if err := store.SaveKeyPairWithFilename(keypair, filename, keyContext); err != nil { return nil, err } return keypair, nil } // SaveKeyPairWithFilename save encrypted private key and public key to configured folders -func (store *KeyStore) SaveKeyPairWithFilename(keypair *keys.Keypair, filename string, id []byte) error { +func (store *KeyStore) SaveKeyPairWithFilename(keypair *keys.Keypair, filename string, keyContext keystore.KeyContext) error { privateKeysFolder := filepath.Dir(store.GetPrivateKeyFilePath(filename)) err := store.fs.MkdirAll(privateKeysFolder, keyDirMode) if err != nil { @@ -311,7 +316,7 @@ func (store *KeyStore) SaveKeyPairWithFilename(keypair *keys.Keypair, filename s return err } - encryptedPrivate, err := store.encryptor.Encrypt(keypair.Private.Value, id) + encryptedPrivate, err := store.encryptor.Encrypt(store.encryptorCtx, keypair.Private.Value, keyContext) if err != nil { return err } @@ -415,13 +420,16 @@ func (store *KeyStore) backupHistoricalKeyFile(filename string) error { // generateZoneKey for specific zone id. Will be generated new key pair and private key will be overwrited func (store *KeyStore) generateZoneKey(id []byte) ([]byte, []byte, error) { /* save private key in fs, return id and public key*/ - keypair, err := store.generateKeyPair(GetZoneKeyFilename(id), id) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZonePrivateKey).WithContext(id).WithZoneID(id) + keypair, err := store.generateKeyPair(GetZoneKeyFilename(id), *keyContext) if err != nil { return []byte{}, []byte{}, err } store.lock.Lock() defer store.lock.Unlock() - encryptedKey, err := store.encryptor.Encrypt(keypair.Private.Value, id) + + encryptedKey, err := store.encryptor.Encrypt(store.encryptorCtx, keypair.Private.Value, *keyContext) if err != nil { return nil, nil, nil } @@ -550,7 +558,7 @@ func (store *KeyStore) loadPublicKey(path string) (*keys.PublicKey, error) { return &keys.PublicKey{Value: key}, nil } -func (store *KeyStore) getPrivateKeyByFilename(id []byte, filename string) (*keys.PrivateKey, error) { +func (store *KeyStore) getPrivateKeyByFilename(filename string, keyContext keystore.KeyContext) (*keys.PrivateKey, error) { store.lock.Lock() defer store.lock.Unlock() encryptedKey, ok := store.cache.Get(filename) @@ -562,7 +570,7 @@ func (store *KeyStore) getPrivateKeyByFilename(id []byte, filename string) (*key encryptedKey = encryptedPrivateKey.Value } - decryptedKey, err := store.encryptor.Decrypt(encryptedKey, id) + decryptedKey, err := store.encryptor.Decrypt(store.encryptorCtx, encryptedKey, keyContext) if err != nil { return nil, err } @@ -571,12 +579,12 @@ func (store *KeyStore) getPrivateKeyByFilename(id []byte, filename string) (*key return &keys.PrivateKey{Value: decryptedKey}, nil } -func (store *KeyStore) getPrivateKeysByFilenames(id []byte, filenames []string) ([]*keys.PrivateKey, error) { +func (store *KeyStore) getPrivateKeysByFilenames(filenames []string, keyContext keystore.KeyContext) ([]*keys.PrivateKey, error) { // TODO: this can be optimized to avoid thrashing store.lock and repeatedly revalidating id // by copy-pasting getPrivateKeyByFilename() and extending that to retrieve multiple keys privateKeys := make([]*keys.PrivateKey, len(filenames)) for i, name := range filenames { - key, err := store.getPrivateKeyByFilename(id, name) + key, err := store.getPrivateKeyByFilename(name, keyContext) if err != nil { utils.ZeroizePrivateKeys(privateKeys) return nil, err @@ -620,7 +628,9 @@ func (store *KeyStore) GetClientIDEncryptionPublicKey(clientID []byte) (*keys.Pu // and returns plaintext private key, or reading/decryption error. func (store *KeyStore) GetZonePrivateKey(id []byte) (*keys.PrivateKey, error) { fname := GetZoneKeyFilename(id) - return store.getPrivateKeyByFilename(id, fname) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZonePrivateKey).WithZoneID(id).WithContext(id) + return store.getPrivateKeyByFilename(fname, *keyContext) } // HasZonePrivateKey returns if private key for this zoneID exists in cache or is written to fs. @@ -652,7 +662,9 @@ func (store *KeyStore) GetZonePrivateKeys(id []byte) ([]*keys.PrivateKey, error) if err != nil { return nil, err } - return store.getPrivateKeysByFilenames(id, filenames) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZonePrivateKey).WithZoneID(id).WithContext(id) + return store.getPrivateKeysByFilenames(filenames, *keyContext) } // GetPeerPublicKey returns public key for this clientID, gets it from cache or reads from fs. @@ -681,7 +693,9 @@ func (store *KeyStore) GetPeerPublicKey(id []byte) (*keys.PublicKey, error) { // and returns plaintext private key, or reading/decryption error. func (store *KeyStore) GetPrivateKey(id []byte) (*keys.PrivateKey, error) { fname := getServerKeyFilename(id) - return store.getPrivateKeyByFilename(id, fname) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) + return store.getPrivateKeyByFilename(fname, *keyContext) } // GetServerDecryptionPrivateKey reads encrypted server storage private key from fs, @@ -689,7 +703,8 @@ func (store *KeyStore) GetPrivateKey(id []byte) (*keys.PrivateKey, error) { // and returns plaintext private key, or reading/decryption error. func (store *KeyStore) GetServerDecryptionPrivateKey(id []byte) (*keys.PrivateKey, error) { fname := GetServerDecryptionKeyFilename(id) - return store.getPrivateKeyByFilename(id, fname) + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) + return store.getPrivateKeyByFilename(fname, *keyContext) } // GetServerDecryptionPrivateKeys reads encrypted server storage private keys from fs, @@ -700,7 +715,9 @@ func (store *KeyStore) GetServerDecryptionPrivateKeys(id []byte) ([]*keys.Privat if err != nil { return nil, err } - return store.getPrivateKeysByFilenames(id, filenames) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) + return store.getPrivateKeysByFilenames(filenames, *keyContext) } // GenerateConnectorKeys generates AcraConnector transport EC keypair using clientID as part of key name. @@ -712,7 +729,8 @@ func (store *KeyStore) GenerateConnectorKeys(id []byte) error { } filename := getConnectorKeyFilename(id) - _, err := store.generateKeyPair(filename, id) + keyContext := keystore.NewKeyContext(keystore.PurposeLegacy).WithContext(id) + _, err := store.generateKeyPair(filename, *keyContext) if err != nil { return err } @@ -727,7 +745,9 @@ func (store *KeyStore) GenerateServerKeys(id []byte) error { return keystore.ErrInvalidClientID } filename := getServerKeyFilename(id) - _, err := store.generateKeyPair(filename, id) + + keyContext := keystore.NewKeyContext(keystore.PurposeLegacy).WithContext(id) + _, err := store.generateKeyPair(filename, *keyContext) if err != nil { return err } @@ -742,7 +762,9 @@ func (store *KeyStore) GenerateTranslatorKeys(id []byte) error { return keystore.ErrInvalidClientID } filename := getTranslatorKeyFilename(id) - _, err := store.generateKeyPair(filename, id) + + keyContext := keystore.NewKeyContext(keystore.PurposeLegacy).WithContext(id) + _, err := store.generateKeyPair(filename, *keyContext) if err != nil { return err } @@ -757,7 +779,9 @@ func (store *KeyStore) GenerateDataEncryptionKeys(id []byte) error { if !keystore.ValidateID(id) { return keystore.ErrInvalidClientID } - _, err := store.generateKeyPair(GetServerDecryptionKeyFilename(id), id) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) + _, err := store.generateKeyPair(GetServerDecryptionKeyFilename(id), *keyContext) if err != nil { return err } @@ -809,7 +833,7 @@ func (store *KeyStore) describeDir(dirName string) ([]keystore.KeyDescription, e if err != nil { return nil, err } - if description.Purpose == PurposeLegacy { + if description.Purpose == keystore.PurposeLegacy { log.WithField("ID", description.ID).Warn("Ignoring legacy key") continue } @@ -825,22 +849,22 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr case poisonPrivateKey: return &keystore.KeyDescription{ ID: poisonPrivateKey, - Purpose: PurposePoisonRecordKeyPair, + Purpose: keystore.PurposePoisonRecordKeyPair, }, nil case poisonPublicKey: return &keystore.KeyDescription{ ID: poisonPublicKey, - Purpose: PurposePoisonRecordKeyPair, + Purpose: keystore.PurposePoisonRecordKeyPair, }, nil case poisonSymmetricKey: return &keystore.KeyDescription{ ID: poisonSymmetricKey, - Purpose: PurposePoisonRecordSymmetricKey, + Purpose: keystore.PurposePoisonRecordSymmetricKey, }, nil case legacyWebConfigKey: return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeLegacy, + Purpose: keystore.PurposeLegacy, }, nil } @@ -851,7 +875,7 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr return &keystore.KeyDescription{ ID: id, - Purpose: PurposeLegacy, + Purpose: keystore.PurposeLegacy, ClientID: []byte(components[0]), }, nil } @@ -867,7 +891,7 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr if lastKeyPart == "hmac" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeSearchHMAC, + Purpose: keystore.PurposeSearchHMAC, ClientID: []byte(strings.Join(components[:len(components)-1], "_")), }, nil } @@ -875,7 +899,7 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr if lastKeyPart == "storage" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeStorageClientPrivateKey, + Purpose: keystore.PurposeStorageClientPrivateKey, ClientID: []byte(strings.Join(components[:len(components)-1], "_")), }, nil } @@ -883,7 +907,7 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr if lastKeyPart == "storage.pub" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeStorageClientPublicKey, + Purpose: keystore.PurposeStorageClientPublicKey, ClientID: []byte(strings.Join(components[:len(components)-1], "_")), }, nil } @@ -891,7 +915,7 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr if lastKeyPart == "zone" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeStorageZonePrivateKey, + Purpose: keystore.PurposeStorageZonePrivateKey, ZoneID: []byte(strings.Join(components[:len(components)-1], "_")), }, nil } @@ -899,7 +923,7 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr if lastKeyPart == "zone.pub" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeStorageZonePublicKey, + Purpose: keystore.PurposeStorageZonePublicKey, ZoneID: []byte(strings.Join(components[:len(components)-1], "_")), }, nil } @@ -907,7 +931,7 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr if penultimateKeyPart == "storage" && lastKeyPart == "sym" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeStorageClientSymmetricKey, + Purpose: keystore.PurposeStorageClientSymmetricKey, ClientID: []byte(strings.Join(components[:len(components)-2], "_")), }, nil } @@ -915,7 +939,7 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr if penultimateKeyPart == "zone" && lastKeyPart == "sym" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeStorageZoneSymmetricKey, + Purpose: keystore.PurposeStorageZoneSymmetricKey, ZoneID: []byte(strings.Join(components[:len(components)-2], "_")), }, nil } @@ -923,14 +947,14 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr if penultimateKeyPart == "log" && lastKeyPart == "key" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeAuditLog, + Purpose: keystore.PurposeAuditLog, }, nil } if lastKeyPart == "server" || lastKeyPart == "server.pub" || lastKeyPart == "translator" || lastKeyPart == "translator.pub" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeLegacy, + Purpose: keystore.PurposeLegacy, ClientID: []byte(components[0]), }, nil } @@ -947,10 +971,12 @@ func (store *KeyStore) Reset() { // Returns an error if fs or crypto operations fail. Also, returns ErrKeysNotFound // if the key pair doesn't exist. func (store *KeyStore) GetPoisonKeyPair() (*keys.Keypair, error) { + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair).WithContext([]byte(PoisonKeyFilename)) + privateKey, privateOk := store.cache.Get(PoisonKeyFilename) publicKey, publicOk := store.cache.Get(poisonKeyFilenamePublic) if privateOk && publicOk { - decryptedPrivate, err := store.encryptor.Decrypt(privateKey, []byte(PoisonKeyFilename)) + decryptedPrivate, err := store.encryptor.Decrypt(store.encryptorCtx, privateKey, *keyContext) if err != nil { return nil, err } @@ -965,7 +991,7 @@ func (store *KeyStore) GetPoisonKeyPair() (*keys.Keypair, error) { return nil, err } encryptedPrivate := private.Value - if private.Value, err = store.encryptor.Decrypt(private.Value, []byte(PoisonKeyFilename)); err != nil { + if private.Value, err = store.encryptor.Decrypt(store.encryptorCtx, private.Value, *keyContext); err != nil { return nil, err } publicPath := store.GetPublicKeyFilePath(poisonKeyFilenamePublic) @@ -988,7 +1014,9 @@ func (store *KeyStore) GetPoisonPrivateKeys() ([]*keys.PrivateKey, error) { if err != nil { return nil, err } - poisonKeys, err := store.getPrivateKeysByFilenames([]byte(PoisonKeyFilename), filenames) + + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair).WithContext([]byte(PoisonKeyFilename)) + poisonKeys, err := store.getPrivateKeysByFilenames(filenames, *keyContext) if err != nil { if IsKeyReadError(err) { return nil, keystore.ErrKeysNotFound @@ -1007,7 +1035,9 @@ func (store *KeyStore) GetPoisonPrivateKeys() ([]*keys.PrivateKey, error) { // ErrKeysNotFound if the keys don't exist. func (store *KeyStore) GetPoisonSymmetricKeys() ([][]byte, error) { keyFileName := getSymmetricKeyName(PoisonKeyFilename) - keys, err := store.getSymmetricKeys([]byte(keyFileName), keyFileName) + + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey).WithContext([]byte(keyFileName)) + keys, err := store.getSymmetricKeys(keyFileName, *keyContext) if err != nil { if IsKeyReadError(err) { @@ -1026,7 +1056,9 @@ func (store *KeyStore) GetPoisonSymmetricKeys() ([][]byte, error) { // ErrKeysNotFound if the keys don't exist. func (store *KeyStore) GetPoisonSymmetricKey() ([]byte, error) { keyFileName := getSymmetricKeyName(PoisonKeyFilename) - key, err := store.getLatestSymmetricKey([]byte(keyFileName), keyFileName) + + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey).WithContext([]byte(keyFileName)) + key, err := store.getLatestSymmetricKey(keyFileName, *keyContext) if err == nil { return key, nil } @@ -1047,19 +1079,25 @@ func (store *KeyStore) RotateZoneKey(zoneID []byte) ([]byte, error) { // RotateSymmetricZoneKey generate new symmetric key for ZoneId, overwrite private key with new func (store *KeyStore) RotateSymmetricZoneKey(zoneID []byte) error { keyName := getZoneIDSymmetricKeyName(zoneID) - return store.generateAndSaveSymmetricKey(zoneID, store.GetPrivateKeyFilePath(keyName)) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZoneSymmetricKey).WithZoneID(zoneID).WithContext(zoneID) + return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), *keyContext) } // SaveZoneKeypair save or overwrite zone keypair func (store *KeyStore) SaveZoneKeypair(id []byte, keypair *keys.Keypair) error { filename := GetZoneKeyFilename(id) - return store.SaveKeyPairWithFilename(keypair, filename, id) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZonePrivateKey).WithZoneID(id).WithContext(id) + return store.SaveKeyPairWithFilename(keypair, filename, *keyContext) } // SaveDataEncryptionKeys save or overwrite decryption keypair for client id func (store *KeyStore) SaveDataEncryptionKeys(id []byte, keypair *keys.Keypair) error { filename := GetServerDecryptionKeyFilename(id) - return store.SaveKeyPairWithFilename(keypair, filename, id) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) + return store.SaveKeyPairWithFilename(keypair, filename, *keyContext) } // destroyKeyWithFilename removes private and public key with given filename. @@ -1122,7 +1160,8 @@ func (store *KeyStore) GetHMACSecretKey(id []byte) ([]byte, error) { } } - decryptedKey, err := store.encryptor.Decrypt(encryptedKey, id) + keyContext := keystore.NewKeyContext(keystore.PurposeSearchHMAC).WithClientID(id).WithContext(id) + decryptedKey, err := store.encryptor.Decrypt(store.encryptorCtx, encryptedKey, *keyContext) if err != nil { return nil, err } @@ -1138,7 +1177,9 @@ func (store *KeyStore) GenerateHmacKey(id []byte) error { if err != nil { return err } - encryptedKey, err := store.encryptor.Encrypt(key, id) + + keyContext := keystore.NewKeyContext(keystore.PurposeSearchHMAC).WithContext(id).WithClientID(id) + encryptedKey, err := store.encryptor.Encrypt(store.encryptorCtx, key, *keyContext) if err != nil { return err } @@ -1162,7 +1203,9 @@ func (store *KeyStore) GenerateLogKey() error { if err != nil { return err } - encryptedKey, err := store.encryptor.Encrypt(key, []byte(SecureLogKeyFilename)) + + keyContext := keystore.NewKeyContext(keystore.PurposeAuditLog).WithContext([]byte(SecureLogKeyFilename)) + encryptedKey, err := store.encryptor.Encrypt(store.encryptorCtx, key, *keyContext) if err != nil { return err } @@ -1190,7 +1233,8 @@ func (store *KeyStore) GetLogSecretKey() ([]byte, error) { return nil, err } } - decryptedKey, err := store.encryptor.Decrypt(encryptedKey, []byte(SecureLogKeyFilename)) + keyContext := keystore.NewKeyContext(keystore.PurposeAuditLog).WithContext([]byte(SecureLogKeyFilename)) + decryptedKey, err := store.encryptor.Decrypt(store.encryptorCtx, encryptedKey, *keyContext) if err != nil { return nil, err } @@ -1200,12 +1244,13 @@ func (store *KeyStore) GetLogSecretKey() ([]byte, error) { } // generateSymmetricKey generate symmetric key with specific identifier -func (store *KeyStore) generateAndSaveSymmetricKey(id []byte, filename string) error { +func (store *KeyStore) generateAndSaveSymmetricKey(filename string, keyContext keystore.KeyContext) error { symKey, err := keystore.GenerateSymmetricKey() if err != nil { return err } - encryptedSymKey, err := store.encryptor.Encrypt(symKey, id) + + encryptedSymKey, err := store.encryptor.Encrypt(store.encryptorCtx, symKey, keyContext) if err != nil { return err } @@ -1213,7 +1258,7 @@ func (store *KeyStore) generateAndSaveSymmetricKey(id []byte, filename string) e } // GetSymmetricKey return symmetric key with specific identifier -func (store *KeyStore) readEncryptedKey(id []byte, filename string) ([]byte, error) { +func (store *KeyStore) readEncryptedKey(filename string, keyContext keystore.KeyContext) ([]byte, error) { encryptedSymKey, ok := store.Get(filename) if !ok { var err error @@ -1223,36 +1268,43 @@ func (store *KeyStore) readEncryptedKey(id []byte, filename string) ([]byte, err } store.Add(filename, encryptedSymKey) } - return store.encryptor.Decrypt(encryptedSymKey, id) + return store.encryptor.Decrypt(store.encryptorCtx, encryptedSymKey, keyContext) } // GenerateClientIDSymmetricKey generate symmetric key for specified client id func (store *KeyStore) GenerateClientIDSymmetricKey(id []byte) error { keyName := getClientIDSymmetricKeyName(id) - return store.generateAndSaveSymmetricKey(id, store.GetPrivateKeyFilePath(keyName)) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithClientID(id).WithContext(id) + return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), *keyContext) } // GenerateZoneIDSymmetricKey generate symmetric key for specified zone id func (store *KeyStore) GenerateZoneIDSymmetricKey(id []byte) error { keyName := getZoneIDSymmetricKeyName(id) - return store.generateAndSaveSymmetricKey(id, store.GetPrivateKeyFilePath(keyName)) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZoneSymmetricKey).WithZoneID(id).WithContext(id) + return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), *keyContext) } // GeneratePoisonSymmetricKey generate symmetric key for poison records func (store *KeyStore) GeneratePoisonSymmetricKey() error { keyName := getSymmetricKeyName(PoisonKeyFilename) keyPath := store.GetPrivateKeyFilePath(keyName) - return store.generateAndSaveSymmetricKey([]byte(keyName), keyPath) + + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey).WithContext([]byte(keyName)) + return store.generateAndSaveSymmetricKey(keyPath, *keyContext) } // GeneratePoisonKeyPair generates new poison keypair, saving it in the storage. // Old keypair is rotated. func (store *KeyStore) GeneratePoisonKeyPair() error { - _, err := store.generateKeyPair(PoisonKeyFilename, []byte(PoisonKeyFilename)) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair).WithContext([]byte(PoisonKeyFilename)) + _, err := store.generateKeyPair(PoisonKeyFilename, *keyContext) return err } -func (store *KeyStore) getSymmetricKeys(id []byte, keyname string) ([][]byte, error) { +func (store *KeyStore) getSymmetricKeys(keyname string, keyContext keystore.KeyContext) ([][]byte, error) { keys := make([][]byte, 0, 4) historicalKeys, err := store.GetHistoricalPrivateKeyFilenames(keyname) if err != nil { @@ -1263,7 +1315,7 @@ func (store *KeyStore) getSymmetricKeys(id []byte, keyname string) ([][]byte, er return nil, err } for _, path := range historicalKeys { - key, err := store.readEncryptedKey(id, path) + key, err := store.readEncryptedKey(path, keyContext) if err != nil { for _, key := range keys { utils.ZeroizeSymmetricKey(key) @@ -1275,8 +1327,8 @@ func (store *KeyStore) getSymmetricKeys(id []byte, keyname string) ([][]byte, er return keys, nil } -func (store *KeyStore) getLatestSymmetricKey(id []byte, keyname string) ([]byte, error) { - key, err := store.readEncryptedKey(id, keyname) +func (store *KeyStore) getLatestSymmetricKey(keyname string, keyContext keystore.KeyContext) ([]byte, error) { + key, err := store.readEncryptedKey(keyname, keyContext) if err != nil { return nil, err } @@ -1287,23 +1339,31 @@ func (store *KeyStore) getLatestSymmetricKey(id []byte, keyname string) ([]byte, // GetClientIDSymmetricKeys return symmetric keys for specified client id func (store *KeyStore) GetClientIDSymmetricKeys(id []byte) ([][]byte, error) { keyName := getClientIDSymmetricKeyName(id) - return store.getSymmetricKeys(id, keyName) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithContext(id).WithClientID(id) + return store.getSymmetricKeys(keyName, *keyContext) } // GetClientIDSymmetricKey return latest symmetric key for encryption by specified client id func (store *KeyStore) GetClientIDSymmetricKey(id []byte) ([]byte, error) { keyName := getClientIDSymmetricKeyName(id) - return store.getLatestSymmetricKey(id, keyName) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithContext(id).WithClientID(id) + return store.getLatestSymmetricKey(keyName, *keyContext) } // GetZoneIDSymmetricKeys return symmetric keys for specified zone id func (store *KeyStore) GetZoneIDSymmetricKeys(id []byte) ([][]byte, error) { keyName := getZoneIDSymmetricKeyName(id) - return store.getSymmetricKeys(id, keyName) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZoneSymmetricKey).WithContext(id).WithZoneID(id) + return store.getSymmetricKeys(keyName, *keyContext) } // GetZoneIDSymmetricKey return latest symmetric key for encryption in specified zone id func (store *KeyStore) GetZoneIDSymmetricKey(id []byte) ([]byte, error) { keyName := getZoneIDSymmetricKeyName(id) - return store.getLatestSymmetricKey(id, keyName) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZoneSymmetricKey).WithContext(id).WithZoneID(id) + return store.getLatestSymmetricKey(keyName, *keyContext) } diff --git a/keystore/filesystem/server_keystore_test.go b/keystore/filesystem/server_keystore_test.go index 53c31317a..475332e65 100644 --- a/keystore/filesystem/server_keystore_test.go +++ b/keystore/filesystem/server_keystore_test.go @@ -18,6 +18,7 @@ package filesystem import ( "bytes" + "context" "fmt" "io/ioutil" "log" @@ -53,7 +54,9 @@ func testGenerateKeyPair(store *KeyStore, t *testing.T) { t.Fatal(err) } defer store.fs.Remove(path) - keypair, err := store.generateKeyPair(path, clientID) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithContext(clientID) + keypair, err := store.generateKeyPair(path, *keyContext) if err != nil { t.Fatal(err) } @@ -141,7 +144,8 @@ func testGenerateSymKeyUncreatedDir(store *KeyStore, t *testing.T) { t.Fatal(err) } - err = store.generateAndSaveSymmetricKey([]byte("key"), fmt.Sprintf("%s/%s", dir, "test_id_sym")) + keyContext := keystore.NewEmptyKeyContext().WithContext([]byte("key")) + err = store.generateAndSaveSymmetricKey(fmt.Sprintf("%s/%s", dir, "test_id_sym"), *keyContext) if err != nil { t.Fatal(err) } @@ -613,7 +617,8 @@ func testFilesystemKeyStoreSymmetricWithCache(storage Storage, t *testing.T) { if !ok { t.Fatal("Expected key in result") } - decrypted, err := encryptor.Decrypt(value, testID2) + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithContext(testID2) + decrypted, err := encryptor.Decrypt(context.Background(), value, *keyContext) if err != nil { t.Fatal(err) } @@ -691,7 +696,9 @@ func testFilesystemKeyStoreWithCache(storage Storage, t *testing.T) { if !ok { t.Fatal("Expected key in result") } - decrypted, err := encryptor.Decrypt(value, testID2) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithContext(testID2) + decrypted, err := encryptor.Decrypt(context.Background(), value, *keyContext) if err != nil { t.Fatal(err) } @@ -952,13 +959,14 @@ func testSaveKeypairs(store *KeyStore, t *testing.T) { } // no matter which function to generate correct filename we will use filename := GetServerDecryptionKeyFilename(testID) - if _, err := store.getPrivateKeyByFilename(testID, filename); err == nil { + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithContext(testID) + if _, err := store.getPrivateKeyByFilename(filename, *keyContext); err == nil { t.Fatal("Expected error") } - if err = store.SaveKeyPairWithFilename(startKeypair, filename, testID); err != nil { + if err = store.SaveKeyPairWithFilename(startKeypair, filename, *keyContext); err != nil { t.Fatal(err) } - if privateKey, err := store.getPrivateKeyByFilename(testID, filename); err != nil { + if privateKey, err := store.getPrivateKeyByFilename(filename, *keyContext); err != nil { t.Fatal(err) } else { if !bytes.Equal(startKeypair.Private.Value, privateKey.Value) { @@ -966,10 +974,10 @@ func testSaveKeypairs(store *KeyStore, t *testing.T) { } } - if err = store.SaveKeyPairWithFilename(overwritedKeypair, filename, testID); err != nil { + if err = store.SaveKeyPairWithFilename(overwritedKeypair, filename, *keyContext); err != nil { t.Fatal(err) } - if privateKey, err := store.getPrivateKeyByFilename(testID, filename); err != nil { + if privateKey, err := store.getPrivateKeyByFilename(filename, *keyContext); err != nil { t.Fatal(err) } else { if !bytes.Equal(overwritedKeypair.Private.Value, privateKey.Value) { @@ -1052,7 +1060,7 @@ func TestFilesystemKeyStoreExport(t *testing.T) { for i := range exportedKeys { switch exportedKeys[i].Purpose { - case PurposePoisonRecordKeyPair: + case keystore.PurposePoisonRecordKeyPair: seenPoisonKeyPair = true publicKey, err := keyStore.ExportPublicKey(exportedKeys[i]) if err != nil { @@ -1068,7 +1076,7 @@ func TestFilesystemKeyStoreExport(t *testing.T) { if !bytes.Equal(poisonKeyPair.Private.Value, privateKey.Value) { t.Error("incorrect poison record private key value") } - case PurposeStorageClientKeyPair: + case keystore.PurposeStorageClientKeyPair: seenStorageClientKeyPair = true publicKey, err := keyStore.ExportPublicKey(exportedKeys[i]) if err != nil { @@ -1084,7 +1092,7 @@ func TestFilesystemKeyStoreExport(t *testing.T) { if !bytes.Equal(storagePrivateKey.Value, privateKey.Value) { t.Error("incorrect client storage private key value") } - case PurposeStorageZoneKeyPair: + case keystore.PurposeStorageZoneKeyPair: seenStorageZoneKeyPair = true publicKey, err := keyStore.ExportPublicKey(exportedKeys[i]) if err != nil { @@ -1542,15 +1550,15 @@ func generateEveryKey(keyStore *KeyStore, t *testing.T) { func getAllExpectedKeys() []keystore.KeyDescription { expectedKeys := []keystore.KeyDescription{ - {ID: "poison_key", Purpose: PurposePoisonRecordKeyPair}, - {ID: "poison_key.pub", Purpose: PurposePoisonRecordKeyPair}, - {ID: "poison_key_sym", Purpose: PurposePoisonRecordSymmetricKey}, - {ID: "cossack-hmac_hmac", Purpose: PurposeSearchHMAC, ClientID: []byte(hmacEncID)}, - {ID: "cossack_storage", Purpose: PurposeStorageClientPrivateKey, ClientID: []byte(clientID)}, - {ID: "cossack_storage.pub", Purpose: PurposeStorageClientPublicKey, ClientID: []byte(clientID)}, - {ID: "cossack_storage_sym", Purpose: PurposeStorageClientSymmetricKey, ClientID: []byte(clientID)}, - {ID: "secure_log_key", Purpose: PurposeAuditLog}, - {ID: "sich_zone_sym", Purpose: PurposeStorageZoneSymmetricKey, ZoneID: []byte(zoneID)}, + {ID: "poison_key", Purpose: keystore.PurposePoisonRecordKeyPair}, + {ID: "poison_key.pub", Purpose: keystore.PurposePoisonRecordKeyPair}, + {ID: "poison_key_sym", Purpose: keystore.PurposePoisonRecordSymmetricKey}, + {ID: "cossack-hmac_hmac", Purpose: keystore.PurposeSearchHMAC, ClientID: []byte(hmacEncID)}, + {ID: "cossack_storage", Purpose: keystore.PurposeStorageClientPrivateKey, ClientID: []byte(clientID)}, + {ID: "cossack_storage.pub", Purpose: keystore.PurposeStorageClientPublicKey, ClientID: []byte(clientID)}, + {ID: "cossack_storage_sym", Purpose: keystore.PurposeStorageClientSymmetricKey, ClientID: []byte(clientID)}, + {ID: "secure_log_key", Purpose: keystore.PurposeAuditLog}, + {ID: "sich_zone_sym", Purpose: keystore.PurposeStorageZoneSymmetricKey, ZoneID: []byte(zoneID)}, } // sort to compare consistently sort.Slice(expectedKeys, func(i, j int) bool { diff --git a/keystore/filesystem/translator_keystore.go b/keystore/filesystem/translator_keystore.go index 023fa1955..1016a06f6 100644 --- a/keystore/filesystem/translator_keystore.go +++ b/keystore/filesystem/translator_keystore.go @@ -17,6 +17,7 @@ limitations under the License. package filesystem import ( + "context" "path/filepath" "github.com/cossacklabs/acra/keystore" @@ -111,7 +112,9 @@ func (store *TranslatorFileSystemKeyStore) GetPrivateKey(id []byte) (*keys.Priva } var privateKey []byte - if privateKey, err = store.encryptor.Decrypt(keyData, id); err != nil { + + keyContext := keystore.NewKeyContext(keystore.PurposeLegacy).WithContext(id) + if privateKey, err = store.encryptor.Decrypt(context.Background(), keyData, *keyContext); err != nil { return nil, err } return &keys.PrivateKey{Value: privateKey}, nil diff --git a/keystore/keystore.go b/keystore/keystore.go index 6aa5809a0..03dd12c90 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -20,6 +20,7 @@ limitations under the License. package keystore import ( + "context" "crypto/rand" "encoding/base64" "errors" @@ -45,6 +46,30 @@ const ( NoKeyFoundExit = true ) +// KeyPurpose describe usage of specific key +type KeyPurpose string + +func (p KeyPurpose) String() string { + return string(p) +} + +// Supported key purposes +const ( + PurposeSearchHMAC KeyPurpose = "search_hmac" + PurposeAuditLog KeyPurpose = "audit_log" + PurposePoisonRecordSymmetricKey KeyPurpose = "poison_sym_key" + PurposeStorageClientSymmetricKey KeyPurpose = "storage_sym_key" + PurposeStorageZoneSymmetricKey KeyPurpose = "zone_sym_key" + PurposePoisonRecordKeyPair KeyPurpose = "poison_key" + PurposeStorageClientKeyPair KeyPurpose = "storage" + PurposeStorageClientPublicKey KeyPurpose = "public_storage" + PurposeStorageClientPrivateKey KeyPurpose = "private_storage" + PurposeStorageZoneKeyPair KeyPurpose = "zone" + PurposeStorageZonePrivateKey KeyPurpose = "private_zone" + PurposeStorageZonePublicKey KeyPurpose = "public_zone" + PurposeLegacy KeyPurpose = "legacy" +) + // Errors returned during accessing to client id or master key. var ( ErrInvalidClientID = errors.New("invalid client ID") @@ -185,10 +210,48 @@ func GetMasterKeyFromEnvironmentVariable(varname string) ([]byte, error) { return key, nil } +// KeyContext contains generic key context for key operation +type KeyContext struct { + ClientID []byte + ZoneID []byte + Context []byte + Purpose KeyPurpose +} + +// NewEmptyKeyContext create new empty key context +func NewEmptyKeyContext() *KeyContext { + return &KeyContext{} +} + +// NewKeyContext create new key context with key purpose +func NewKeyContext(purpose KeyPurpose) *KeyContext { + return &KeyContext{ + Purpose: purpose, + } +} + +// WithZoneID set zoneID to key context +func (k *KeyContext) WithZoneID(zoneID []byte) *KeyContext { + k.ZoneID = zoneID + return k +} + +// WithClientID set clientID to key context +func (k *KeyContext) WithClientID(clientID []byte) *KeyContext { + k.ClientID = clientID + return k +} + +// WithContext set encryption context to key context +func (k *KeyContext) WithContext(ctx []byte) *KeyContext { + k.Context = ctx + return k +} + // KeyEncryptor describes Encrypt and Decrypt interfaces. type KeyEncryptor interface { - Encrypt(key, context []byte) ([]byte, error) - Decrypt(key, context []byte) ([]byte, error) + Encrypt(ctx context.Context, key []byte, keyContext KeyContext) ([]byte, error) + Decrypt(ctx context.Context, key []byte, keyContext KeyContext) ([]byte, error) } // SCellKeyEncryptor uses Themis Secure Cell with provided master key to encrypt and decrypt keys. @@ -202,14 +265,14 @@ func NewSCellKeyEncryptor(masterKey []byte) (*SCellKeyEncryptor, error) { } // Encrypt return encrypted key using masterKey and context. -func (encryptor *SCellKeyEncryptor) Encrypt(key, context []byte) ([]byte, error) { - encrypted, _, err := encryptor.scell.Protect(key, context) +func (encryptor *SCellKeyEncryptor) Encrypt(ctx context.Context, key []byte, keyContext KeyContext) ([]byte, error) { + encrypted, _, err := encryptor.scell.Protect(key, keyContext.Context) return encrypted, err } // Decrypt return decrypted key using masterKey and context. -func (encryptor *SCellKeyEncryptor) Decrypt(key, context []byte) ([]byte, error) { - return encryptor.scell.Unprotect(key, nil, context) +func (encryptor *SCellKeyEncryptor) Decrypt(ctx context.Context, key []byte, keyContext KeyContext) ([]byte, error) { + return encryptor.scell.Unprotect(key, nil, keyContext.Context) } // TransportKeyStore provides access to transport keys. It is used by acra-connector tool. @@ -326,7 +389,7 @@ type ServerKeyStore interface { // "ClientID" and "ZoneID" are filled in where relevant. type KeyDescription struct { ID string - Purpose string + Purpose KeyPurpose ClientID []byte `json:",omitempty"` ZoneID []byte `json:",omitempty"` } diff --git a/keystore/kms/key_encryptor.go b/keystore/kms/key_encryptor.go new file mode 100644 index 000000000..38c56368f --- /dev/null +++ b/keystore/kms/key_encryptor.go @@ -0,0 +1,70 @@ +package kms + +import ( + "context" + "errors" + "github.com/cossacklabs/acra/keystore" +) + +// KmsEncryptor errors +var ( + ErrMissingKeyPurpose = errors.New("key purpose is required for keyID creating") + ErrUnsupportedKeyPurpose = errors.New("unsupported KeyPurpose option provided") + ErrEmptyClientIDProvided = errors.New("empty clientID in key context") + ErrEmptyZoneIDProvided = errors.New("empty zoneID in key context") +) + +// KeyEncryptor implementation of KMS keystore.KeyEncryptor +type KeyEncryptor struct { + kmsEncryptor Encryptor +} + +// NewKeyEncryptor create new KeyEncryptor +func NewKeyEncryptor(kmsEncryptor Encryptor) *KeyEncryptor { + return &KeyEncryptor{ + kmsEncryptor, + } +} + +// Encrypt return encrypted key using KMS encryptor and context. +func (encryptor *KeyEncryptor) Encrypt(ctx context.Context, key []byte, keyContext keystore.KeyContext) ([]byte, error) { + keyID, err := getKeyIDFromContext(keyContext) + if err != nil { + // TODO: add logging + return nil, err + } + return encryptor.kmsEncryptor.Encrypt(ctx, keyID, key, nil) +} + +// Decrypt return decrypted key using KMS encryptor and context. +func (encryptor *KeyEncryptor) Decrypt(ctx context.Context, key []byte, keyContext keystore.KeyContext) ([]byte, error) { + keyID, err := getKeyIDFromContext(keyContext) + if err != nil { + // TODO: add logging + return nil, err + } + return encryptor.kmsEncryptor.Encrypt(ctx, keyID, key, nil) +} + +func getKeyIDFromContext(ctx keystore.KeyContext) ([]byte, error) { + if ctx.Purpose == "" { + return nil, ErrMissingKeyPurpose + } + + switch ctx.Purpose { + case keystore.PurposeStorageClientSymmetricKey, keystore.PurposeStorageClientPrivateKey, keystore.PurposeSearchHMAC: + if ctx.ClientID == nil { + return nil, ErrEmptyClientIDProvided + } + return []byte(string(ctx.ClientID) + "/" + ctx.Purpose.String()), nil + case keystore.PurposeStorageZoneSymmetricKey, keystore.PurposeStorageZonePrivateKey: + if ctx.ZoneID == nil { + return nil, ErrEmptyZoneIDProvided + } + return []byte(string(ctx.ClientID) + "/" + ctx.Purpose.String()), nil + case keystore.PurposeAuditLog, keystore.PurposePoisonRecordSymmetricKey, keystore.PurposePoisonRecordKeyPair: + return []byte("acra/common/" + ctx.Purpose.String()), nil + default: + return nil, ErrUnsupportedKeyPurpose + } +} diff --git a/keystore/kms/key_making_wrapper.go b/keystore/kms/key_making_wrapper.go new file mode 100644 index 000000000..eb65c4d98 --- /dev/null +++ b/keystore/kms/key_making_wrapper.go @@ -0,0 +1,115 @@ +package kms + +import ( + "context" + "github.com/cossacklabs/acra/keystore" + "github.com/cossacklabs/acra/network" + log "github.com/sirupsen/logrus" +) + +// KeyMakingWrapper wrap keystore.KeyMaking implementation with KMS key creation at start +type KeyMakingWrapper struct { + keystore.KeyMaking + kmsKeyManager KeyManager +} + +// NewKeyMakingWrapper create new KeyMakingWrapper +func NewKeyMakingWrapper(keyMaking keystore.KeyMaking, manager KeyManager) KeyMakingWrapper { + return KeyMakingWrapper{ + KeyMaking: keyMaking, + kmsKeyManager: manager, + } +} + +// GenerateDataEncryptionKeys wrap GenerateDataEncryptionKeys with KMS key creation at start +func (k KeyMakingWrapper) GenerateDataEncryptionKeys(clientID []byte) error { + err := k.createKMSKeyFromContext(keystore.KeyContext{ + ClientID: clientID, + Purpose: keystore.PurposeStorageClientPrivateKey, + }) + if err != nil { + return err + } + + return k.KeyMaking.GenerateDataEncryptionKeys(clientID) +} + +// GeneratePoisonSymmetricKey wrap GeneratePoisonSymmetricKey with KMS key creation at start +func (k KeyMakingWrapper) GeneratePoisonSymmetricKey() error { + err := k.createKMSKeyFromContext(keystore.KeyContext{ + Purpose: keystore.PurposePoisonRecordSymmetricKey, + }) + if err != nil { + return err + } + + return k.KeyMaking.GeneratePoisonSymmetricKey() +} + +// GeneratePoisonKeyPair wrap GeneratePoisonKeyPair with KMS key creation at start +func (k KeyMakingWrapper) GeneratePoisonKeyPair() error { + err := k.createKMSKeyFromContext(keystore.KeyContext{ + Purpose: keystore.PurposePoisonRecordKeyPair, + }) + if err != nil { + return err + } + + return k.KeyMaking.GeneratePoisonKeyPair() +} + +// GenerateLogKey wrap GenerateLogKey with KMS key creation at start +func (k KeyMakingWrapper) GenerateLogKey() error { + err := k.createKMSKeyFromContext(keystore.KeyContext{ + Purpose: keystore.PurposeAuditLog, + }) + if err != nil { + return err + } + + return k.KeyMaking.GenerateLogKey() +} + +// GenerateHmacKey wrap GenerateHmacKey with KMS key creation at start +func (k KeyMakingWrapper) GenerateHmacKey(clientID []byte) error { + err := k.createKMSKeyFromContext(keystore.KeyContext{ + ClientID: clientID, + Purpose: keystore.PurposeSearchHMAC, + }) + if err != nil { + return err + } + + return k.KeyMaking.GenerateHmacKey(clientID) +} + +// GenerateClientIDSymmetricKey wrap GenerateClientIDSymmetricKey with KMS key creation at start +func (k KeyMakingWrapper) GenerateClientIDSymmetricKey(id []byte) error { + err := k.createKMSKeyFromContext(keystore.KeyContext{ + ClientID: id, + Purpose: keystore.PurposeStorageClientSymmetricKey, + }) + if err != nil { + return err + } + + return k.KeyMaking.GenerateClientIDSymmetricKey(id) +} + +func (k KeyMakingWrapper) createKMSKeyFromContext(keyContext keystore.KeyContext) error { + ctx, _ := context.WithTimeout(context.Background(), network.DefaultNetworkTimeout) + + keyID, err := getKeyIDFromContext(keyContext) + if err != nil { + return err + } + + resp, err := k.kmsKeyManager.CreateKey(ctx, CreateKeyMetadata{ + KeyName: string(keyID), + }) + if err != nil { + return err + } + log.WithField("purpose", keyContext.Purpose).WithField("keyID", resp.KeyID).Info("KMS key created") + return nil +} diff --git a/keystore/v2/keystore/filesystem/export.go b/keystore/v2/keystore/filesystem/export.go index 4bc35bd46..bd2d9b7ca 100644 --- a/keystore/v2/keystore/filesystem/export.go +++ b/keystore/v2/keystore/filesystem/export.go @@ -17,7 +17,9 @@ package filesystem import ( + "context" "errors" + keystoreV1 "github.com/cossacklabs/acra/keystore" "time" "github.com/cossacklabs/acra/keystore/v2/keystore/api" @@ -121,7 +123,8 @@ func (s *KeyStore) encryptAndSignKeyRings(rings []asn1.KeyRing, cryptosuite *cry } defer utils.ZeroizeBytes(keysBytes) - encryptedKeyBytes, err := cryptosuite.KeyEncryptor.Encrypt(keysBytes, exportKeyContext) + keyContext := keystoreV1.NewEmptyKeyContext().WithContext(exportKeyContext) + encryptedKeyBytes, err := cryptosuite.KeyEncryptor.Encrypt(context.Background(), keysBytes, *keyContext) if err != nil { return nil, err } @@ -159,7 +162,8 @@ func (s *KeyStore) decryptAndVerifyKeyRings(ringData []byte, cryptosuite *crypto return nil, errUnsupportedVersion } - decryptedKeyBytes, err := cryptosuite.KeyEncryptor.Decrypt(container.Payload.Data.Bytes, exportKeyContext) + keyContext := keystoreV1.NewEmptyKeyContext().WithContext(exportKeyContext) + decryptedKeyBytes, err := cryptosuite.KeyEncryptor.Decrypt(context.Background(), container.Payload.Data.Bytes, *keyContext) if err != nil { return nil, err } diff --git a/keystore/v2/keystore/filesystem/keyStore.go b/keystore/v2/keystore/filesystem/keyStore.go index 7354bfc9a..39694242f 100644 --- a/keystore/v2/keystore/filesystem/keyStore.go +++ b/keystore/v2/keystore/filesystem/keyStore.go @@ -17,6 +17,7 @@ package filesystem import ( + "context" "errors" "github.com/cossacklabs/acra/cmd" "github.com/go-redis/redis/v7" @@ -257,12 +258,14 @@ func (s *KeyStore) keyStoreContext(context []byte) []byte { return c } -func (s *KeyStore) encrypt(data, context []byte) ([]byte, error) { - return s.encryptor.Encrypt(data, s.keyStoreContext(context)) +func (s *KeyStore) encrypt(data, ctx []byte) ([]byte, error) { + keyContext := keystoreV1.NewEmptyKeyContext().WithContext(s.keyStoreContext(ctx)) + return s.encryptor.Encrypt(context.Background(), data, *keyContext) } -func (s *KeyStore) decrypt(data, context []byte) ([]byte, error) { - return s.encryptor.Decrypt(data, s.keyStoreContext(context)) +func (s *KeyStore) decrypt(data, ctx []byte) ([]byte, error) { + keyContext := keystoreV1.NewEmptyKeyContext().WithContext(s.keyStoreContext(ctx)) + return s.encryptor.Decrypt(context.Background(), data, *keyContext) } func (s *KeyStore) keyRingSignatureContext(path string) []byte { diff --git a/keystore/v2/keystore/importV1.go b/keystore/v2/keystore/importV1.go index 8ebea14a7..dd6ed2fbd 100644 --- a/keystore/v2/keystore/importV1.go +++ b/keystore/v2/keystore/importV1.go @@ -18,6 +18,7 @@ package keystore import ( "errors" + "github.com/cossacklabs/acra/keystore" filesystemV1 "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/utils" @@ -37,7 +38,7 @@ type KeyFileImportV1 interface { func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key filesystemV1.ExportedKey) error { log := s.log.WithField("purpose", key.Purpose).WithField("id", key.ID) switch key.Purpose { - case filesystemV1.PurposePoisonRecordKeyPair: + case keystore.PurposePoisonRecordKeyPair: keypair, err := oldKeyStore.ExportKeyPair(key) if err != nil { log.WithError(err).Debug("failed to export poison record key pair") @@ -49,7 +50,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key log.WithError(err).Debug("failed to import poison record key pair") return err } - case filesystemV1.PurposeStorageClientKeyPair: + case keystore.PurposeStorageClientKeyPair: keypair, err := oldKeyStore.ExportKeyPair(key) if err != nil { log.WithError(err).Debug("failed to export client storage key pair") @@ -61,7 +62,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key log.WithError(err).Debug("failed to import client storage key pair") return err } - case filesystemV1.PurposeStorageZoneKeyPair: + case keystore.PurposeStorageZoneKeyPair: keypair, err := oldKeyStore.ExportKeyPair(key) if err != nil { log.WithError(err).Debug("failed to export zone storage key pair") @@ -73,7 +74,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key log.WithError(err).Debug("failed to import zone storage key pair") return err } - case filesystemV1.PurposeAuditLog: + case keystore.PurposeAuditLog: symkey, err := oldKeyStore.ExportSymmetricKey(key) if err != nil { log.WithError(err).Debug("Failed to export audit log key") @@ -85,7 +86,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key log.WithError(err).Debug("Failed to import audit log key") return err } - case filesystemV1.PurposeSearchHMAC: + case keystore.PurposeSearchHMAC: symkey, err := oldKeyStore.ExportSymmetricKey(key) if err != nil { log.WithError(err).Debug("Failed to export search HMAC key") @@ -98,7 +99,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key return err } - case filesystemV1.PurposePoisonRecordSymmetricKey: + case keystore.PurposePoisonRecordSymmetricKey: symkey, err := oldKeyStore.ExportSymmetricKey(key) if err != nil { log.WithError(err).Debug("Failed to export poison record symmetric key") @@ -111,7 +112,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key return err } - case filesystemV1.PurposeStorageClientSymmetricKey: + case keystore.PurposeStorageClientSymmetricKey: symkey, err := oldKeyStore.ExportSymmetricKey(key) if err != nil { log.WithError(err).Debug("Failed to export client storage symmetric key") @@ -124,7 +125,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key return err } - case filesystemV1.PurposeStorageZoneSymmetricKey: + case keystore.PurposeStorageZoneSymmetricKey: symkey, err := oldKeyStore.ExportSymmetricKey(key) if err != nil { log.WithError(err).Debug("Failed to export zone storage symmetric key") From 712f7dd433cd2004c6861075fe51c7d59274857a Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Mon, 1 Aug 2022 15:47:42 +0200 Subject: [PATCH 02/13] zhars/use_kms_per_client Fixed conflicts with master --- cmd/acra-keymaker/acra-keymaker.go | 16 +++--- keystore/filesystem/key_export.go | 2 +- keystore/filesystem/server_keystore.go | 64 ++++++++++----------- keystore/filesystem/server_keystore_test.go | 7 ++- keystore/kms/key_encryptor.go | 7 ++- keystore/kms/key_making_wrapper.go | 1 + 6 files changed, 51 insertions(+), 46 deletions(-) diff --git a/cmd/acra-keymaker/acra-keymaker.go b/cmd/acra-keymaker/acra-keymaker.go index a07d220d5..f08137efa 100644 --- a/cmd/acra-keymaker/acra-keymaker.go +++ b/cmd/acra-keymaker/acra-keymaker.go @@ -198,11 +198,6 @@ func main() { switch *keystoreVersion { case "v1": store = openKeyStoreV1(*outputDir, *outputPublicKey, keyLoader) - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { - keyManager, _ := kmsOptions.NewKeyManager() - store = baseKMS.NewKeyMakingWrapper(store, keyManager) - } - case "v2": store = openKeyStoreV2(*outputDir, keyLoader) case "": @@ -293,7 +288,7 @@ func main() { } } -func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoader) keystore.KeyMaking { +func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoader) (keyMaking keystore.KeyMaking) { var keyStoreEncryptor keystore.KeyEncryptor if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { keyManager, err := kmsOptions.NewKeyManager() @@ -333,12 +328,17 @@ func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoade } keyStore.Storage(keyStorage) } - keyStoreV1, err := keyStore.Build() + keyMaking, err := keyStore.Build() if err != nil { log.WithError(err).Errorln("Can't init keystore") os.Exit(1) } - return keyStoreV1 + + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { + keyManager, _ := kmsOptions.NewKeyManager() + keyMaking = baseKMS.NewKeyMakingWrapper(keyMaking, keyManager) + } + return } func openKeyStoreV2(keyDirPath string, loader keyloader.MasterKeyLoader) keystore.KeyMaking { diff --git a/keystore/filesystem/key_export.go b/keystore/filesystem/key_export.go index 48381bdf3..9db2329b8 100644 --- a/keystore/filesystem/key_export.go +++ b/keystore/filesystem/key_export.go @@ -18,10 +18,10 @@ package filesystem import ( "context" - "github.com/cossacklabs/acra/keystore" "path/filepath" "strings" + "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/utils" "github.com/cossacklabs/themis/gothemis/keys" ) diff --git a/keystore/filesystem/server_keystore.go b/keystore/filesystem/server_keystore.go index 7b48e53b7..1369da1e9 100644 --- a/keystore/filesystem/server_keystore.go +++ b/keystore/filesystem/server_keystore.go @@ -25,6 +25,7 @@ limitations under the License. package filesystem import ( + "context" "crypto/rand" "errors" "fmt" @@ -39,6 +40,7 @@ import ( fs "github.com/cossacklabs/acra/keystore/filesystem/internal" "github.com/cossacklabs/acra/keystore/lru" "github.com/cossacklabs/acra/logging" + "github.com/cossacklabs/acra/network" "github.com/cossacklabs/acra/utils" "github.com/cossacklabs/acra/zone" "github.com/cossacklabs/themis/gothemis/keys" @@ -343,7 +345,7 @@ func (store *KeyStore) SaveKeyPairWithFilename(keypair *keys.Keypair, filename s return err } - cacheEncryptedPrivate, err := store.cacheEncryptor.Encrypt(keypair.Private.Value, id) + cacheEncryptedPrivate, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, keypair.Private.Value, keyContext) if err != nil { return err } @@ -447,7 +449,7 @@ func (store *KeyStore) generateZoneKey(id []byte) ([]byte, []byte, error) { } store.lock.Lock() defer store.lock.Unlock() - cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(keypair.Private.Value, id) + cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, keypair.Private.Value, *keyContext) if err != nil { return nil, nil, nil } @@ -589,14 +591,14 @@ func (store *KeyStore) getPrivateKeyByFilename(filename string, keyContext keyst return encryptedPrivateKey.Value, nil } - loadedKey, err := store.loadKeyAndCache(id, filename, loadKeyCallback) + loadedKey, err := store.loadKeyAndCache(filename, keyContext, loadKeyCallback) if err != nil { return nil, err } return &keys.PrivateKey{Value: loadedKey}, nil } - decryptedKey, err := store.cacheEncryptor.Decrypt(encryptedKey, id) + decryptedKey, err := store.cacheEncryptor.Decrypt(store.encryptorCtx, encryptedKey, keyContext) if err != nil { return nil, err } @@ -687,7 +689,8 @@ func (store *KeyStore) GetZonePrivateKeys(id []byte) ([]*keys.PrivateKey, error) if err != nil { return nil, err } - return store.getPrivateKeysByFilenames(id, filenames) + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZonePrivateKey).WithZoneID(id).WithContext(id) + return store.getPrivateKeysByFilenames(filenames, *keyContext) } // GetPeerPublicKey returns public key for this clientID, gets it from cache or reads from fs. @@ -970,14 +973,14 @@ func (store *KeyStore) DescribeKeyFile(fileInfo os.FileInfo) (*keystore.KeyDescr if penultimateKeyPart == "log" && lastKeyPart == "key" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeAuditLog, + Purpose: keystore.PurposeAuditLog, }, nil } if lastKeyPart == "server" || lastKeyPart == "server.pub" || lastKeyPart == "translator" || lastKeyPart == "translator.pub" { return &keystore.KeyDescription{ ID: fileInfo.Name(), - Purpose: PurposeLegacy, + Purpose: keystore.PurposeLegacy, ClientID: []byte(components[0]), }, nil } @@ -999,7 +1002,7 @@ func (store *KeyStore) GetPoisonKeyPair() (*keys.Keypair, error) { privateKey, privateOk := store.cache.Get(PoisonKeyFilename) publicKey, publicOk := store.cache.Get(poisonKeyFilenamePublic) if privateOk && publicOk { - decryptedPrivate, err := store.cacheEncryptor.Decrypt(privateKey, *keyContext) + decryptedPrivate, err := store.cacheEncryptor.Decrypt(store.encryptorCtx, privateKey, *keyContext) if err != nil { return nil, err } @@ -1013,11 +1016,11 @@ func (store *KeyStore) GetPoisonKeyPair() (*keys.Keypair, error) { } return nil, err } - if private.Value, err = store.encryptor.Decrypt(private.Value, *keyContext); err != nil { + if private.Value, err = store.encryptor.Decrypt(store.encryptorCtx, private.Value, *keyContext); err != nil { return nil, err } - cacheEncrypted, err := store.cacheEncryptor.Encrypt(private.Value, []byte(PoisonKeyFilename)) + cacheEncrypted, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, private.Value, *keyContext) if err != nil { return nil, err } @@ -1180,22 +1183,15 @@ func (store *KeyStore) Get(keyID string) ([]byte, bool) { // GetHMACSecretKey return key for hmac calculation according to id func (store *KeyStore) GetHMACSecretKey(id []byte) ([]byte, error) { filename := getHmacKeyFilename(id) + keyContext := keystore.NewKeyContext(keystore.PurposeSearchHMAC).WithClientID(id).WithContext(id) + encryptedKey, ok := store.Get(filename) if !ok { - return store.loadKeyAndCache(id, filename, func() ([]byte, error) { + return store.loadKeyAndCache(filename, *keyContext, func() ([]byte, error) { return store.ReadKeyFile(store.GetPrivateKeyFilePath(filename)) }) } - return store.cacheEncryptor.Decrypt(encryptedKey, id) - - keyContext := keystore.NewKeyContext(keystore.PurposeSearchHMAC).WithClientID(id).WithContext(id) - decryptedKey, err := store.encryptor.Decrypt(store.encryptorCtx, encryptedKey, *keyContext) - if err != nil { - return nil, err - } - log.Debugf("Load key from fs: %s", filename) - store.Add(filename, encryptedKey) - return decryptedKey, nil + return store.cacheEncryptor.Decrypt(store.encryptorCtx, encryptedKey, *keyContext) } // GenerateHmacKey key for hmac calculation in in folder for private keys @@ -1212,7 +1208,7 @@ func (store *KeyStore) GenerateHmacKey(id []byte) error { return err } - cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(key, id) + cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, key, *keyContext) if err != nil { return err } @@ -1244,7 +1240,7 @@ func (store *KeyStore) GenerateLogKey() error { return err } - cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(key, []byte(SecureLogKeyFilename)) + cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, key, *keyContext) if err != nil { return err } @@ -1265,14 +1261,16 @@ func (store *KeyStore) GenerateLogKey() error { // GetLogSecretKey return key for log integrity checks func (store *KeyStore) GetLogSecretKey() ([]byte, error) { filename := getLogKeyFilename() + keyContext := keystore.NewKeyContext(keystore.PurposeAuditLog).WithContext([]byte(SecureLogKeyFilename)) + encryptedKey, ok := store.Get(filename) if !ok { - return store.loadKeyAndCache([]byte(SecureLogKeyFilename), filename, func() ([]byte, error) { + return store.loadKeyAndCache(filename, *keyContext, func() ([]byte, error) { return store.ReadKeyFile(store.GetPrivateKeyFilePath(filename)) }) } - return store.cacheEncryptor.Decrypt(encryptedKey, []byte(SecureLogKeyFilename)) + return store.cacheEncryptor.Decrypt(store.encryptorCtx, encryptedKey, *keyContext) } // generateSymmetricKey generate symmetric key with specific identifier @@ -1293,26 +1291,26 @@ func (store *KeyStore) generateAndSaveSymmetricKey(filename string, keyContext k func (store *KeyStore) readEncryptedKey(filename string, keyContext keystore.KeyContext) ([]byte, error) { encryptedSymKey, ok := store.Get(filename) if !ok { - return store.loadKeyAndCache(id, filename, func() ([]byte, error) { + return store.loadKeyAndCache(filename, keyContext, func() ([]byte, error) { return store.ReadKeyFile(store.GetPrivateKeyFilePath(filename)) }) } - return store.cacheEncryptor.Decrypt(encryptedSymKey, id) + return store.cacheEncryptor.Decrypt(store.encryptorCtx, encryptedSymKey, keyContext) } -func (store *KeyStore) loadKeyAndCache(id []byte, filename string, loadKeyCallback func() ([]byte, error)) ([]byte, error) { +func (store *KeyStore) loadKeyAndCache(filename string, keyContext keystore.KeyContext, loadKeyCallback func() ([]byte, error)) ([]byte, error) { encryptedKey, err := loadKeyCallback() if err != nil { return nil, err } - decrypted, err := store.encryptor.Decrypt(encryptedKey, id) + decrypted, err := store.encryptor.Decrypt(store.encryptorCtx, encryptedKey, keyContext) if err != nil { return nil, err } - cacheEncrypted, err := store.cacheEncryptor.Encrypt(decrypted, id) + cacheEncrypted, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, decrypted, keyContext) if err != nil { - log.WithError(err).WithField("id", id).Debugln("Failed to encrypt with cacheEncryptor") + log.WithError(err).WithField("id", keyContext.Context).Debugln("Failed to encrypt with cacheEncryptor") return nil, err } @@ -1324,7 +1322,9 @@ func (store *KeyStore) loadKeyAndCache(id []byte, filename string, loadKeyCallba // GenerateClientIDSymmetricKey generate symmetric key for specified client id func (store *KeyStore) GenerateClientIDSymmetricKey(id []byte) error { keyName := getClientIDSymmetricKeyName(id) - return store.generateAndSaveSymmetricKey(id, store.GetPrivateKeyFilePath(keyName)) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithClientID(id).WithContext(id) + return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), *keyContext) } // GenerateZoneIDSymmetricKey generate symmetric key for specified zone id diff --git a/keystore/filesystem/server_keystore_test.go b/keystore/filesystem/server_keystore_test.go index 2cdbd015a..2c54596f6 100644 --- a/keystore/filesystem/server_keystore_test.go +++ b/keystore/filesystem/server_keystore_test.go @@ -18,6 +18,7 @@ package filesystem import ( "bytes" + "context" "fmt" "io/ioutil" "log" @@ -619,7 +620,7 @@ func testFilesystemKeyStoreSymmetricWithCache(storage Storage, t *testing.T) { } keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithContext(testID2) - decrypted, err := store.cacheEncryptor.Decrypt(value, testID2) + decrypted, err := store.cacheEncryptor.Decrypt(context.Background(), value, *keyContext) if err != nil { t.Fatal(err) } @@ -698,7 +699,9 @@ func testFilesystemKeyStoreWithCache(storage Storage, t *testing.T) { if !ok { t.Fatal("Expected key in result") } - decrypted, err := store.cacheEncryptor.Decrypt(value, testID2) + + keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithContext(testID2) + decrypted, err := store.cacheEncryptor.Decrypt(context.Background(), value, *keyContext) if err != nil { t.Fatal(err) } diff --git a/keystore/kms/key_encryptor.go b/keystore/kms/key_encryptor.go index 38c56368f..50aabf341 100644 --- a/keystore/kms/key_encryptor.go +++ b/keystore/kms/key_encryptor.go @@ -4,6 +4,7 @@ import ( "context" "errors" "github.com/cossacklabs/acra/keystore" + log "github.com/sirupsen/logrus" ) // KmsEncryptor errors @@ -30,7 +31,7 @@ func NewKeyEncryptor(kmsEncryptor Encryptor) *KeyEncryptor { func (encryptor *KeyEncryptor) Encrypt(ctx context.Context, key []byte, keyContext keystore.KeyContext) ([]byte, error) { keyID, err := getKeyIDFromContext(keyContext) if err != nil { - // TODO: add logging + log.WithError(err).Errorln("Failed to obtain keyID from keyContext") return nil, err } return encryptor.kmsEncryptor.Encrypt(ctx, keyID, key, nil) @@ -40,10 +41,10 @@ func (encryptor *KeyEncryptor) Encrypt(ctx context.Context, key []byte, keyConte func (encryptor *KeyEncryptor) Decrypt(ctx context.Context, key []byte, keyContext keystore.KeyContext) ([]byte, error) { keyID, err := getKeyIDFromContext(keyContext) if err != nil { - // TODO: add logging + log.WithError(err).Errorln("Failed to obtain keyID from keyContext") return nil, err } - return encryptor.kmsEncryptor.Encrypt(ctx, keyID, key, nil) + return encryptor.kmsEncryptor.Decrypt(ctx, keyID, key, nil) } func getKeyIDFromContext(ctx keystore.KeyContext) ([]byte, error) { diff --git a/keystore/kms/key_making_wrapper.go b/keystore/kms/key_making_wrapper.go index eb65c4d98..356b7f04b 100644 --- a/keystore/kms/key_making_wrapper.go +++ b/keystore/kms/key_making_wrapper.go @@ -2,6 +2,7 @@ package kms import ( "context" + "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/network" log "github.com/sirupsen/logrus" From c053fa5a22eb15e3156bd3935a400cf714bbf2a1 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Mon, 1 Aug 2022 17:44:40 +0200 Subject: [PATCH 03/13] zhars/use_kms_per_client Fixed after review --- keystore/filesystem/filesystem_backup.go | 2 +- keystore/keystore.go | 1 + keystore/kms/key_encryptor.go | 10 ++-- keystore/kms/key_making_wrapper.go | 61 ++++++++++++++++++------ 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/keystore/filesystem/filesystem_backup.go b/keystore/filesystem/filesystem_backup.go index 1e5fd57f0..4850080b7 100644 --- a/keystore/filesystem/filesystem_backup.go +++ b/keystore/filesystem/filesystem_backup.go @@ -123,7 +123,7 @@ func getIDFromFilename(fname string) (keystore.KeyPurpose, []byte) { return "", []byte(fname[:len(fname)-len("_sym")]) } - return "", []byte(fname) + return keystore.PurposeUndefined, []byte(fname) } type dummyEncryptor struct{} diff --git a/keystore/keystore.go b/keystore/keystore.go index 03dd12c90..d133fea8d 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -68,6 +68,7 @@ const ( PurposeStorageZonePrivateKey KeyPurpose = "private_zone" PurposeStorageZonePublicKey KeyPurpose = "public_zone" PurposeLegacy KeyPurpose = "legacy" + PurposeUndefined KeyPurpose = "undefined" ) // Errors returned during accessing to client id or master key. diff --git a/keystore/kms/key_encryptor.go b/keystore/kms/key_encryptor.go index 50aabf341..06f077a35 100644 --- a/keystore/kms/key_encryptor.go +++ b/keystore/kms/key_encryptor.go @@ -57,14 +57,16 @@ func getKeyIDFromContext(ctx keystore.KeyContext) ([]byte, error) { if ctx.ClientID == nil { return nil, ErrEmptyClientIDProvided } - return []byte(string(ctx.ClientID) + "/" + ctx.Purpose.String()), nil + return []byte("acra_" + string(ctx.ClientID)), nil case keystore.PurposeStorageZoneSymmetricKey, keystore.PurposeStorageZonePrivateKey: if ctx.ZoneID == nil { return nil, ErrEmptyZoneIDProvided } - return []byte(string(ctx.ClientID) + "/" + ctx.Purpose.String()), nil - case keystore.PurposeAuditLog, keystore.PurposePoisonRecordSymmetricKey, keystore.PurposePoisonRecordKeyPair: - return []byte("acra/common/" + ctx.Purpose.String()), nil + return []byte("acra_" + string(ctx.ZoneID)), nil + case keystore.PurposePoisonRecordSymmetricKey, keystore.PurposePoisonRecordKeyPair: + return []byte("acra_poison"), nil + case keystore.PurposeAuditLog: + return []byte("acra_audit_log"), nil default: return nil, ErrUnsupportedKeyPurpose } diff --git a/keystore/kms/key_making_wrapper.go b/keystore/kms/key_making_wrapper.go index 356b7f04b..0401e1e48 100644 --- a/keystore/kms/key_making_wrapper.go +++ b/keystore/kms/key_making_wrapper.go @@ -8,6 +8,14 @@ import ( log "github.com/sirupsen/logrus" ) +// KMS kek descriptions +const ( + AcraClientKeyDescription = "Acra client key encryption key, used for encryption/decryption AcraBlock symmetric key, AcraStruct private key and HMAC symmetric key" + AcraZoneKeyDescription = "Acra zone key encryption key, used for encryption/decryption Zone symmetric key and Zone private key" + AcraPoisonKeyDescription = "Acra common key encryption key, used for encryption/decryption poison symmetric/private keys" + AcraAuditLogKeyDescription = "Acra common key encryption key, used for encryption/decryption audit log key" +) + // KeyMakingWrapper wrap keystore.KeyMaking implementation with KMS key creation at start type KeyMakingWrapper struct { keystore.KeyMaking @@ -24,10 +32,12 @@ func NewKeyMakingWrapper(keyMaking keystore.KeyMaking, manager KeyManager) KeyMa // GenerateDataEncryptionKeys wrap GenerateDataEncryptionKeys with KMS key creation at start func (k KeyMakingWrapper) GenerateDataEncryptionKeys(clientID []byte) error { - err := k.createKMSKeyFromContext(keystore.KeyContext{ + ctx := keystore.KeyContext{ ClientID: clientID, Purpose: keystore.PurposeStorageClientPrivateKey, - }) + } + + err := k.createKMSKeyFromContext(ctx, AcraClientKeyDescription) if err != nil { return err } @@ -37,9 +47,11 @@ func (k KeyMakingWrapper) GenerateDataEncryptionKeys(clientID []byte) error { // GeneratePoisonSymmetricKey wrap GeneratePoisonSymmetricKey with KMS key creation at start func (k KeyMakingWrapper) GeneratePoisonSymmetricKey() error { - err := k.createKMSKeyFromContext(keystore.KeyContext{ + ctx := keystore.KeyContext{ Purpose: keystore.PurposePoisonRecordSymmetricKey, - }) + } + + err := k.createKMSKeyFromContext(ctx, AcraPoisonKeyDescription) if err != nil { return err } @@ -49,9 +61,11 @@ func (k KeyMakingWrapper) GeneratePoisonSymmetricKey() error { // GeneratePoisonKeyPair wrap GeneratePoisonKeyPair with KMS key creation at start func (k KeyMakingWrapper) GeneratePoisonKeyPair() error { - err := k.createKMSKeyFromContext(keystore.KeyContext{ + ctx := keystore.KeyContext{ Purpose: keystore.PurposePoisonRecordKeyPair, - }) + } + + err := k.createKMSKeyFromContext(ctx, AcraPoisonKeyDescription) if err != nil { return err } @@ -61,9 +75,11 @@ func (k KeyMakingWrapper) GeneratePoisonKeyPair() error { // GenerateLogKey wrap GenerateLogKey with KMS key creation at start func (k KeyMakingWrapper) GenerateLogKey() error { - err := k.createKMSKeyFromContext(keystore.KeyContext{ + ctx := keystore.KeyContext{ Purpose: keystore.PurposeAuditLog, - }) + } + + err := k.createKMSKeyFromContext(ctx, AcraAuditLogKeyDescription) if err != nil { return err } @@ -73,10 +89,12 @@ func (k KeyMakingWrapper) GenerateLogKey() error { // GenerateHmacKey wrap GenerateHmacKey with KMS key creation at start func (k KeyMakingWrapper) GenerateHmacKey(clientID []byte) error { - err := k.createKMSKeyFromContext(keystore.KeyContext{ + ctx := keystore.KeyContext{ ClientID: clientID, Purpose: keystore.PurposeSearchHMAC, - }) + } + + err := k.createKMSKeyFromContext(ctx, AcraClientKeyDescription) if err != nil { return err } @@ -86,10 +104,12 @@ func (k KeyMakingWrapper) GenerateHmacKey(clientID []byte) error { // GenerateClientIDSymmetricKey wrap GenerateClientIDSymmetricKey with KMS key creation at start func (k KeyMakingWrapper) GenerateClientIDSymmetricKey(id []byte) error { - err := k.createKMSKeyFromContext(keystore.KeyContext{ + ctx := keystore.KeyContext{ ClientID: id, Purpose: keystore.PurposeStorageClientSymmetricKey, - }) + } + + err := k.createKMSKeyFromContext(ctx, AcraClientKeyDescription) if err != nil { return err } @@ -97,7 +117,7 @@ func (k KeyMakingWrapper) GenerateClientIDSymmetricKey(id []byte) error { return k.KeyMaking.GenerateClientIDSymmetricKey(id) } -func (k KeyMakingWrapper) createKMSKeyFromContext(keyContext keystore.KeyContext) error { +func (k KeyMakingWrapper) createKMSKeyFromContext(keyContext keystore.KeyContext, description string) error { ctx, _ := context.WithTimeout(context.Background(), network.DefaultNetworkTimeout) keyID, err := getKeyIDFromContext(keyContext) @@ -105,12 +125,23 @@ func (k KeyMakingWrapper) createKMSKeyFromContext(keyContext keystore.KeyContext return err } + keyExist, err := k.kmsKeyManager.IsKeyExist(ctx, string(keyID)) + if err != nil { + return err + } + + if keyExist { + log.WithField("keyID", string(keyID)).Debugln("KMS key already exist") + return nil + } + resp, err := k.kmsKeyManager.CreateKey(ctx, CreateKeyMetadata{ - KeyName: string(keyID), + KeyName: string(keyID), + Description: description, }) if err != nil { return err } - log.WithField("purpose", keyContext.Purpose).WithField("keyID", resp.KeyID).Info("KMS key created") + log.WithField("keyID", resp.KeyID).WithField("key-name", string(keyID)).Info("KMS key created") return nil } From 2ad118dff2a71d32167c4e9e7d9677dbcb27f22a Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Mon, 1 Aug 2022 17:46:30 +0200 Subject: [PATCH 04/13] zhars/use_kms_per_client Fixed imports --- cmd/acra-keymaker/acra-keymaker.go | 2 -- cmd/acra-server/acra-server.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/acra-keymaker/acra-keymaker.go b/cmd/acra-keymaker/acra-keymaker.go index f08137efa..365cdc0c6 100644 --- a/cmd/acra-keymaker/acra-keymaker.go +++ b/cmd/acra-keymaker/acra-keymaker.go @@ -209,8 +209,6 @@ func main() { } if *poisonRecord { - //TODO: ask if we want to create KMS key per each poison key (private and symmetric) or just one - // Generate poison record symmetric key if err = store.GeneratePoisonSymmetricKey(); err != nil { panic(err) diff --git a/cmd/acra-server/acra-server.go b/cmd/acra-server/acra-server.go index 99eedfbaa..21c1485c4 100644 --- a/cmd/acra-server/acra-server.go +++ b/cmd/acra-server/acra-server.go @@ -37,7 +37,6 @@ import ( "errors" "flag" "fmt" - baseKMS "github.com/cossacklabs/acra/keystore/kms" "net/http" _ "net/http/pprof" "os" @@ -58,6 +57,7 @@ import ( "github.com/cossacklabs/acra/keystore/keyloader" "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" "github.com/cossacklabs/acra/keystore/keyloader/kms" + baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" filesystemBackendV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem/backend" From 53e5ac384a8404a64213dab5ae3c32a4bd113041 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Wed, 3 Aug 2022 17:15:37 +0200 Subject: [PATCH 05/13] zhars/use_kms_per_client Fixed after review/added integration tests/extend addzone with KMS KEK creating --- CHANGELOG_DEV.md | 4 + cmd/acra-addzone/acra-addzone.go | 54 +++++--- cmd/acra-keymaker/acra-keymaker.go | 25 ++-- cmd/acra-keys/keys/migrate-keys.go | 2 +- cmd/acra-server/acra-server.go | 2 +- configs/acra-addzone.yaml | 6 + configs/acra-backup.yaml | 6 + configs/acra-keymaker.yaml | 3 + configs/acra-keys.yaml | 6 + configs/acra-poisonrecordmaker.yaml | 6 + configs/acra-rollback.yaml | 6 + configs/acra-rotate.yaml | 6 + configs/acra-server.yaml | 6 + configs/acra-translator.yaml | 6 + keystore/filesystem/filesystem_backup.go | 39 +++--- keystore/filesystem/key_export.go | 73 ++++++----- keystore/filesystem/server_keystore.go | 130 ++++++++++---------- keystore/filesystem/server_keystore_test.go | 32 ++--- keystore/filesystem/translator_keystore.go | 4 +- keystore/keyloader/kms/kms_cli.go | 9 +- keystore/keystore.go | 53 +++++--- keystore/kms/key_encryptor.go | 2 +- keystore/kms/key_making_wrapper.go | 56 ++++++++- keystore/v2/keystore/filesystem/export.go | 8 +- keystore/v2/keystore/filesystem/keyStore.go | 8 +- keystore/v2/keystore/importV1.go | 16 +-- tests/test.py | 89 +++++++++++++- 27 files changed, 437 insertions(+), 220 deletions(-) diff --git a/CHANGELOG_DEV.md b/CHANGELOG_DEV.md index ed0a31131..aced7d645 100644 --- a/CHANGELOG_DEV.md +++ b/CHANGELOG_DEV.md @@ -1,3 +1,7 @@ +# 0.94.0 - 2022-08-03 +- Implement KMS Keystore encryptor +- Extend `acra-keymaker` and `acra-addzone` tools with ability to create key encryption keys on KMS + # 0.94.0 - 2022-07-08 - Extend encryptor config struct with database settings section - Has subsections for mysql-specific settings as well as for postgres-specific ones diff --git a/cmd/acra-addzone/acra-addzone.go b/cmd/acra-addzone/acra-addzone.go index 98a3b0345..4c4460eac 100644 --- a/cmd/acra-addzone/acra-addzone.go +++ b/cmd/acra-addzone/acra-addzone.go @@ -30,6 +30,7 @@ package main import ( "flag" "fmt" + baseKMS "github.com/cossacklabs/acra/keystore/kms" "os" "github.com/cossacklabs/acra/cmd" @@ -85,7 +86,7 @@ func main() { os.Exit(1) } - var keyStore keystore.StorageKeyGenerator + var keyStore keystore.KeyMaking if filesystemV2.IsKeyDirectory(*outputDir) { keyStore = openKeyStoreV2(*outputDir, keyLoader) } else { @@ -111,21 +112,32 @@ func main() { fmt.Println(string(json)) } -func openKeyStoreV1(output string, loader keyloader.MasterKeyLoader) keystore.StorageKeyGenerator { - masterKey, err := loader.LoadMasterKey() - if err != nil { - log.WithError(err).Errorln("Cannot load master key") - os.Exit(1) - } - scellEncryptor, err := keystore.NewSCellKeyEncryptor(masterKey) - if err != nil { - log.WithError(err).Errorln("Can't init scell encryptor") - os.Exit(1) +func openKeyStoreV1(output string, loader keyloader.MasterKeyLoader) keystore.KeyMaking { + var keyStoreEncryptor keystore.KeyEncryptor + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { + keyManager, err := kmsOptions.NewKeyManager() + if err != nil { + log.WithError(err).Errorln("Failed to initializer kms KeyManager") + os.Exit(1) + } + + keyStoreEncryptor = baseKMS.NewKeyEncryptor(keyManager) + } else { + masterKey, err := loader.LoadMasterKey() + if err != nil { + log.WithError(err).Errorln("Cannot load master key") + os.Exit(1) + } + keyStoreEncryptor, err = keystore.NewSCellKeyEncryptor(masterKey) + if err != nil { + log.WithError(err).Errorln("Can't init scell encryptor") + os.Exit(1) + } } - keyStore := filesystem.NewCustomFilesystemKeyStore() - keyStore.KeyDirectory(output) - keyStore.Encryptor(scellEncryptor) + keyStoreBuilder := filesystem.NewCustomFilesystemKeyStore() + keyStoreBuilder.KeyDirectory(output) + keyStoreBuilder.Encryptor(keyStoreEncryptor) redis := cmd.GetRedisParameters() if redis.KeysConfigured() { keyStorage, err := filesystem.NewRedisStorage(redis.HostPort, redis.Password, redis.DBKeys, nil) @@ -134,17 +146,23 @@ func openKeyStoreV1(output string, loader keyloader.MasterKeyLoader) keystore.St Errorln("Can't initialize Redis client") os.Exit(1) } - keyStore.Storage(keyStorage) + keyStoreBuilder.Storage(keyStorage) } - keyStoreV1, err := keyStore.Build() + keyStore, err := keyStoreBuilder.Build() if err != nil { log.WithError(err).Errorln("Can't init keystore") os.Exit(1) } - return keyStoreV1 + + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { + keyManager, _ := kmsOptions.NewKeyManager() + return baseKMS.NewKeyMakingWrapper(keyStore, keyManager) + } + + return keyStore } -func openKeyStoreV2(keyDirPath string, loader keyloader.MasterKeyLoader) keystore.StorageKeyGenerator { +func openKeyStoreV2(keyDirPath string, loader keyloader.MasterKeyLoader) keystore.KeyMaking { encryption, signature, err := loader.LoadMasterKeys() if err != nil { log.WithError(err).Errorln("Cannot load master key") diff --git a/cmd/acra-keymaker/acra-keymaker.go b/cmd/acra-keymaker/acra-keymaker.go index 365cdc0c6..dd3f05ac9 100644 --- a/cmd/acra-keymaker/acra-keymaker.go +++ b/cmd/acra-keymaker/acra-keymaker.go @@ -67,7 +67,6 @@ func main() { poisonRecord := flag.Bool("generate_poisonrecord_keys", false, "Generate keypair and symmetric key for poison records") cmd.RegisterRedisKeyStoreParameters() keystoreVersion := flag.String("keystore", "", "set keystore format: v1 (current), v2 (new)") - kmsKeyPolicy := flag.String("kms_key_policy", kms.KeyPolicyCreate, fmt.Sprintf("KMS usage key policy: <%s>", strings.Join(kms.SupportedPolicies, "|"))) tlsClientCert := flag.String("tls_cert", "", "Path to TLS certificate to use as client_id identifier") tlsIdentifierExtractorType := flag.String("tls_identifier_extractor_type", network.IdentifierExtractorTypeDistinguishedName, fmt.Sprintf("Decide which field of TLS certificate to use as ClientID (%s). Default is %s.", strings.Join(network.IdentifierExtractorTypesList, "|"), network.IdentifierExtractorTypeDistinguishedName)) @@ -158,7 +157,7 @@ func main() { os.Exit(1) } - switch *kmsKeyPolicy { + switch kmsOptions.KeyPolicy { case kms.KeyPolicyCreate: newKey, err = newMasterKeyWithKMSCreate(keyManager, newKey) if err != nil { @@ -286,9 +285,9 @@ func main() { } } -func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoader) (keyMaking keystore.KeyMaking) { +func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoader) keystore.KeyMaking { var keyStoreEncryptor keystore.KeyEncryptor - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { keyManager, err := kmsOptions.NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") @@ -309,13 +308,13 @@ func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoade } } - keyStore := filesystem.NewCustomFilesystemKeyStore() + keyStoreBuilder := filesystem.NewCustomFilesystemKeyStore() if outputPublic != output { - keyStore.KeyDirectories(output, outputPublic) + keyStoreBuilder.KeyDirectories(output, outputPublic) } else { - keyStore.KeyDirectory(output) + keyStoreBuilder.KeyDirectory(output) } - keyStore.Encryptor(keyStoreEncryptor) + keyStoreBuilder.Encryptor(keyStoreEncryptor) redis := cmd.GetRedisParameters() if redis.KeysConfigured() { keyStorage, err := filesystem.NewRedisStorage(redis.HostPort, redis.Password, redis.DBKeys, nil) @@ -324,19 +323,19 @@ func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoade Errorln("Can't initialize Redis client") os.Exit(1) } - keyStore.Storage(keyStorage) + keyStoreBuilder.Storage(keyStorage) } - keyMaking, err := keyStore.Build() + keyStore, err := keyStoreBuilder.Build() if err != nil { log.WithError(err).Errorln("Can't init keystore") os.Exit(1) } - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { keyManager, _ := kmsOptions.NewKeyManager() - keyMaking = baseKMS.NewKeyMakingWrapper(keyMaking, keyManager) + return baseKMS.NewKeyMakingWrapper(keyStore, keyManager) } - return + return keyStore } func openKeyStoreV2(keyDirPath string, loader keyloader.MasterKeyLoader) keystore.KeyMaking { diff --git a/cmd/acra-keys/keys/migrate-keys.go b/cmd/acra-keys/keys/migrate-keys.go index f87808e9c..9ced2b782 100644 --- a/cmd/acra-keys/keys/migrate-keys.go +++ b/cmd/acra-keys/keys/migrate-keys.go @@ -226,7 +226,7 @@ func MigrateV1toV2(srcV1 filesystem.KeyExport, dstV2 keystoreV2.KeyFileImportV1) log.Tracef("Importing %d keys from keystore v1", expected) for _, key := range keys { - log := log.WithField("purpose", key.Purpose).WithField("id", key.ID) + log := log.WithField("purpose", key.KeyContext.Purpose).WithField("id", keystore.GetKeyContextFromContext(key.KeyContext)) err := dstV2.ImportKeyFileV1(srcV1, key) if err != nil { log.WithError(err).Warn("Failed to import key") diff --git a/cmd/acra-server/acra-server.go b/cmd/acra-server/acra-server.go index 21c1485c4..9abc4b27e 100644 --- a/cmd/acra-server/acra-server.go +++ b/cmd/acra-server/acra-server.go @@ -874,7 +874,7 @@ func openKeyStoreV1(output string, cacheSize int, loader keyloader.MasterKeyLoad var keyStoreEncryptor keystore.KeyEncryptor // TODO: consider creating new flag exactly for KMS keystore - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { keyManager, err := kmsOptions.NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") diff --git a/configs/acra-addzone.yaml b/configs/acra-addzone.yaml index db3a7112b..0799d3082 100644 --- a/configs/acra-addzone.yaml +++ b/configs/acra-addzone.yaml @@ -17,6 +17,12 @@ keys_output_dir: .acrakeys # KMS credentials JSON file path kms_credentials_path: +# KMS usage key policy: +kms_key_policy: create + +# Use KMS for keystore encryption +kms_keystore_encryptor: false + # KMS type for using: kms_type: diff --git a/configs/acra-backup.yaml b/configs/acra-backup.yaml index 3d2d52384..14c162b6a 100644 --- a/configs/acra-backup.yaml +++ b/configs/acra-backup.yaml @@ -23,6 +23,12 @@ keys_public_dir: # KMS credentials JSON file path kms_credentials_path: +# KMS usage key policy: +kms_key_policy: create + +# Use KMS for keystore encryption +kms_keystore_encryptor: false + # KMS type for using: kms_type: diff --git a/configs/acra-keymaker.yaml b/configs/acra-keymaker.yaml index 15bd4e4d0..0c2210d90 100644 --- a/configs/acra-keymaker.yaml +++ b/configs/acra-keymaker.yaml @@ -44,6 +44,9 @@ kms_credentials_path: # KMS usage key policy: kms_key_policy: create +# Use KMS for keystore encryption +kms_keystore_encryptor: false + # KMS type for using: kms_type: diff --git a/configs/acra-keys.yaml b/configs/acra-keys.yaml index c1c8000a0..b23a27dcd 100644 --- a/configs/acra-keys.yaml +++ b/configs/acra-keys.yaml @@ -20,6 +20,12 @@ keys_dir_public: # KMS credentials JSON file path kms_credentials_path: +# KMS usage key policy: +kms_key_policy: create + +# Use KMS for keystore encryption +kms_keystore_encryptor: false + # KMS type for using: kms_type: diff --git a/configs/acra-poisonrecordmaker.yaml b/configs/acra-poisonrecordmaker.yaml index f45c52ec5..ec239b45c 100644 --- a/configs/acra-poisonrecordmaker.yaml +++ b/configs/acra-poisonrecordmaker.yaml @@ -17,6 +17,12 @@ keys_dir: .acrakeys # KMS credentials JSON file path kms_credentials_path: +# KMS usage key policy: +kms_key_policy: create + +# Use KMS for keystore encryption +kms_keystore_encryptor: false + # KMS type for using: kms_type: diff --git a/configs/acra-rollback.yaml b/configs/acra-rollback.yaml index 0edb67f71..b378163da 100644 --- a/configs/acra-rollback.yaml +++ b/configs/acra-rollback.yaml @@ -29,6 +29,12 @@ keys_dir: .acrakeys # KMS credentials JSON file path kms_credentials_path: +# KMS usage key policy: +kms_key_policy: create + +# Use KMS for keystore encryption +kms_keystore_encryptor: false + # KMS type for using: kms_type: diff --git a/configs/acra-rotate.yaml b/configs/acra-rotate.yaml index adbae06ac..ec7b37f7b 100644 --- a/configs/acra-rotate.yaml +++ b/configs/acra-rotate.yaml @@ -23,6 +23,12 @@ keys_dir: .acrakeys # KMS credentials JSON file path kms_credentials_path: +# KMS usage key policy: +kms_key_policy: create + +# Use KMS for keystore encryption +kms_keystore_encryptor: false + # KMS type for using: kms_type: diff --git a/configs/acra-server.yaml b/configs/acra-server.yaml index d9c5837de..1329d7a61 100644 --- a/configs/acra-server.yaml +++ b/configs/acra-server.yaml @@ -89,6 +89,12 @@ keystore_cache_size: 1000 # KMS credentials JSON file path kms_credentials_path: +# KMS usage key policy: +kms_key_policy: create + +# Use KMS for keystore encryption +kms_keystore_encryptor: false + # KMS type for using: kms_type: diff --git a/configs/acra-translator.yaml b/configs/acra-translator.yaml index bb5c6dca0..372f7f7ab 100644 --- a/configs/acra-translator.yaml +++ b/configs/acra-translator.yaml @@ -53,6 +53,12 @@ keystore_cache_size: 1000 # KMS credentials JSON file path kms_credentials_path: +# KMS usage key policy: +kms_key_policy: create + +# Use KMS for keystore encryption +kms_keystore_encryptor: false + # KMS type for using: kms_type: diff --git a/keystore/filesystem/filesystem_backup.go b/keystore/filesystem/filesystem_backup.go index 4850080b7..a2a1047f5 100644 --- a/keystore/filesystem/filesystem_backup.go +++ b/keystore/filesystem/filesystem_backup.go @@ -83,47 +83,46 @@ func isPrivate(fname string) bool { return true } -func getIDFromFilename(fname string) (keystore.KeyPurpose, []byte) { +func getContextFromFilename(fname string) keystore.KeyContext { if isHistoricalFilename(fname) { fname = filepath.Dir(fname) } if fname == PoisonKeyFilename { - return keystore.PurposePoisonRecordKeyPair, []byte(fname) + return keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair, []byte(fname)) } if fname == getSymmetricKeyName(PoisonKeyFilename) { - return keystore.PurposePoisonRecordSymmetricKey, []byte(fname[:len(fname)-len("_sym")]) + return keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey, []byte(fname[:len(fname)-len("_sym")])) } fname = filepath.Base(fname) if strings.HasSuffix(fname, ".old") { fname = fname[:len(fname)-len(".old")] } if strings.HasSuffix(fname, "_hmac") { - return keystore.PurposeSearchHMAC, []byte(fname[:len(fname)-len("_hmac")]) + return keystore.NewClientIDKeyContext(keystore.PurposeSearchHMAC, []byte(fname[:len(fname)-len("_hmac")])) } if strings.HasSuffix(fname, "_server") { - return keystore.PurposeLegacy, []byte(fname[:len(fname)-len("_server")]) + return keystore.NewClientIDKeyContext(keystore.PurposeLegacy, []byte(fname[:len(fname)-len("_server")])) } if strings.HasSuffix(fname, "_translator") { - return keystore.PurposeLegacy, []byte(fname[:len(fname)-len("_translator")]) + return keystore.NewClientIDKeyContext(keystore.PurposeLegacy, []byte(fname[:len(fname)-len("_translator")])) } if strings.HasSuffix(fname, "_storage") { - return keystore.PurposeStorageClientPrivateKey, []byte(fname[:len(fname)-len("_storage")]) + return keystore.NewClientIDKeyContext(keystore.PurposeStorageClientPrivateKey, []byte(fname[:len(fname)-len("_storage")])) } if strings.HasSuffix(fname, "_storage_sym") { - return keystore.PurposeStorageClientSymmetricKey, []byte(fname[:len(fname)-len("_storage_sym")]) + return keystore.NewClientIDKeyContext(keystore.PurposeStorageClientSymmetricKey, []byte(fname[:len(fname)-len("_storage_sym")])) } if strings.HasSuffix(fname, "_zone") { - return keystore.PurposeStorageZonePrivateKey, []byte(fname[:len(fname)-len("_zone")]) + return keystore.NewZoneIDKeyContext(keystore.PurposeStorageZonePrivateKey, []byte(fname[:len(fname)-len("_zone")])) } if strings.HasSuffix(fname, "_zone_sym") { - return keystore.PurposeStorageZoneSymmetricKey, []byte(fname[:len(fname)-len("_zone_sym")]) + return keystore.NewZoneIDKeyContext(keystore.PurposeStorageZoneSymmetricKey, []byte(fname[:len(fname)-len("_zone_sym")])) } if strings.HasSuffix(fname, "_sym") { - //TODO: what it the sym key here? - return "", []byte(fname[:len(fname)-len("_sym")]) + return keystore.NewKeyContext(keystore.PurposeStorageZoneSymmetricKey, []byte(fname[:len(fname)-len("_sym")])) } - return keystore.PurposeUndefined, []byte(fname) + return keystore.NewKeyContext(keystore.PurposeUndefined, []byte(fname)) } type dummyEncryptor struct{} @@ -153,10 +152,9 @@ func readFilesAsKeys(files []string, basePath string, encryptor keystore2.KeyEnc // remove absolute first part, leave only relative to path relativeName := strings.Replace(f, basePath+"/", "", -1) if isPrivate(relativeName) { - purpose, id := getIDFromFilename(relativeName) + keyContext := getContextFromFilename(relativeName) - keyContext := keystore.NewKeyContext(purpose).WithContext(id).WithZoneID(id).WithClientID(id) - content, err = encryptor.Decrypt(context.Background(), content, *keyContext) + content, err = encryptor.Decrypt(context.Background(), content, keyContext) if err != nil { return nil, err } @@ -216,7 +214,7 @@ func (store *KeyBackuper) Export() (*keystore.KeysBackup, error) { return nil, err } - encryptedKeys, err := encryptor.Encrypt(context.Background(), buf.Bytes(), *keystore.NewEmptyKeyContext()) + encryptedKeys, err := encryptor.Encrypt(context.Background(), buf.Bytes(), keystore.NewEmptyKeyContext(nil)) if err != nil { return nil, err } @@ -230,7 +228,7 @@ func (store *KeyBackuper) Import(backup *keystore.KeysBackup) error { return err } - decryptedData, err := decryptor.Decrypt(context.Background(), backup.Keys, *keystore.NewEmptyKeyContext()) + decryptedData, err := decryptor.Decrypt(context.Background(), backup.Keys, keystore.NewEmptyKeyContext(nil)) if err != nil { return err } @@ -248,9 +246,8 @@ func (store *KeyBackuper) Import(backup *keystore.KeysBackup) error { content := key.Content if isPrivateKey { - purpose, id := getIDFromFilename(key.Name) - keyContext := keystore.NewKeyContext(purpose).WithContext(id).WithClientID(id).WithZoneID(id) - content, err = store.currentDecryptor.Encrypt(context.Background(), key.Content, *keyContext) + keyContext := getContextFromFilename(key.Name) + content, err = store.currentDecryptor.Encrypt(context.Background(), key.Content, keyContext) // anyway fill with zeros utils.ZeroizeBytes(key.Content) if err != nil { diff --git a/keystore/filesystem/key_export.go b/keystore/filesystem/key_export.go index 9db2329b8..1dd02d54b 100644 --- a/keystore/filesystem/key_export.go +++ b/keystore/filesystem/key_export.go @@ -60,11 +60,10 @@ type KeyFileClassifier interface { // For example, symmetric keys will not have public or private parts, // and only public or private key of a key pair may be present. type ExportedKey struct { - Purpose keystore.KeyPurpose PublicPath string PrivatePath string SymmetricPath string - ID []byte + KeyContext keystore.KeyContext } // Exported key purpose constants: @@ -89,8 +88,7 @@ func (store *KeyStore) ExportPrivateKey(key ExportedKey) (*keys.PrivateKey, erro return nil, err } - keyContext := keystore.NewKeyContext(key.Purpose).WithContext(key.ID).WithClientID(key.ID).WithZoneID(key.ID) - decryptedKey, err := store.encryptor.Decrypt(context.Background(), privateKey.Value, *keyContext) + decryptedKey, err := store.encryptor.Decrypt(context.Background(), privateKey.Value, key.KeyContext) if err != nil { return nil, err } @@ -121,8 +119,7 @@ func (store *KeyStore) ExportSymmetricKey(key ExportedKey) ([]byte, error) { return nil, err } - keyContext := keystore.NewKeyContext(key.Purpose).WithContext(key.ID).WithClientID(key.ID).WithZoneID(key.ID) - keyValue, err := store.encryptor.Decrypt(context.Background(), encrypted, *keyContext) + keyValue, err := store.encryptor.Decrypt(context.Background(), encrypted, key.KeyContext) if err != nil { return nil, err } @@ -212,7 +209,7 @@ func (store *KeyStore) EnumerateExportedKeyPaths() ([]string, error) { // map keys, we have to get a bit creative. func (key *ExportedKey) fusedID() string { - return key.Purpose.String() + string(key.ID) + return key.KeyContext.Purpose.String() + string(keystore.GetKeyContextFromContext(key.KeyContext)) } func (key *ExportedKey) addPathFrom(other *ExportedKey) { @@ -228,36 +225,33 @@ func (key *ExportedKey) addPathFrom(other *ExportedKey) { } // NewExportedSymmetricKey makes an ExportedKey for an unencrypted symmetric key file. -func NewExportedSymmetricKey(symmetricPath string, context []byte, purpose keystore.KeyPurpose) *ExportedKey { +func NewExportedSymmetricKey(symmetricPath string, keyContext keystore.KeyContext) *ExportedKey { return &ExportedKey{ - Purpose: purpose, - ID: context, + KeyContext: keyContext, SymmetricPath: symmetricPath, } } // NewExportedPlaintextSymmetricKey makes an ExportedKey for an unencrypted symmetric key file. -func NewExportedPlaintextSymmetricKey(symmetricPath string, purpose keystore.KeyPurpose) *ExportedKey { +func NewExportedPlaintextSymmetricKey(symmetricPath string, keyContext keystore.KeyContext) *ExportedKey { return &ExportedKey{ - Purpose: purpose, + KeyContext: keyContext, SymmetricPath: symmetricPath, } } // NewExportedPublicKey makes an ExportedKey for a public key file. -func NewExportedPublicKey(publicPath string, id []byte, purpose keystore.KeyPurpose) *ExportedKey { +func NewExportedPublicKey(publicPath string, keyContext keystore.KeyContext) *ExportedKey { return &ExportedKey{ - Purpose: purpose, - ID: id, + KeyContext: keyContext, PublicPath: publicPath, } } // NewExportedPrivateKey makes an ExportedKey for a private key file. -func NewExportedPrivateKey(privatePath string, id []byte, purpose keystore.KeyPurpose) *ExportedKey { +func NewExportedPrivateKey(privatePath string, keyContext keystore.KeyContext) *ExportedKey { return &ExportedKey{ - Purpose: purpose, - ID: id, + KeyContext: keyContext, PrivatePath: privatePath, } } @@ -267,54 +261,57 @@ func (*DefaultKeyFileClassifier) ClassifyExportedKey(path string) *ExportedKey { filename := filepath.Base(path) if filename == SecureLogKeyFilename { - return NewExportedSymmetricKey(path, []byte(SecureLogKeyFilename), keystore.PurposeAuditLog) + keyContext := keystore.NewKeyContext(keystore.PurposeAuditLog, []byte(SecureLogKeyFilename)) + return NewExportedSymmetricKey(path, keyContext) } // Poison key is in ".poison_key" subdirectory, we can't look at filename alone. if strings.HasSuffix(path, "/"+getSymmetricKeyName(PoisonKeyFilename)) { - return NewExportedSymmetricKey(path, []byte(PoisonKeyFilename), keystore.PurposePoisonRecordSymmetricKey) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey, []byte(PoisonKeyFilename)) + return NewExportedSymmetricKey(path, keyContext) } if strings.HasSuffix(filename, "_hmac") { - id := []byte(strings.TrimSuffix(filename, "_hmac")) - return NewExportedSymmetricKey(path, id, keystore.PurposeSearchHMAC) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeSearchHMAC, []byte(strings.TrimSuffix(filename, "_hmac"))) + return NewExportedSymmetricKey(path, keyContext) } if strings.HasSuffix(filename, "_storage_sym") { - id := []byte(strings.TrimSuffix(filename, "_storage_sym")) - return NewExportedSymmetricKey(path, id, keystore.PurposeStorageClientSymmetricKey) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientSymmetricKey, []byte(strings.TrimSuffix(filename, "_storage_sym"))) + return NewExportedSymmetricKey(path, keyContext) } if strings.HasSuffix(filename, "_zone_sym") { - id := []byte(strings.TrimSuffix(filename, "_zone_sym")) - return NewExportedSymmetricKey(path, id, keystore.PurposeStorageZoneSymmetricKey) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZoneSymmetricKey, []byte(strings.TrimSuffix(filename, "_zone_sym"))) + + return NewExportedSymmetricKey(path, keyContext) } // Poison key pairs use PoisonKeyFilename as context for encryption. if strings.HasSuffix(path, poisonKeyFilenamePublic) { - id := []byte(PoisonKeyFilename) - return NewExportedPublicKey(path, id, keystore.PurposePoisonRecordKeyPair) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair, []byte(PoisonKeyFilename)) + return NewExportedPublicKey(path, keyContext) } if strings.HasSuffix(path, PoisonKeyFilename) { - id := []byte(PoisonKeyFilename) - return NewExportedPrivateKey(path, id, keystore.PurposePoisonRecordKeyPair) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair, []byte(PoisonKeyFilename)) + return NewExportedPrivateKey(path, keyContext) } if strings.HasSuffix(filename, "_storage.pub") { - id := []byte(strings.TrimSuffix(filename, "_storage.pub")) - return NewExportedPublicKey(path, id, keystore.PurposeStorageClientKeyPair) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientKeyPair, []byte(strings.TrimSuffix(filename, "_storage.pub"))) + return NewExportedPublicKey(path, keyContext) } if strings.HasSuffix(filename, "_storage") { - id := []byte(strings.TrimSuffix(filename, "_storage")) - return NewExportedPrivateKey(path, id, keystore.PurposeStorageClientKeyPair) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientKeyPair, []byte(strings.TrimSuffix(filename, "_storage"))) + return NewExportedPrivateKey(path, keyContext) } if strings.HasSuffix(filename, "_zone.pub") { - id := []byte(strings.TrimSuffix(filename, "_zone.pub")) - return NewExportedPublicKey(path, id, keystore.PurposeStorageZoneKeyPair) + keyContext := keystore.NewKeyContext(keystore.PurposeStorageZoneKeyPair, []byte(strings.TrimSuffix(filename, "_zone.pub"))) + return NewExportedPublicKey(path, keyContext) } - id := []byte(strings.TrimSuffix(filename, "_zone")) - return NewExportedPrivateKey(path, id, keystore.PurposeStorageZoneKeyPair) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZoneKeyPair, []byte(strings.TrimSuffix(filename, "_zone"))) + return NewExportedPrivateKey(path, keyContext) } diff --git a/keystore/filesystem/server_keystore.go b/keystore/filesystem/server_keystore.go index 1369da1e9..d33a5b0bd 100644 --- a/keystore/filesystem/server_keystore.go +++ b/keystore/filesystem/server_keystore.go @@ -442,14 +442,14 @@ func (store *KeyStore) backupHistoricalKeyFile(filename string) error { func (store *KeyStore) generateZoneKey(id []byte) ([]byte, []byte, error) { /* save private key in fs, return id and public key*/ - keyContext := keystore.NewKeyContext(keystore.PurposeStorageZonePrivateKey).WithContext(id).WithZoneID(id) - keypair, err := store.generateKeyPair(GetZoneKeyFilename(id), *keyContext) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZonePrivateKey, id) + keypair, err := store.generateKeyPair(GetZoneKeyFilename(id), keyContext) if err != nil { return []byte{}, []byte{}, err } store.lock.Lock() defer store.lock.Unlock() - cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, keypair.Private.Value, *keyContext) + cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, keypair.Private.Value, keyContext) if err != nil { return nil, nil, nil } @@ -656,8 +656,8 @@ func (store *KeyStore) GetClientIDEncryptionPublicKey(clientID []byte) (*keys.Pu func (store *KeyStore) GetZonePrivateKey(id []byte) (*keys.PrivateKey, error) { fname := GetZoneKeyFilename(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageZonePrivateKey).WithZoneID(id).WithContext(id) - return store.getPrivateKeyByFilename(fname, *keyContext) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZonePrivateKey, id) + return store.getPrivateKeyByFilename(fname, keyContext) } // HasZonePrivateKey returns if private key for this zoneID exists in cache or is written to fs. @@ -689,8 +689,8 @@ func (store *KeyStore) GetZonePrivateKeys(id []byte) ([]*keys.PrivateKey, error) if err != nil { return nil, err } - keyContext := keystore.NewKeyContext(keystore.PurposeStorageZonePrivateKey).WithZoneID(id).WithContext(id) - return store.getPrivateKeysByFilenames(filenames, *keyContext) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZonePrivateKey, id) + return store.getPrivateKeysByFilenames(filenames, keyContext) } // GetPeerPublicKey returns public key for this clientID, gets it from cache or reads from fs. @@ -720,8 +720,8 @@ func (store *KeyStore) GetPeerPublicKey(id []byte) (*keys.PublicKey, error) { func (store *KeyStore) GetPrivateKey(id []byte) (*keys.PrivateKey, error) { fname := getServerKeyFilename(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) - return store.getPrivateKeyByFilename(fname, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientPrivateKey, id) + return store.getPrivateKeyByFilename(fname, keyContext) } // GetServerDecryptionPrivateKey reads encrypted server storage private key from fs, @@ -729,8 +729,8 @@ func (store *KeyStore) GetPrivateKey(id []byte) (*keys.PrivateKey, error) { // and returns plaintext private key, or reading/decryption error. func (store *KeyStore) GetServerDecryptionPrivateKey(id []byte) (*keys.PrivateKey, error) { fname := GetServerDecryptionKeyFilename(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) - return store.getPrivateKeyByFilename(fname, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientPrivateKey, id) + return store.getPrivateKeyByFilename(fname, keyContext) } // GetServerDecryptionPrivateKeys reads encrypted server storage private keys from fs, @@ -742,8 +742,8 @@ func (store *KeyStore) GetServerDecryptionPrivateKeys(id []byte) ([]*keys.Privat return nil, err } - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) - return store.getPrivateKeysByFilenames(filenames, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientPrivateKey, id) + return store.getPrivateKeysByFilenames(filenames, keyContext) } // GenerateConnectorKeys generates AcraConnector transport EC keypair using clientID as part of key name. @@ -755,8 +755,8 @@ func (store *KeyStore) GenerateConnectorKeys(id []byte) error { } filename := getConnectorKeyFilename(id) - keyContext := keystore.NewKeyContext(keystore.PurposeLegacy).WithContext(id) - _, err := store.generateKeyPair(filename, *keyContext) + keyContext := keystore.NewKeyContext(keystore.PurposeLegacy, id) + _, err := store.generateKeyPair(filename, keyContext) if err != nil { return err } @@ -772,8 +772,8 @@ func (store *KeyStore) GenerateServerKeys(id []byte) error { } filename := getServerKeyFilename(id) - keyContext := keystore.NewKeyContext(keystore.PurposeLegacy).WithContext(id) - _, err := store.generateKeyPair(filename, *keyContext) + keyContext := keystore.NewKeyContext(keystore.PurposeLegacy, id) + _, err := store.generateKeyPair(filename, keyContext) if err != nil { return err } @@ -789,8 +789,8 @@ func (store *KeyStore) GenerateTranslatorKeys(id []byte) error { } filename := getTranslatorKeyFilename(id) - keyContext := keystore.NewKeyContext(keystore.PurposeLegacy).WithContext(id) - _, err := store.generateKeyPair(filename, *keyContext) + keyContext := keystore.NewKeyContext(keystore.PurposeLegacy, id) + _, err := store.generateKeyPair(filename, keyContext) if err != nil { return err } @@ -806,8 +806,8 @@ func (store *KeyStore) GenerateDataEncryptionKeys(id []byte) error { return keystore.ErrInvalidClientID } - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) - _, err := store.generateKeyPair(GetServerDecryptionKeyFilename(id), *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientPrivateKey, id) + _, err := store.generateKeyPair(GetServerDecryptionKeyFilename(id), keyContext) if err != nil { return err } @@ -997,12 +997,12 @@ func (store *KeyStore) Reset() { // Returns an error if fs or crypto operations fail. Also, returns ErrKeysNotFound // if the key pair doesn't exist. func (store *KeyStore) GetPoisonKeyPair() (*keys.Keypair, error) { - keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair).WithContext([]byte(PoisonKeyFilename)) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair, []byte(PoisonKeyFilename)) privateKey, privateOk := store.cache.Get(PoisonKeyFilename) publicKey, publicOk := store.cache.Get(poisonKeyFilenamePublic) if privateOk && publicOk { - decryptedPrivate, err := store.cacheEncryptor.Decrypt(store.encryptorCtx, privateKey, *keyContext) + decryptedPrivate, err := store.cacheEncryptor.Decrypt(store.encryptorCtx, privateKey, keyContext) if err != nil { return nil, err } @@ -1016,11 +1016,11 @@ func (store *KeyStore) GetPoisonKeyPair() (*keys.Keypair, error) { } return nil, err } - if private.Value, err = store.encryptor.Decrypt(store.encryptorCtx, private.Value, *keyContext); err != nil { + if private.Value, err = store.encryptor.Decrypt(store.encryptorCtx, private.Value, keyContext); err != nil { return nil, err } - cacheEncrypted, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, private.Value, *keyContext) + cacheEncrypted, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, private.Value, keyContext) if err != nil { return nil, err } @@ -1047,8 +1047,8 @@ func (store *KeyStore) GetPoisonPrivateKeys() ([]*keys.PrivateKey, error) { return nil, err } - keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair).WithContext([]byte(PoisonKeyFilename)) - poisonKeys, err := store.getPrivateKeysByFilenames(filenames, *keyContext) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair, []byte(PoisonKeyFilename)) + poisonKeys, err := store.getPrivateKeysByFilenames(filenames, keyContext) if err != nil { if IsKeyReadError(err) { return nil, keystore.ErrKeysNotFound @@ -1068,8 +1068,8 @@ func (store *KeyStore) GetPoisonPrivateKeys() ([]*keys.PrivateKey, error) { func (store *KeyStore) GetPoisonSymmetricKeys() ([][]byte, error) { keyFileName := getSymmetricKeyName(PoisonKeyFilename) - keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey).WithContext([]byte(keyFileName)) - keys, err := store.getSymmetricKeys(keyFileName, *keyContext) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey, []byte(keyFileName)) + keys, err := store.getSymmetricKeys(keyFileName, keyContext) if err != nil { if IsKeyReadError(err) { @@ -1089,8 +1089,8 @@ func (store *KeyStore) GetPoisonSymmetricKeys() ([][]byte, error) { func (store *KeyStore) GetPoisonSymmetricKey() ([]byte, error) { keyFileName := getSymmetricKeyName(PoisonKeyFilename) - keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey).WithContext([]byte(keyFileName)) - key, err := store.getLatestSymmetricKey(keyFileName, *keyContext) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey, []byte(keyFileName)) + key, err := store.getLatestSymmetricKey(keyFileName, keyContext) if err == nil { return key, nil } @@ -1112,24 +1112,24 @@ func (store *KeyStore) RotateZoneKey(zoneID []byte) ([]byte, error) { func (store *KeyStore) RotateSymmetricZoneKey(zoneID []byte) error { keyName := getZoneIDSymmetricKeyName(zoneID) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageZoneSymmetricKey).WithZoneID(zoneID).WithContext(zoneID) - return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), *keyContext) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZoneSymmetricKey, zoneID) + return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), keyContext) } // SaveZoneKeypair save or overwrite zone keypair func (store *KeyStore) SaveZoneKeypair(id []byte, keypair *keys.Keypair) error { filename := GetZoneKeyFilename(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageZonePrivateKey).WithZoneID(id).WithContext(id) - return store.SaveKeyPairWithFilename(keypair, filename, *keyContext) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZonePrivateKey, id) + return store.SaveKeyPairWithFilename(keypair, filename, keyContext) } // SaveDataEncryptionKeys save or overwrite decryption keypair for client id func (store *KeyStore) SaveDataEncryptionKeys(id []byte, keypair *keys.Keypair) error { filename := GetServerDecryptionKeyFilename(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithClientID(id).WithContext(id) - return store.SaveKeyPairWithFilename(keypair, filename, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientPrivateKey, id) + return store.SaveKeyPairWithFilename(keypair, filename, keyContext) } // destroyKeyWithFilename removes private and public key with given filename. @@ -1183,15 +1183,15 @@ func (store *KeyStore) Get(keyID string) ([]byte, bool) { // GetHMACSecretKey return key for hmac calculation according to id func (store *KeyStore) GetHMACSecretKey(id []byte) ([]byte, error) { filename := getHmacKeyFilename(id) - keyContext := keystore.NewKeyContext(keystore.PurposeSearchHMAC).WithClientID(id).WithContext(id) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeSearchHMAC, id) encryptedKey, ok := store.Get(filename) if !ok { - return store.loadKeyAndCache(filename, *keyContext, func() ([]byte, error) { + return store.loadKeyAndCache(filename, keyContext, func() ([]byte, error) { return store.ReadKeyFile(store.GetPrivateKeyFilePath(filename)) }) } - return store.cacheEncryptor.Decrypt(store.encryptorCtx, encryptedKey, *keyContext) + return store.cacheEncryptor.Decrypt(store.encryptorCtx, encryptedKey, keyContext) } // GenerateHmacKey key for hmac calculation in in folder for private keys @@ -1202,13 +1202,13 @@ func (store *KeyStore) GenerateHmacKey(id []byte) error { return err } - keyContext := keystore.NewKeyContext(keystore.PurposeSearchHMAC).WithContext(id).WithClientID(id) - encryptedKey, err := store.encryptor.Encrypt(store.encryptorCtx, key, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeSearchHMAC, id) + encryptedKey, err := store.encryptor.Encrypt(store.encryptorCtx, key, keyContext) if err != nil { return err } - cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, key, *keyContext) + cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, key, keyContext) if err != nil { return err } @@ -1234,13 +1234,13 @@ func (store *KeyStore) GenerateLogKey() error { return err } - keyContext := keystore.NewKeyContext(keystore.PurposeAuditLog).WithContext([]byte(SecureLogKeyFilename)) - encryptedKey, err := store.encryptor.Encrypt(store.encryptorCtx, key, *keyContext) + keyContext := keystore.NewKeyContext(keystore.PurposeAuditLog, []byte(SecureLogKeyFilename)) + encryptedKey, err := store.encryptor.Encrypt(store.encryptorCtx, key, keyContext) if err != nil { return err } - cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, key, *keyContext) + cacheEncryptedKey, err := store.cacheEncryptor.Encrypt(store.encryptorCtx, key, keyContext) if err != nil { return err } @@ -1261,16 +1261,16 @@ func (store *KeyStore) GenerateLogKey() error { // GetLogSecretKey return key for log integrity checks func (store *KeyStore) GetLogSecretKey() ([]byte, error) { filename := getLogKeyFilename() - keyContext := keystore.NewKeyContext(keystore.PurposeAuditLog).WithContext([]byte(SecureLogKeyFilename)) + keyContext := keystore.NewKeyContext(keystore.PurposeAuditLog, []byte(SecureLogKeyFilename)) encryptedKey, ok := store.Get(filename) if !ok { - return store.loadKeyAndCache(filename, *keyContext, func() ([]byte, error) { + return store.loadKeyAndCache(filename, keyContext, func() ([]byte, error) { return store.ReadKeyFile(store.GetPrivateKeyFilePath(filename)) }) } - return store.cacheEncryptor.Decrypt(store.encryptorCtx, encryptedKey, *keyContext) + return store.cacheEncryptor.Decrypt(store.encryptorCtx, encryptedKey, keyContext) } // generateSymmetricKey generate symmetric key with specific identifier @@ -1323,16 +1323,16 @@ func (store *KeyStore) loadKeyAndCache(filename string, keyContext keystore.KeyC func (store *KeyStore) GenerateClientIDSymmetricKey(id []byte) error { keyName := getClientIDSymmetricKeyName(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithClientID(id).WithContext(id) - return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientSymmetricKey, id) + return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), keyContext) } // GenerateZoneIDSymmetricKey generate symmetric key for specified zone id func (store *KeyStore) GenerateZoneIDSymmetricKey(id []byte) error { keyName := getZoneIDSymmetricKeyName(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageZoneSymmetricKey).WithZoneID(id).WithContext(id) - return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), *keyContext) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZoneSymmetricKey, id) + return store.generateAndSaveSymmetricKey(store.GetPrivateKeyFilePath(keyName), keyContext) } // GeneratePoisonSymmetricKey generate symmetric key for poison records @@ -1340,15 +1340,15 @@ func (store *KeyStore) GeneratePoisonSymmetricKey() error { keyName := getSymmetricKeyName(PoisonKeyFilename) keyPath := store.GetPrivateKeyFilePath(keyName) - keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey).WithContext([]byte(keyName)) - return store.generateAndSaveSymmetricKey(keyPath, *keyContext) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordSymmetricKey, []byte(keyName)) + return store.generateAndSaveSymmetricKey(keyPath, keyContext) } // GeneratePoisonKeyPair generates new poison keypair, saving it in the storage. // Old keypair is rotated. func (store *KeyStore) GeneratePoisonKeyPair() error { - keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair).WithContext([]byte(PoisonKeyFilename)) - _, err := store.generateKeyPair(PoisonKeyFilename, *keyContext) + keyContext := keystore.NewKeyContext(keystore.PurposePoisonRecordKeyPair, []byte(PoisonKeyFilename)) + _, err := store.generateKeyPair(PoisonKeyFilename, keyContext) return err } @@ -1388,30 +1388,30 @@ func (store *KeyStore) getLatestSymmetricKey(keyname string, keyContext keystore func (store *KeyStore) GetClientIDSymmetricKeys(id []byte) ([][]byte, error) { keyName := getClientIDSymmetricKeyName(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithContext(id).WithClientID(id) - return store.getSymmetricKeys(keyName, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientSymmetricKey, id) + return store.getSymmetricKeys(keyName, keyContext) } // GetClientIDSymmetricKey return latest symmetric key for encryption by specified client id func (store *KeyStore) GetClientIDSymmetricKey(id []byte) ([]byte, error) { keyName := getClientIDSymmetricKeyName(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithContext(id).WithClientID(id) - return store.getLatestSymmetricKey(keyName, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientSymmetricKey, id) + return store.getLatestSymmetricKey(keyName, keyContext) } // GetZoneIDSymmetricKeys return symmetric keys for specified zone id func (store *KeyStore) GetZoneIDSymmetricKeys(id []byte) ([][]byte, error) { keyName := getZoneIDSymmetricKeyName(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageZoneSymmetricKey).WithContext(id).WithZoneID(id) - return store.getSymmetricKeys(keyName, *keyContext) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZoneSymmetricKey, id) + return store.getSymmetricKeys(keyName, keyContext) } // GetZoneIDSymmetricKey return latest symmetric key for encryption in specified zone id func (store *KeyStore) GetZoneIDSymmetricKey(id []byte) ([]byte, error) { keyName := getZoneIDSymmetricKeyName(id) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageZoneSymmetricKey).WithContext(id).WithZoneID(id) - return store.getLatestSymmetricKey(keyName, *keyContext) + keyContext := keystore.NewZoneIDKeyContext(keystore.PurposeStorageZoneSymmetricKey, id) + return store.getLatestSymmetricKey(keyName, keyContext) } diff --git a/keystore/filesystem/server_keystore_test.go b/keystore/filesystem/server_keystore_test.go index 2c54596f6..7eef01a8c 100644 --- a/keystore/filesystem/server_keystore_test.go +++ b/keystore/filesystem/server_keystore_test.go @@ -55,8 +55,8 @@ func testGenerateKeyPair(store *KeyStore, t *testing.T) { } defer store.fs.Remove(path) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithContext(clientID) - keypair, err := store.generateKeyPair(path, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientPrivateKey, clientID) + keypair, err := store.generateKeyPair(path, keyContext) if err != nil { t.Fatal(err) } @@ -144,8 +144,8 @@ func testGenerateSymKeyUncreatedDir(store *KeyStore, t *testing.T) { t.Fatal(err) } - keyContext := keystore.NewEmptyKeyContext().WithContext([]byte("key")) - err = store.generateAndSaveSymmetricKey(fmt.Sprintf("%s/%s", dir, "test_id_sym"), *keyContext) + keyContext := keystore.NewEmptyKeyContext([]byte("key")) + err = store.generateAndSaveSymmetricKey(fmt.Sprintf("%s/%s", dir, "test_id_sym"), keyContext) if err != nil { t.Fatal(err) } @@ -619,8 +619,8 @@ func testFilesystemKeyStoreSymmetricWithCache(storage Storage, t *testing.T) { t.Fatal("Expected key in result") } - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithContext(testID2) - decrypted, err := store.cacheEncryptor.Decrypt(context.Background(), value, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientSymmetricKey, testID2) + decrypted, err := store.cacheEncryptor.Decrypt(context.Background(), value, keyContext) if err != nil { t.Fatal(err) } @@ -700,8 +700,8 @@ func testFilesystemKeyStoreWithCache(storage Storage, t *testing.T) { t.Fatal("Expected key in result") } - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientSymmetricKey).WithContext(testID2) - decrypted, err := store.cacheEncryptor.Decrypt(context.Background(), value, *keyContext) + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientSymmetricKey, testID2) + decrypted, err := store.cacheEncryptor.Decrypt(context.Background(), value, keyContext) if err != nil { t.Fatal(err) } @@ -963,14 +963,14 @@ func testSaveKeypairs(store *KeyStore, t *testing.T) { } // no matter which function to generate correct filename we will use filename := GetServerDecryptionKeyFilename(testID) - keyContext := keystore.NewKeyContext(keystore.PurposeStorageClientPrivateKey).WithContext(testID) - if _, err := store.getPrivateKeyByFilename(filename, *keyContext); err == nil { + keyContext := keystore.NewClientIDKeyContext(keystore.PurposeStorageClientPrivateKey, testID) + if _, err := store.getPrivateKeyByFilename(filename, keyContext); err == nil { t.Fatal("Expected error") } - if err = store.SaveKeyPairWithFilename(startKeypair, filename, *keyContext); err != nil { + if err = store.SaveKeyPairWithFilename(startKeypair, filename, keyContext); err != nil { t.Fatal(err) } - if privateKey, err := store.getPrivateKeyByFilename(filename, *keyContext); err != nil { + if privateKey, err := store.getPrivateKeyByFilename(filename, keyContext); err != nil { t.Fatal(err) } else { if !bytes.Equal(startKeypair.Private.Value, privateKey.Value) { @@ -978,10 +978,10 @@ func testSaveKeypairs(store *KeyStore, t *testing.T) { } } - if err = store.SaveKeyPairWithFilename(overwritedKeypair, filename, *keyContext); err != nil { + if err = store.SaveKeyPairWithFilename(overwritedKeypair, filename, keyContext); err != nil { t.Fatal(err) } - if privateKey, err := store.getPrivateKeyByFilename(filename, *keyContext); err != nil { + if privateKey, err := store.getPrivateKeyByFilename(filename, keyContext); err != nil { t.Fatal(err) } else { if !bytes.Equal(overwritedKeypair.Private.Value, privateKey.Value) { @@ -1064,7 +1064,7 @@ func TestFilesystemKeyStoreExport(t *testing.T) { seenStorageZoneKeyPair := false for i := range exportedKeys { - switch exportedKeys[i].Purpose { + switch exportedKeys[i].KeyContext.Purpose { case keystore.PurposePoisonRecordKeyPair: seenPoisonKeyPair = true publicKey, err := keyStore.ExportPublicKey(exportedKeys[i]) @@ -1114,7 +1114,7 @@ func TestFilesystemKeyStoreExport(t *testing.T) { t.Error("incorrect zone storage private key value") } default: - t.Errorf("unknow key purpose: %s", exportedKeys[i].Purpose) + t.Errorf("unknow key purpose: %s", exportedKeys[i].KeyContext.Purpose) } } diff --git a/keystore/filesystem/translator_keystore.go b/keystore/filesystem/translator_keystore.go index 1016a06f6..822da66db 100644 --- a/keystore/filesystem/translator_keystore.go +++ b/keystore/filesystem/translator_keystore.go @@ -113,8 +113,8 @@ func (store *TranslatorFileSystemKeyStore) GetPrivateKey(id []byte) (*keys.Priva var privateKey []byte - keyContext := keystore.NewKeyContext(keystore.PurposeLegacy).WithContext(id) - if privateKey, err = store.encryptor.Decrypt(context.Background(), keyData, *keyContext); err != nil { + keyContext := keystore.NewKeyContext(keystore.PurposeLegacy, id) + if privateKey, err = store.encryptor.Decrypt(context.Background(), keyData, keyContext); err != nil { return nil, err } return &keys.PrivateKey{Value: privateKey}, nil diff --git a/keystore/keyloader/kms/kms_cli.go b/keystore/keyloader/kms/kms_cli.go index 4c026afb5..e8e7a992c 100644 --- a/keystore/keyloader/kms/kms_cli.go +++ b/keystore/keyloader/kms/kms_cli.go @@ -31,9 +31,10 @@ var SupportedPolicies = []string{ // CLIOptions keep command-line options related to KMS ACRA_MASTER_KEY loading. type CLIOptions struct { - KMSType string - CredentialsPath string - KeyPolicy string + KMSType string + CredentialsPath string + KeyPolicy string + KMSKeystoreEncryptor bool } var cliOptions CLIOptions @@ -53,6 +54,8 @@ func (options *CLIOptions) RegisterCLIParameters(flags *flag.FlagSet, prefix str flags.StringVar(&options.KMSType, prefix+"kms_type", "", fmt.Sprintf("KMS type for using: <%s>", strings.Join(supportedTypes, "|")+description)) // TODO: how to better provide an example of configuration files for different providers flags.StringVar(&options.CredentialsPath, prefix+"kms_credentials_path", "", "KMS credentials JSON file path"+description) + flags.StringVar(&options.CredentialsPath, "kms_key_policy", KeyPolicyCreate, fmt.Sprintf("KMS usage key policy: <%s>", strings.Join(SupportedPolicies, "|"))) + flags.BoolVar(&options.KMSKeystoreEncryptor, "kms_keystore_encryptor", false, "Use KMS for keystore encryption") } } diff --git a/keystore/keystore.go b/keystore/keystore.go index d133fea8d..8d0da87ca 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -220,33 +220,48 @@ type KeyContext struct { } // NewEmptyKeyContext create new empty key context -func NewEmptyKeyContext() *KeyContext { - return &KeyContext{} +func NewEmptyKeyContext(ctx []byte) KeyContext { + return KeyContext{ + Context: ctx, + } } -// NewKeyContext create new key context with key purpose -func NewKeyContext(purpose KeyPurpose) *KeyContext { - return &KeyContext{ +// NewKeyContext create new key context with key purpose and pure context +func NewKeyContext(purpose KeyPurpose, ctx []byte) KeyContext { + return KeyContext{ Purpose: purpose, + Context: ctx, } } -// WithZoneID set zoneID to key context -func (k *KeyContext) WithZoneID(zoneID []byte) *KeyContext { - k.ZoneID = zoneID - return k +// NewClientIDKeyContext create new key context with key purpose and clientID +func NewClientIDKeyContext(purpose KeyPurpose, clientID []byte) KeyContext { + return KeyContext{ + Purpose: purpose, + ClientID: clientID, + } } -// WithClientID set clientID to key context -func (k *KeyContext) WithClientID(clientID []byte) *KeyContext { - k.ClientID = clientID - return k +// NewZoneIDKeyContext create new key context with key purpose and zoneID +func NewZoneIDKeyContext(purpose KeyPurpose, zoneID []byte) KeyContext { + return KeyContext{ + Purpose: purpose, + ZoneID: zoneID, + } } -// WithContext set encryption context to key context -func (k *KeyContext) WithContext(ctx []byte) *KeyContext { - k.Context = ctx - return k +// GetKeyContextFromContext return byte context depending on provided options +func GetKeyContextFromContext(keyContext KeyContext) []byte { + if keyContext.ClientID != nil { + return keyContext.ClientID + } + if keyContext.ZoneID != nil { + return keyContext.ZoneID + } + if keyContext.Context != nil { + return keyContext.Context + } + return nil } // KeyEncryptor describes Encrypt and Decrypt interfaces. @@ -267,13 +282,13 @@ func NewSCellKeyEncryptor(masterKey []byte) (*SCellKeyEncryptor, error) { // Encrypt return encrypted key using masterKey and context. func (encryptor *SCellKeyEncryptor) Encrypt(ctx context.Context, key []byte, keyContext KeyContext) ([]byte, error) { - encrypted, _, err := encryptor.scell.Protect(key, keyContext.Context) + encrypted, _, err := encryptor.scell.Protect(key, GetKeyContextFromContext(keyContext)) return encrypted, err } // Decrypt return decrypted key using masterKey and context. func (encryptor *SCellKeyEncryptor) Decrypt(ctx context.Context, key []byte, keyContext KeyContext) ([]byte, error) { - return encryptor.scell.Unprotect(key, nil, keyContext.Context) + return encryptor.scell.Unprotect(key, nil, GetKeyContextFromContext(keyContext)) } // TransportKeyStore provides access to transport keys. It is used by acra-connector tool. diff --git a/keystore/kms/key_encryptor.go b/keystore/kms/key_encryptor.go index 06f077a35..e1040a027 100644 --- a/keystore/kms/key_encryptor.go +++ b/keystore/kms/key_encryptor.go @@ -58,7 +58,7 @@ func getKeyIDFromContext(ctx keystore.KeyContext) ([]byte, error) { return nil, ErrEmptyClientIDProvided } return []byte("acra_" + string(ctx.ClientID)), nil - case keystore.PurposeStorageZoneSymmetricKey, keystore.PurposeStorageZonePrivateKey: + case keystore.PurposeStorageZoneSymmetricKey, keystore.PurposeStorageZonePrivateKey, keystore.PurposeStorageZoneKeyPair: if ctx.ZoneID == nil { return nil, ErrEmptyZoneIDProvided } diff --git a/keystore/kms/key_making_wrapper.go b/keystore/kms/key_making_wrapper.go index 0401e1e48..c2d96b088 100644 --- a/keystore/kms/key_making_wrapper.go +++ b/keystore/kms/key_making_wrapper.go @@ -2,9 +2,10 @@ package kms import ( "context" - "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/network" + "github.com/cossacklabs/acra/zone" + "github.com/cossacklabs/themis/gothemis/keys" log "github.com/sirupsen/logrus" ) @@ -16,14 +17,20 @@ const ( AcraAuditLogKeyDescription = "Acra common key encryption key, used for encryption/decryption audit log key" ) +// KeyMaking interface used by KMS wrapper for generating keys +type KeyMaking interface { + zone.KeyChecker + keystore.KeyMaking +} + // KeyMakingWrapper wrap keystore.KeyMaking implementation with KMS key creation at start type KeyMakingWrapper struct { - keystore.KeyMaking + KeyMaking kmsKeyManager KeyManager } // NewKeyMakingWrapper create new KeyMakingWrapper -func NewKeyMakingWrapper(keyMaking keystore.KeyMaking, manager KeyManager) KeyMakingWrapper { +func NewKeyMakingWrapper(keyMaking KeyMaking, manager KeyManager) KeyMakingWrapper { return KeyMakingWrapper{ KeyMaking: keyMaking, kmsKeyManager: manager, @@ -117,6 +124,49 @@ func (k KeyMakingWrapper) GenerateClientIDSymmetricKey(id []byte) error { return k.KeyMaking.GenerateClientIDSymmetricKey(id) } +// GenerateZoneKey wrap GenerateZoneKey with KMS key creation at start +func (k KeyMakingWrapper) GenerateZoneKey() ([]byte, []byte, error) { + var id []byte + for { + // generate until key not exists + id = zone.GenerateZoneID() + if !k.KeyMaking.HasZonePrivateKey(id) { + break + } + } + + ctx := keystore.KeyContext{ + ZoneID: id, + Purpose: keystore.PurposeStorageZoneKeyPair, + } + + err := k.createKMSKeyFromContext(ctx, AcraZoneKeyDescription) + if err != nil { + return nil, nil, err + } + + keypair, err := keys.New(keys.TypeEC) + if err != nil { + return nil, nil, err + } + return id, keypair.Public.Value, k.KeyMaking.SaveZoneKeypair(id, keypair) +} + +// GenerateZoneIDSymmetricKey wrap GenerateZoneIDSymmetricKey with KMS key creation at start +func (k KeyMakingWrapper) GenerateZoneIDSymmetricKey(id []byte) error { + ctx := keystore.KeyContext{ + ZoneID: id, + Purpose: keystore.PurposeStorageZoneSymmetricKey, + } + + err := k.createKMSKeyFromContext(ctx, AcraZoneKeyDescription) + if err != nil { + return err + } + + return k.KeyMaking.GenerateZoneIDSymmetricKey(id) +} + func (k KeyMakingWrapper) createKMSKeyFromContext(keyContext keystore.KeyContext, description string) error { ctx, _ := context.WithTimeout(context.Background(), network.DefaultNetworkTimeout) diff --git a/keystore/v2/keystore/filesystem/export.go b/keystore/v2/keystore/filesystem/export.go index bd2d9b7ca..49ff32be7 100644 --- a/keystore/v2/keystore/filesystem/export.go +++ b/keystore/v2/keystore/filesystem/export.go @@ -123,8 +123,8 @@ func (s *KeyStore) encryptAndSignKeyRings(rings []asn1.KeyRing, cryptosuite *cry } defer utils.ZeroizeBytes(keysBytes) - keyContext := keystoreV1.NewEmptyKeyContext().WithContext(exportKeyContext) - encryptedKeyBytes, err := cryptosuite.KeyEncryptor.Encrypt(context.Background(), keysBytes, *keyContext) + keyContext := keystoreV1.NewEmptyKeyContext(exportKeyContext) + encryptedKeyBytes, err := cryptosuite.KeyEncryptor.Encrypt(context.Background(), keysBytes, keyContext) if err != nil { return nil, err } @@ -162,8 +162,8 @@ func (s *KeyStore) decryptAndVerifyKeyRings(ringData []byte, cryptosuite *crypto return nil, errUnsupportedVersion } - keyContext := keystoreV1.NewEmptyKeyContext().WithContext(exportKeyContext) - decryptedKeyBytes, err := cryptosuite.KeyEncryptor.Decrypt(context.Background(), container.Payload.Data.Bytes, *keyContext) + keyContext := keystoreV1.NewEmptyKeyContext(exportKeyContext) + decryptedKeyBytes, err := cryptosuite.KeyEncryptor.Decrypt(context.Background(), container.Payload.Data.Bytes, keyContext) if err != nil { return nil, err } diff --git a/keystore/v2/keystore/filesystem/keyStore.go b/keystore/v2/keystore/filesystem/keyStore.go index 39694242f..2d96a59e4 100644 --- a/keystore/v2/keystore/filesystem/keyStore.go +++ b/keystore/v2/keystore/filesystem/keyStore.go @@ -259,13 +259,13 @@ func (s *KeyStore) keyStoreContext(context []byte) []byte { } func (s *KeyStore) encrypt(data, ctx []byte) ([]byte, error) { - keyContext := keystoreV1.NewEmptyKeyContext().WithContext(s.keyStoreContext(ctx)) - return s.encryptor.Encrypt(context.Background(), data, *keyContext) + keyContext := keystoreV1.NewEmptyKeyContext(s.keyStoreContext(ctx)) + return s.encryptor.Encrypt(context.Background(), data, keyContext) } func (s *KeyStore) decrypt(data, ctx []byte) ([]byte, error) { - keyContext := keystoreV1.NewEmptyKeyContext().WithContext(s.keyStoreContext(ctx)) - return s.encryptor.Decrypt(context.Background(), data, *keyContext) + keyContext := keystoreV1.NewEmptyKeyContext(s.keyStoreContext(ctx)) + return s.encryptor.Decrypt(context.Background(), data, keyContext) } func (s *KeyStore) keyRingSignatureContext(path string) []byte { diff --git a/keystore/v2/keystore/importV1.go b/keystore/v2/keystore/importV1.go index dd6ed2fbd..f0bc9674c 100644 --- a/keystore/v2/keystore/importV1.go +++ b/keystore/v2/keystore/importV1.go @@ -36,8 +36,10 @@ type KeyFileImportV1 interface { // ImportKeyFileV1 transfers key data from keystore version 1. func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key filesystemV1.ExportedKey) error { - log := s.log.WithField("purpose", key.Purpose).WithField("id", key.ID) - switch key.Purpose { + keyID := keystore.GetKeyContextFromContext(key.KeyContext) + log := s.log.WithField("purpose", key.KeyContext.Purpose).WithField("id", keyID) + + switch key.KeyContext.Purpose { case keystore.PurposePoisonRecordKeyPair: keypair, err := oldKeyStore.ExportKeyPair(key) if err != nil { @@ -57,7 +59,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key return err } defer utils.ZeroizeKeyPair(keypair) - err = s.SaveDataEncryptionKeys(key.ID, keypair) + err = s.SaveDataEncryptionKeys(keyID, keypair) if err != nil { log.WithError(err).Debug("failed to import client storage key pair") return err @@ -69,7 +71,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key return err } defer utils.ZeroizeKeyPair(keypair) - err = s.SaveZoneKeypair(key.ID, keypair) + err = s.SaveZoneKeypair(keyID, keypair) if err != nil { log.WithError(err).Debug("failed to import zone storage key pair") return err @@ -93,7 +95,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key return err } defer utils.ZeroizeSymmetricKey(symkey) - err = s.importHmacKey(key.ID, symkey) + err = s.importHmacKey(keyID, symkey) if err != nil { log.WithError(err).Debug("Failed to import search HMAC key") return err @@ -119,7 +121,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key return err } defer utils.ZeroizeSymmetricKey(symkey) - err = s.importClientIDSymmetricKey(key.ID, symkey) + err = s.importClientIDSymmetricKey(keyID, symkey) if err != nil { log.WithError(err).Debug("Failed to import client storage symmetric key") return err @@ -132,7 +134,7 @@ func (s *ServerKeyStore) ImportKeyFileV1(oldKeyStore filesystemV1.KeyExport, key return err } defer utils.ZeroizeSymmetricKey(symkey) - err = s.importZoneIDSymmetricKey(key.ID, symkey) + err = s.importZoneIDSymmetricKey(keyID, symkey) if err != nil { log.WithError(err).Debug("Failed to import zone storage symmetric key") return err diff --git a/tests/test.py b/tests/test.py index 46d38e882..b6d713595 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1225,7 +1225,7 @@ def create_configuration_file(self): configuration = { 'access_key_id': 'access_key_id', 'secret_access_key': 'secret_key_id', - 'region': 'eu-west-1', + 'region': os.environ.get('KMS_REGION', 'eu-west-2'), 'endpoint': os.environ.get('AWS_KMS_ADDRESS', 'http://localhost:8080') } self.config_file = tempfile.NamedTemporaryFile('w+', encoding='utf-8') @@ -1373,7 +1373,7 @@ def get_vault_cli_args(self, mount_path=None, secret_path=None): class AWSKMSClient: def __init__(self): self.url = os.environ.get('AWS_KMS_ADDRESS', 'http://localhost:8080') - self.kms_client = boto3.client('kms', aws_access_key_id='', aws_secret_access_key='', region_name='eu-west-1', endpoint_url=self.url) + self.kms_client = boto3.client('kms', aws_access_key_id='', aws_secret_access_key='', region_name=os.environ.get('KMS_REGION', 'eu-west-2'), endpoint_url=self.url) # override request signer to skip boto3 looking for credentials in ~/.aws_credentials self.kms_client._request_signer.sign = (lambda *args, **kwargs: None) @@ -2299,6 +2299,65 @@ def testReadAcrastructInAcrastruct(self): self.assertEqual(row['empty'], b'') +class KMSAWSType: + def setUp(self): + if not TEST_WITH_AWS_KMS: + self.skipTest("test with AWS KMS ACRA_MASTER_KEY loader") + + configuration = { + 'access_key_id': 'access_key_id', + 'secret_access_key': 'secret_key_id', + 'region': os.environ.get('KMS_REGION', 'eu-west-2'), + 'endpoint': os.environ.get('AWS_KMS_ADDRESS', 'http://localhost:8080') + } + self.config_file = tempfile.NamedTemporaryFile('w+', encoding='utf-8') + json.dump(configuration, self.config_file) + self.config_file.flush() + super().setUp() + + def get_kms_type(self): + return 'aws' + + def get_kms_configuration_path(self): + return self.config_file.name + + +class KMSEncryptorMixing: + ZONES = [] + + def setUp(self): + self.keystore = tempfile.TemporaryDirectory() + self.keys_dir = os.path.join(self.keystore.name, '.acrakeys') + + extra_args = { + 'kms_keystore_encryptor' : 'true', + 'kms_type': self.get_kms_type(), + 'kms_credentials_path': self.get_kms_configuration_path(), + } + assert create_client_keypair_from_certificate(TEST_TLS_CLIENT_CERT, keys_dir=self.keys_dir, extra_kwargs=extra_args) == 0 + assert create_client_keypair_from_certificate(TEST_TLS_CLIENT_2_CERT, keys_dir=self.keys_dir, extra_kwargs=extra_args) == 0 + + self.ZONES.append(json.loads(subprocess.check_output( + [os.path.join(BINARY_OUTPUT_FOLDER, 'acra-addzone'), '--keys_output_dir={}'.format(self.keys_dir), + '--kms_keystore_encryptor=true', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format( self.get_kms_configuration_path())], + cwd=os.getcwd(), timeout=PROCESS_CALL_TIMEOUT).decode('utf-8'))) + + self.ZONES.append(json.loads(subprocess.check_output( + [os.path.join(BINARY_OUTPUT_FOLDER, 'acra-addzone'), '--keys_output_dir={}'.format(self.keys_dir), + '--kms_keystore_encryptor=true', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format( self.get_kms_configuration_path())], + cwd=os.getcwd(), timeout=PROCESS_CALL_TIMEOUT).decode('utf-8'))) + + super().setUp() + + def fork_acra(self, popen_kwargs: dict=None, **acra_kwargs: dict): + acra_kwargs['kms_type'] = self.get_kms_type() + acra_kwargs['kms_credentials_path'] = self.get_kms_configuration_path() + acra_kwargs['kms_keystore_encryptor'] = 'true' + acra_kwargs['keys_dir'] = self.keys_dir + + return super(KMSEncryptorMixing, self).fork_acra(popen_kwargs, **acra_kwargs) + + class TestEnableCachedOnStartupTest(HexFormatTest): def checkSkip(self): @@ -2327,6 +2386,15 @@ def testClientIDRead(self): super().testClientIDRead() +class TestEnableCachedOnStartupAWSKMSKeystore(TestEnableCachedOnStartupTest, KMSAWSType, KMSEncryptorMixing): + # just passed test to check if cache on start is working with KMS + def testReadAcrastructInAcrastruct(self): + pass + + def testClientIDRead(self): + pass + + class TestEnableCachedOnStartupServerV2ErrorExit(BaseTestCase): def checkSkip(self): if KEYSTORE_VERSION == 'v1': @@ -3177,7 +3245,7 @@ def create_configuration_file(self): configuration = { 'access_key_id': 'access_key_id', 'secret_access_key': 'secret_key_id', - 'region': 'eu-west-1', + 'region': os.environ.get('KMS_REGION', 'eu-west-2'), 'endpoint': self.kms_client.get_kms_url() } self.config_file = tempfile.NamedTemporaryFile('w+', encoding='utf-8') @@ -4778,6 +4846,10 @@ class TestTranslatorDisableCachedOnStartupWithAWSKMS(AWSKMSMasterKeyLoaderMixin, pass +class TestTranslatorDisableCachedOnStartupWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixing, TestTranslatorDisableCachedOnStartup): + pass + + class TestTranslatorEnableCachedOnStartup(AcraTranslatorMixin, BaseTestCase): def checkSkip(self): super().checkSkip() @@ -5557,13 +5629,14 @@ class BaseTransparentEncryption(BaseTestCase): sa.Column('empty', sa.LargeBinary(length=COLUMN_DATA_SIZE), nullable=False, default=b''), ) ENCRYPTOR_CONFIG = get_encryptor_config('tests/encryptor_config.yaml') + ZONES = zones def setUp(self): self.prepare_encryptor_config(client_id=TLS_CERT_CLIENT_ID_1) super(BaseTransparentEncryption, self).setUp() def prepare_encryptor_config(self, client_id=None): - prepare_encryptor_config(zone_id=zones[0][ZONE_ID], config_path=self.ENCRYPTOR_CONFIG, client_id=client_id) + prepare_encryptor_config(zone_id=self.ZONES[0][ZONE_ID], config_path=self.ENCRYPTOR_CONFIG, client_id=client_id) def tearDown(self): self.engine_raw.execute(self.encryptor_table.delete()) @@ -5837,6 +5910,10 @@ def update_data(self, context): self.executor2.execute_prepared_statement(query, parameters) +class TestPostgresqlBinaryPreparedTransparentEncryptionWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixing, TestPostgresqlBinaryPreparedTransparentEncryption): + pass + + class TestPostgresqlBinaryPreparedTransparentEncryptionWithAWSKMSMasterKeyLoader(AWSKMSMasterKeyLoaderMixin, TestPostgresqlBinaryPreparedTransparentEncryption): pass @@ -6989,6 +7066,10 @@ class TestTransparentSearchableEncryptionWithZoneWithAWSKMSMasterKeyLoader(AWSKM pass +class TestTransparentSearchableEncryptionWithZoneWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixing, TestTransparentSearchableEncryptionWithZone): + pass + + class BaseTokenization(BaseTestCase): WHOLECELL_MODE = True ENCRYPTOR_CONFIG = get_encryptor_config('tests/ee_tokenization_config.yaml') From 906cc1cdbfe5d0d0eb58d0843f14362e43e6c6ed Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Wed, 3 Aug 2022 17:45:13 +0200 Subject: [PATCH 06/13] zhars/use_kms_per_client Added KMS keystore encryptor initialization to acra-translator --- cmd/acra-translator/acra-translator.go | 39 +++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/cmd/acra-translator/acra-translator.go b/cmd/acra-translator/acra-translator.go index af6b49fdd..9ffaada91 100644 --- a/cmd/acra-translator/acra-translator.go +++ b/cmd/acra-translator/acra-translator.go @@ -26,6 +26,7 @@ import ( "errors" "flag" "fmt" + baseKMS "github.com/cossacklabs/acra/keystore/kms" _ "net/http/pprof" "os" "os/signal" @@ -653,20 +654,32 @@ func waitReadPipe(timeoutDuration time.Duration) error { } func openKeyStoreV1(keysDir string, cacheSize int, loader keyloader.MasterKeyLoader) (keystore.ServerKeyStore, keystore.TranslationKeyStore, error) { - masterKey, err := loader.LoadMasterKey() - if err != nil { - log.WithError(err). - WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantLoadMasterKey). - Errorln("Cannot load master key") - return nil, nil, err - } - scellEncryptor, err := keystore.NewSCellKeyEncryptor(masterKey) - if err != nil { - log.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantInitPrivateKeysEncryptor).WithError(err).Errorln("Can't init scell encryptor") - return nil, nil, err + var keyStoreEncryptor keystore.KeyEncryptor + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { + keyManager, err := kmsOptions.NewKeyManager() + if err != nil { + log.WithError(err).Errorln("Failed to initializer kms KeyManager") + os.Exit(1) + } + + keyStoreEncryptor = baseKMS.NewKeyEncryptor(keyManager) + } else { + masterKey, err := loader.LoadMasterKey() + if err != nil { + log.WithError(err). + WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantLoadMasterKey). + Errorln("Cannot load master key") + return nil, nil, err + } + keyStoreEncryptor, err = keystore.NewSCellKeyEncryptor(masterKey) + if err != nil { + log.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantInitPrivateKeysEncryptor).WithError(err).Errorln("Can't init scell encryptor") + return nil, nil, err + } } var keyStorage filesystem.Storage = &filesystem.DummyStorage{} + var err error redis := cmd.GetRedisParameters() if redis.KeysConfigured() { keyStorage, err = filesystem.NewRedisStorage(redis.HostPort, redis.Password, redis.DBKeys, nil) @@ -678,7 +691,7 @@ func openKeyStoreV1(keysDir string, cacheSize int, loader keyloader.MasterKeyLoa keyStore := filesystem.NewCustomFilesystemKeyStore() keyStore.KeyDirectory(keysDir) keyStore.CacheSize(cacheSize) - keyStore.Encryptor(scellEncryptor) + keyStore.Encryptor(keyStoreEncryptor) keyStore.Storage(keyStorage) keyStoreV1, err := keyStore.Build() if err != nil { @@ -689,7 +702,7 @@ func openKeyStoreV1(keysDir string, cacheSize int, loader keyloader.MasterKeyLoa transportKeyStore := filesystem.NewCustomTranslatorFileSystemKeyStore() transportKeyStore.KeyDirectory(keysDir) - transportKeyStore.Encryptor(scellEncryptor) + transportKeyStore.Encryptor(keyStoreEncryptor) transportKeyStore.Storage(keyStorage) transportKeyStoreV1, err := transportKeyStore.Build() if err != nil { From 24ef4ec8a8d5a0b9eec55874e9af74633b7e8b33 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Wed, 3 Aug 2022 17:50:00 +0200 Subject: [PATCH 07/13] zhars/use_kms_per_client Restructure configs --- cmd/acra-keymaker/acra-keymaker.go | 5 +++-- configs/acra-addzone.yaml | 3 --- configs/acra-backup.yaml | 3 --- configs/acra-keys.yaml | 3 --- configs/acra-poisonrecordmaker.yaml | 3 --- configs/acra-rollback.yaml | 3 --- configs/acra-rotate.yaml | 3 --- configs/acra-server.yaml | 3 --- configs/acra-translator.yaml | 3 --- keystore/keyloader/kms/kms_cli.go | 3 --- 10 files changed, 3 insertions(+), 29 deletions(-) diff --git a/cmd/acra-keymaker/acra-keymaker.go b/cmd/acra-keymaker/acra-keymaker.go index dd3f05ac9..3b99e6284 100644 --- a/cmd/acra-keymaker/acra-keymaker.go +++ b/cmd/acra-keymaker/acra-keymaker.go @@ -67,6 +67,7 @@ func main() { poisonRecord := flag.Bool("generate_poisonrecord_keys", false, "Generate keypair and symmetric key for poison records") cmd.RegisterRedisKeyStoreParameters() keystoreVersion := flag.String("keystore", "", "set keystore format: v1 (current), v2 (new)") + kmsKeyPolicy := flag.String("kms_key_policy", kms.KeyPolicyCreate, fmt.Sprintf("KMS usage key policy: <%s>", strings.Join(kms.SupportedPolicies, "|"))) tlsClientCert := flag.String("tls_cert", "", "Path to TLS certificate to use as client_id identifier") tlsIdentifierExtractorType := flag.String("tls_identifier_extractor_type", network.IdentifierExtractorTypeDistinguishedName, fmt.Sprintf("Decide which field of TLS certificate to use as ClientID (%s). Default is %s.", strings.Join(network.IdentifierExtractorTypesList, "|"), network.IdentifierExtractorTypeDistinguishedName)) @@ -157,7 +158,7 @@ func main() { os.Exit(1) } - switch kmsOptions.KeyPolicy { + switch *kmsKeyPolicy { case kms.KeyPolicyCreate: newKey, err = newMasterKeyWithKMSCreate(keyManager, newKey) if err != nil { @@ -166,7 +167,7 @@ func main() { } default: - log.WithField("supported", kms.SupportedPolicies).WithField("policy", kmsOptions.KeyPolicy).Errorln("Unsupported key policy for `kms_key_policy`") + log.WithField("supported", kms.SupportedPolicies).WithField("policy", *kmsKeyPolicy).Errorln("Unsupported key policy for `kms_key_policy`") os.Exit(1) } } diff --git a/configs/acra-addzone.yaml b/configs/acra-addzone.yaml index 0799d3082..6db022a54 100644 --- a/configs/acra-addzone.yaml +++ b/configs/acra-addzone.yaml @@ -17,9 +17,6 @@ keys_output_dir: .acrakeys # KMS credentials JSON file path kms_credentials_path: -# KMS usage key policy: -kms_key_policy: create - # Use KMS for keystore encryption kms_keystore_encryptor: false diff --git a/configs/acra-backup.yaml b/configs/acra-backup.yaml index 14c162b6a..f63c6dcc5 100644 --- a/configs/acra-backup.yaml +++ b/configs/acra-backup.yaml @@ -23,9 +23,6 @@ keys_public_dir: # KMS credentials JSON file path kms_credentials_path: -# KMS usage key policy: -kms_key_policy: create - # Use KMS for keystore encryption kms_keystore_encryptor: false diff --git a/configs/acra-keys.yaml b/configs/acra-keys.yaml index b23a27dcd..b4279e29e 100644 --- a/configs/acra-keys.yaml +++ b/configs/acra-keys.yaml @@ -20,9 +20,6 @@ keys_dir_public: # KMS credentials JSON file path kms_credentials_path: -# KMS usage key policy: -kms_key_policy: create - # Use KMS for keystore encryption kms_keystore_encryptor: false diff --git a/configs/acra-poisonrecordmaker.yaml b/configs/acra-poisonrecordmaker.yaml index ec239b45c..a391b3d19 100644 --- a/configs/acra-poisonrecordmaker.yaml +++ b/configs/acra-poisonrecordmaker.yaml @@ -17,9 +17,6 @@ keys_dir: .acrakeys # KMS credentials JSON file path kms_credentials_path: -# KMS usage key policy: -kms_key_policy: create - # Use KMS for keystore encryption kms_keystore_encryptor: false diff --git a/configs/acra-rollback.yaml b/configs/acra-rollback.yaml index b378163da..c572f8047 100644 --- a/configs/acra-rollback.yaml +++ b/configs/acra-rollback.yaml @@ -29,9 +29,6 @@ keys_dir: .acrakeys # KMS credentials JSON file path kms_credentials_path: -# KMS usage key policy: -kms_key_policy: create - # Use KMS for keystore encryption kms_keystore_encryptor: false diff --git a/configs/acra-rotate.yaml b/configs/acra-rotate.yaml index ec7b37f7b..c2ee68b87 100644 --- a/configs/acra-rotate.yaml +++ b/configs/acra-rotate.yaml @@ -23,9 +23,6 @@ keys_dir: .acrakeys # KMS credentials JSON file path kms_credentials_path: -# KMS usage key policy: -kms_key_policy: create - # Use KMS for keystore encryption kms_keystore_encryptor: false diff --git a/configs/acra-server.yaml b/configs/acra-server.yaml index 1329d7a61..8cba99814 100644 --- a/configs/acra-server.yaml +++ b/configs/acra-server.yaml @@ -89,9 +89,6 @@ keystore_cache_size: 1000 # KMS credentials JSON file path kms_credentials_path: -# KMS usage key policy: -kms_key_policy: create - # Use KMS for keystore encryption kms_keystore_encryptor: false diff --git a/configs/acra-translator.yaml b/configs/acra-translator.yaml index 372f7f7ab..a2c830d2f 100644 --- a/configs/acra-translator.yaml +++ b/configs/acra-translator.yaml @@ -53,9 +53,6 @@ keystore_cache_size: 1000 # KMS credentials JSON file path kms_credentials_path: -# KMS usage key policy: -kms_key_policy: create - # Use KMS for keystore encryption kms_keystore_encryptor: false diff --git a/keystore/keyloader/kms/kms_cli.go b/keystore/keyloader/kms/kms_cli.go index e8e7a992c..0c11681ee 100644 --- a/keystore/keyloader/kms/kms_cli.go +++ b/keystore/keyloader/kms/kms_cli.go @@ -33,7 +33,6 @@ var SupportedPolicies = []string{ type CLIOptions struct { KMSType string CredentialsPath string - KeyPolicy string KMSKeystoreEncryptor bool } @@ -52,9 +51,7 @@ func (options *CLIOptions) RegisterCLIParameters(flags *flag.FlagSet, prefix str } if flags.Lookup(prefix+"kms_type") == nil { flags.StringVar(&options.KMSType, prefix+"kms_type", "", fmt.Sprintf("KMS type for using: <%s>", strings.Join(supportedTypes, "|")+description)) - // TODO: how to better provide an example of configuration files for different providers flags.StringVar(&options.CredentialsPath, prefix+"kms_credentials_path", "", "KMS credentials JSON file path"+description) - flags.StringVar(&options.CredentialsPath, "kms_key_policy", KeyPolicyCreate, fmt.Sprintf("KMS usage key policy: <%s>", strings.Join(SupportedPolicies, "|"))) flags.BoolVar(&options.KMSKeystoreEncryptor, "kms_keystore_encryptor", false, "Use KMS for keystore encryption") } } From 184b5f76154b5168bd1de973401e891706f97c06 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Wed, 3 Aug 2022 18:25:14 +0200 Subject: [PATCH 08/13] zhars/use_kms_per_client Added KMS keystore encryptor for all keystore use acra tools --- CHANGELOG_DEV.md | 2 +- cmd/acra-addzone/acra-addzone.go | 2 +- cmd/acra-keys/keys/keystore.go | 34 +++++++++----- .../acra-poisonrecordmaker.go | 45 +++++++++++++------ cmd/acra-rollback/acra-rollback.go | 32 ++++++++----- cmd/acra-rotate/acra-rotate.go | 32 ++++++++----- cmd/acra-translator/acra-translator.go | 2 +- keystore/kms/key_making_wrapper.go | 1 + 8 files changed, 103 insertions(+), 47 deletions(-) diff --git a/CHANGELOG_DEV.md b/CHANGELOG_DEV.md index aced7d645..f3be2879f 100644 --- a/CHANGELOG_DEV.md +++ b/CHANGELOG_DEV.md @@ -1,6 +1,6 @@ # 0.94.0 - 2022-08-03 - Implement KMS Keystore encryptor -- Extend `acra-keymaker` and `acra-addzone` tools with ability to create key encryption keys on KMS +- Extend `acra-keymaker`, `acra-addzone` and `acra-poisonrecord` tools with ability to create key encryption keys on KMS # 0.94.0 - 2022-07-08 - Extend encryptor config struct with database settings section diff --git a/cmd/acra-addzone/acra-addzone.go b/cmd/acra-addzone/acra-addzone.go index 4c4460eac..ac1240852 100644 --- a/cmd/acra-addzone/acra-addzone.go +++ b/cmd/acra-addzone/acra-addzone.go @@ -30,7 +30,6 @@ package main import ( "flag" "fmt" - baseKMS "github.com/cossacklabs/acra/keystore/kms" "os" "github.com/cossacklabs/acra/cmd" @@ -39,6 +38,7 @@ import ( "github.com/cossacklabs/acra/keystore/keyloader" "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" "github.com/cossacklabs/acra/keystore/keyloader/kms" + baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" filesystemBackendV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem/backend" diff --git a/cmd/acra-keys/keys/keystore.go b/cmd/acra-keys/keys/keystore.go index aa5f42869..33618fd1e 100644 --- a/cmd/acra-keys/keys/keystore.go +++ b/cmd/acra-keys/keys/keystore.go @@ -19,12 +19,15 @@ package keys import ( "errors" "flag" + "os" + "github.com/cossacklabs/acra/cmd" "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" "github.com/cossacklabs/acra/keystore/keyloader/kms" + baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" "github.com/cossacklabs/acra/keystore/v2/keystore/api" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" @@ -175,19 +178,30 @@ func OpenKeyStoreForImport(params KeyStoreParameters) (api.MutableKeyStore, erro } func openKeyStoreV1(params KeyStoreParameters, loader keyloader.MasterKeyLoader) (*filesystem.KeyStore, error) { - masterKey, err := loader.LoadMasterKey() - if err != nil { - log.WithError(err).Errorln("Cannot load master key") - return nil, err - } - scellEncryptor, err := keystore.NewSCellKeyEncryptor(masterKey) - if err != nil { - log.WithError(err).Errorln("Failed to initialise Secure Cell encryptor") - return nil, err + var keyStoreEncryptor keystore.KeyEncryptor + if kmsOptions := params.KMSCLIOptions(); kmsOptions.KMSKeystoreEncryptor { + keyManager, err := kmsOptions.NewKeyManager() + if err != nil { + log.WithError(err).Errorln("Failed to initializer kms KeyManager") + os.Exit(1) + } + + keyStoreEncryptor = baseKMS.NewKeyEncryptor(keyManager) + } else { + masterKey, err := loader.LoadMasterKey() + if err != nil { + log.WithError(err).Errorln("Cannot load master key") + return nil, err + } + keyStoreEncryptor, err = keystore.NewSCellKeyEncryptor(masterKey) + if err != nil { + log.WithError(err).Errorln("Failed to initialise Secure Cell encryptor") + return nil, err + } } keyStore := filesystem.NewCustomFilesystemKeyStore() - keyStore.Encryptor(scellEncryptor) + keyStore.Encryptor(keyStoreEncryptor) keyDir := params.KeyDir() keyDirPublic := params.KeyDirPublic() diff --git a/cmd/acra-poisonrecordmaker/acra-poisonrecordmaker.go b/cmd/acra-poisonrecordmaker/acra-poisonrecordmaker.go index 7123a7c7a..f8bd39c03 100644 --- a/cmd/acra-poisonrecordmaker/acra-poisonrecordmaker.go +++ b/cmd/acra-poisonrecordmaker/acra-poisonrecordmaker.go @@ -36,6 +36,7 @@ import ( "github.com/cossacklabs/acra/keystore/keyloader" "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" "github.com/cossacklabs/acra/keystore/keyloader/kms" + baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" filesystemBackendV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem/backend" @@ -107,20 +108,31 @@ func main() { } func openKeyStoreV1(output string, loader keyloader.MasterKeyLoader) keystore.PoisonKeyStorageAndGenerator { - masterKey, err := loader.LoadMasterKey() - if err != nil { - log.WithError(err).Errorln("Cannot load master key") - os.Exit(1) - } - scellEncryptor, err := keystore.NewSCellKeyEncryptor(masterKey) - if err != nil { - log.WithError(err).Errorln("Can't init scell encryptor") - os.Exit(1) + var keyStoreEncryptor keystore.KeyEncryptor + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { + keyManager, err := kmsOptions.NewKeyManager() + if err != nil { + log.WithError(err).Errorln("Failed to initializer kms KeyManager") + os.Exit(1) + } + + keyStoreEncryptor = baseKMS.NewKeyEncryptor(keyManager) + } else { + masterKey, err := loader.LoadMasterKey() + if err != nil { + log.WithError(err).Errorln("Cannot load master key") + os.Exit(1) + } + keyStoreEncryptor, err = keystore.NewSCellKeyEncryptor(masterKey) + if err != nil { + log.WithError(err).Errorln("Can't init scell encryptor") + os.Exit(1) + } } - keyStore := filesystem.NewCustomFilesystemKeyStore() - keyStore.KeyDirectory(output) - keyStore.Encryptor(scellEncryptor) + keyStoreBuilder := filesystem.NewCustomFilesystemKeyStore() + keyStoreBuilder.KeyDirectory(output) + keyStoreBuilder.Encryptor(keyStoreEncryptor) redis := cmd.GetRedisParameters() if redis.KeysConfigured() { keyStorage, err := filesystem.NewRedisStorage(redis.HostPort, redis.Password, redis.DBKeys, nil) @@ -129,13 +141,18 @@ func openKeyStoreV1(output string, loader keyloader.MasterKeyLoader) keystore.Po Errorln("Can't initialize Redis client") os.Exit(1) } - keyStore.Storage(keyStorage) + keyStoreBuilder.Storage(keyStorage) } - keyStoreV1, err := keyStore.Build() + keyStoreV1, err := keyStoreBuilder.Build() if err != nil { log.WithError(err).Errorln("Can't init keystore") os.Exit(1) } + + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { + keyManager, _ := kmsOptions.NewKeyManager() + return baseKMS.NewKeyMakingWrapper(keyStoreV1, keyManager) + } return keyStoreV1 } diff --git a/cmd/acra-rollback/acra-rollback.go b/cmd/acra-rollback/acra-rollback.go index 53a9f9a09..e4c98c77b 100644 --- a/cmd/acra-rollback/acra-rollback.go +++ b/cmd/acra-rollback/acra-rollback.go @@ -39,6 +39,7 @@ import ( "github.com/cossacklabs/acra/keystore/keyloader" "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" "github.com/cossacklabs/acra/keystore/keyloader/kms" + baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" "github.com/cossacklabs/acra/logging" @@ -314,18 +315,29 @@ func main() { } func openKeyStoreV1(keysDir string, loader keyloader.MasterKeyLoader) keystore.DecryptionKeyStore { - masterKey, err := loader.LoadMasterKey() - if err != nil { - log.WithError(err).Errorln("Cannot load master key") - os.Exit(1) - } - scellEncryptor, err := keystore.NewSCellKeyEncryptor(masterKey) - if err != nil { - log.WithError(err).Errorln("Can't init scell encryptor") - os.Exit(1) + var keyStoreEncryptor keystore.KeyEncryptor + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { + keyManager, err := kmsOptions.NewKeyManager() + if err != nil { + log.WithError(err).Errorln("Failed to initializer kms KeyManager") + os.Exit(1) + } + + keyStoreEncryptor = baseKMS.NewKeyEncryptor(keyManager) + } else { + masterKey, err := loader.LoadMasterKey() + if err != nil { + log.WithError(err).Errorln("Cannot load master key") + os.Exit(1) + } + keyStoreEncryptor, err = keystore.NewSCellKeyEncryptor(masterKey) + if err != nil { + log.WithError(err).Errorln("Can't init scell encryptor") + os.Exit(1) + } } - keystorage, err := filesystem.NewFilesystemKeyStore(keysDir, scellEncryptor) + keystorage, err := filesystem.NewFilesystemKeyStore(keysDir, keyStoreEncryptor) if err != nil { log.WithError(err).Errorln("Can't initialize keystore") os.Exit(1) diff --git a/cmd/acra-rotate/acra-rotate.go b/cmd/acra-rotate/acra-rotate.go index 5093a3566..62dd4d300 100644 --- a/cmd/acra-rotate/acra-rotate.go +++ b/cmd/acra-rotate/acra-rotate.go @@ -30,6 +30,7 @@ import ( "github.com/cossacklabs/acra/keystore/keyloader" "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" "github.com/cossacklabs/acra/keystore/keyloader/kms" + baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" filesystemBackendV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem/backend" @@ -49,20 +50,31 @@ var ( ) func openKeyStoreV1(dirPath string, loader keyloader.MasterKeyLoader) keystore.ServerKeyStore { - masterKey, err := loader.LoadMasterKey() - if err != nil { - log.WithError(err).Errorln("Cannot load master key") - os.Exit(1) - } - scellEncryptor, err := keystore.NewSCellKeyEncryptor(masterKey) - if err != nil { - log.WithError(err).Errorln("Can't init scell encryptor") - os.Exit(1) + var keyStoreEncryptor keystore.KeyEncryptor + if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { + keyManager, err := kmsOptions.NewKeyManager() + if err != nil { + log.WithError(err).Errorln("Failed to initializer kms KeyManager") + os.Exit(1) + } + + keyStoreEncryptor = baseKMS.NewKeyEncryptor(keyManager) + } else { + masterKey, err := loader.LoadMasterKey() + if err != nil { + log.WithError(err).Errorln("Cannot load master key") + os.Exit(1) + } + keyStoreEncryptor, err = keystore.NewSCellKeyEncryptor(masterKey) + if err != nil { + log.WithError(err).Errorln("Can't init scell encryptor") + os.Exit(1) + } } keyStore := filesystem.NewCustomFilesystemKeyStore() keyStore.KeyDirectory(dirPath) - keyStore.Encryptor(scellEncryptor) + keyStore.Encryptor(keyStoreEncryptor) redis := cmd.GetRedisParameters() if redis.KeysConfigured() { keyStorage, err := filesystem.NewRedisStorage(redis.HostPort, redis.Password, redis.DBKeys, nil) diff --git a/cmd/acra-translator/acra-translator.go b/cmd/acra-translator/acra-translator.go index 9ffaada91..8044e751f 100644 --- a/cmd/acra-translator/acra-translator.go +++ b/cmd/acra-translator/acra-translator.go @@ -26,7 +26,6 @@ import ( "errors" "flag" "fmt" - baseKMS "github.com/cossacklabs/acra/keystore/kms" _ "net/http/pprof" "os" "os/signal" @@ -46,6 +45,7 @@ import ( "github.com/cossacklabs/acra/keystore/keyloader" "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" "github.com/cossacklabs/acra/keystore/keyloader/kms" + baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystem2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" filesystemBackendV2CE "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem/backend" diff --git a/keystore/kms/key_making_wrapper.go b/keystore/kms/key_making_wrapper.go index c2d96b088..676f1d6d5 100644 --- a/keystore/kms/key_making_wrapper.go +++ b/keystore/kms/key_making_wrapper.go @@ -21,6 +21,7 @@ const ( type KeyMaking interface { zone.KeyChecker keystore.KeyMaking + keystore.PoisonKeyStorageAndGenerator } // KeyMakingWrapper wrap keystore.KeyMaking implementation with KMS key creation at start From 2ec2dc83fecaae2835aa156d9e4c27ffc98201ab Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Thu, 4 Aug 2022 14:44:35 +0200 Subject: [PATCH 09/13] zhars/use_kms_per_client Fixed after review/add integration tests --- keystore/filesystem/filesystem_backup.go | 4 +- keystore/filesystem/key_export.go | 5 +- keystore/filesystem/translator_keystore.go | 3 +- keystore/keystore.go | 6 +-- keystore/kms/aws/client.go | 17 ++++-- tests/test.py | 61 +++++++++++++++++----- 6 files changed, 71 insertions(+), 25 deletions(-) diff --git a/keystore/filesystem/filesystem_backup.go b/keystore/filesystem/filesystem_backup.go index a2a1047f5..4c5cbb72a 100644 --- a/keystore/filesystem/filesystem_backup.go +++ b/keystore/filesystem/filesystem_backup.go @@ -27,6 +27,7 @@ import ( keystore2 "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/keystore" + "github.com/cossacklabs/acra/network" "github.com/cossacklabs/acra/utils" ) @@ -153,8 +154,9 @@ func readFilesAsKeys(files []string, basePath string, encryptor keystore2.KeyEnc relativeName := strings.Replace(f, basePath+"/", "", -1) if isPrivate(relativeName) { keyContext := getContextFromFilename(relativeName) + ctx, _ := context.WithTimeout(context.Background(), network.DefaultNetworkTimeout) - content, err = encryptor.Decrypt(context.Background(), content, keyContext) + content, err = encryptor.Decrypt(ctx, content, keyContext) if err != nil { return nil, err } diff --git a/keystore/filesystem/key_export.go b/keystore/filesystem/key_export.go index 1dd02d54b..94bf3a01f 100644 --- a/keystore/filesystem/key_export.go +++ b/keystore/filesystem/key_export.go @@ -17,7 +17,6 @@ package filesystem import ( - "context" "path/filepath" "strings" @@ -88,7 +87,7 @@ func (store *KeyStore) ExportPrivateKey(key ExportedKey) (*keys.PrivateKey, erro return nil, err } - decryptedKey, err := store.encryptor.Decrypt(context.Background(), privateKey.Value, key.KeyContext) + decryptedKey, err := store.encryptor.Decrypt(store.encryptorCtx, privateKey.Value, key.KeyContext) if err != nil { return nil, err } @@ -119,7 +118,7 @@ func (store *KeyStore) ExportSymmetricKey(key ExportedKey) ([]byte, error) { return nil, err } - keyValue, err := store.encryptor.Decrypt(context.Background(), encrypted, key.KeyContext) + keyValue, err := store.encryptor.Decrypt(store.encryptorCtx, encrypted, key.KeyContext) if err != nil { return nil, err } diff --git a/keystore/filesystem/translator_keystore.go b/keystore/filesystem/translator_keystore.go index 822da66db..98f665521 100644 --- a/keystore/filesystem/translator_keystore.go +++ b/keystore/filesystem/translator_keystore.go @@ -17,7 +17,6 @@ limitations under the License. package filesystem import ( - "context" "path/filepath" "github.com/cossacklabs/acra/keystore" @@ -114,7 +113,7 @@ func (store *TranslatorFileSystemKeyStore) GetPrivateKey(id []byte) (*keys.Priva var privateKey []byte keyContext := keystore.NewKeyContext(keystore.PurposeLegacy, id) - if privateKey, err = store.encryptor.Decrypt(context.Background(), keyData, keyContext); err != nil { + if privateKey, err = store.encryptor.Decrypt(store.encryptorCtx, keyData, keyContext); err != nil { return nil, err } return &keys.PrivateKey{Value: privateKey}, nil diff --git a/keystore/keystore.go b/keystore/keystore.go index 8d0da87ca..e9354d323 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -252,12 +252,12 @@ func NewZoneIDKeyContext(purpose KeyPurpose, zoneID []byte) KeyContext { // GetKeyContextFromContext return byte context depending on provided options func GetKeyContextFromContext(keyContext KeyContext) []byte { - if keyContext.ClientID != nil { - return keyContext.ClientID - } if keyContext.ZoneID != nil { return keyContext.ZoneID } + if keyContext.ClientID != nil { + return keyContext.ClientID + } if keyContext.Context != nil { return keyContext.Context } diff --git a/keystore/kms/aws/client.go b/keystore/kms/aws/client.go index 090799e98..77fed3936 100644 --- a/keystore/kms/aws/client.go +++ b/keystore/kms/aws/client.go @@ -106,12 +106,21 @@ func (e *KMSClient) ListAliases(ctx context.Context, keyID *string) ([]types.Ali input.KeyId = keyID } - result, err := e.client.ListAliases(ctx, input) - if err != nil { - return nil, err + aliases := make([]types.AliasListEntry, 0) + for { + result, err := e.client.ListAliases(ctx, input) + if err != nil { + return nil, err + } + + aliases = append(aliases, result.Aliases...) + if result.NextMarker == nil { + break + } + input.Marker = result.NextMarker } - return result.Aliases, nil + return aliases, nil } func readConfigByPath(credentialPath string) (*Configuration, error) { diff --git a/tests/test.py b/tests/test.py index b6d713595..32e9a83ff 100644 --- a/tests/test.py +++ b/tests/test.py @@ -396,6 +396,22 @@ def get_poison_record(): return poison_record +def get_new_poison_record(extra_kwargs: dict=None, keys_dir=None): + """generate one new poison record for speed up tests and don't create subprocess + for new records""" + args = [os.path.join(BINARY_OUTPUT_FOLDER, 'acra-poisonrecordmaker')] + if keys_dir: + args.append('--keys_dir={}'.format(keys_dir)) + else: + args.append('--keys_dir={}'.format(KEYS_FOLDER.name)) + + if extra_kwargs: + for key, value in extra_kwargs.items(): + param = '-{0}={1}'.format(key, value) + args.append(param) + return b64decode(subprocess.check_output(args, timeout=PROCESS_CALL_TIMEOUT)) + + def get_poison_record_with_acrablock(): """generate one poison record with acrablock for speed up tests and don't create subprocess for new records""" @@ -2322,12 +2338,13 @@ def get_kms_configuration_path(self): return self.config_file.name -class KMSEncryptorMixing: +class KMSEncryptorMixin: + # using local list of zones to store KMS created zones that can be used instead of global ZONES list ZONES = [] + poison_record = None def setUp(self): - self.keystore = tempfile.TemporaryDirectory() - self.keys_dir = os.path.join(self.keystore.name, '.acrakeys') + self.keys_dir = tempfile.TemporaryDirectory().name extra_args = { 'kms_keystore_encryptor' : 'true', @@ -2337,25 +2354,40 @@ def setUp(self): assert create_client_keypair_from_certificate(TEST_TLS_CLIENT_CERT, keys_dir=self.keys_dir, extra_kwargs=extra_args) == 0 assert create_client_keypair_from_certificate(TEST_TLS_CLIENT_2_CERT, keys_dir=self.keys_dir, extra_kwargs=extra_args) == 0 + self.poison_record = get_new_poison_record(extra_kwargs=extra_args, keys_dir=self.keys_dir) + self.ZONES.append(json.loads(subprocess.check_output( [os.path.join(BINARY_OUTPUT_FOLDER, 'acra-addzone'), '--keys_output_dir={}'.format(self.keys_dir), - '--kms_keystore_encryptor=true', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format( self.get_kms_configuration_path())], + '--kms_keystore_encryptor=true', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format(self.get_kms_configuration_path())], cwd=os.getcwd(), timeout=PROCESS_CALL_TIMEOUT).decode('utf-8'))) self.ZONES.append(json.loads(subprocess.check_output( [os.path.join(BINARY_OUTPUT_FOLDER, 'acra-addzone'), '--keys_output_dir={}'.format(self.keys_dir), - '--kms_keystore_encryptor=true', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format( self.get_kms_configuration_path())], + '--kms_keystore_encryptor=true', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format(self.get_kms_configuration_path())], cwd=os.getcwd(), timeout=PROCESS_CALL_TIMEOUT).decode('utf-8'))) - super().setUp() + def get_poison_records(self): + return self.poison_record + def fork_acra(self, popen_kwargs: dict=None, **acra_kwargs: dict): acra_kwargs['kms_type'] = self.get_kms_type() acra_kwargs['kms_credentials_path'] = self.get_kms_configuration_path() acra_kwargs['kms_keystore_encryptor'] = 'true' acra_kwargs['keys_dir'] = self.keys_dir - return super(KMSEncryptorMixing, self).fork_acra(popen_kwargs, **acra_kwargs) + return super(KMSEncryptorMixin, self).fork_acra(popen_kwargs, **acra_kwargs) + + def fork_translator(self, translator_kwargs, popen_kwargs=None): + args = { + 'kms_type': self.get_kms_type(), + 'kms_credentials_path': self.get_kms_configuration_path(), + 'kms_keystore_encryptor': 'true', + 'keys_dir': self.keys_dir, + 'logging_format': 'text', + } + translator_kwargs.update(args) + return super(KMSEncryptorMixin, self).fork_translator(translator_kwargs, popen_kwargs) class TestEnableCachedOnStartupTest(HexFormatTest): @@ -2386,7 +2418,7 @@ def testClientIDRead(self): super().testClientIDRead() -class TestEnableCachedOnStartupAWSKMSKeystore(TestEnableCachedOnStartupTest, KMSAWSType, KMSEncryptorMixing): +class TestEnableCachedOnStartupAWSKMSKeystore(TestEnableCachedOnStartupTest, KMSAWSType, KMSEncryptorMixin): # just passed test to check if cache on start is working with KMS def testReadAcrastructInAcrastruct(self): pass @@ -2769,7 +2801,7 @@ def testShutdown3(self): on data decryption failure """ row_id = get_random_id() - poison_record = get_poison_record() + poison_record = self.get_poison_record_data() begin_tag = poison_record[:4] # test with extra long begin tag data = os.urandom(100) + begin_tag + poison_record + os.urandom(100) @@ -2860,6 +2892,11 @@ def testShutdownTranslatorgRPC(self): self.assertEqual(exc.exception.args[0], WAIT_CONNECTION_ERROR_MESSAGE) +class TestPoisonRecordShutdownWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixin, TestPoisonRecordShutdown): + def get_poison_record_data(self): + return self.get_poison_records() + + class TestPoisonRecordShutdownWithAcraBlock(TestPoisonRecordShutdown): def get_poison_record_data(self): return get_poison_record_with_acrablock() @@ -4846,7 +4883,7 @@ class TestTranslatorDisableCachedOnStartupWithAWSKMS(AWSKMSMasterKeyLoaderMixin, pass -class TestTranslatorDisableCachedOnStartupWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixing, TestTranslatorDisableCachedOnStartup): +class TestTranslatorDisableCachedOnStartupWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixin, TestTranslatorDisableCachedOnStartup): pass @@ -5910,7 +5947,7 @@ def update_data(self, context): self.executor2.execute_prepared_statement(query, parameters) -class TestPostgresqlBinaryPreparedTransparentEncryptionWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixing, TestPostgresqlBinaryPreparedTransparentEncryption): +class TestPostgresqlBinaryPreparedTransparentEncryptionWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixin, TestPostgresqlBinaryPreparedTransparentEncryption): pass @@ -7066,7 +7103,7 @@ class TestTransparentSearchableEncryptionWithZoneWithAWSKMSMasterKeyLoader(AWSKM pass -class TestTransparentSearchableEncryptionWithZoneWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixing, TestTransparentSearchableEncryptionWithZone): +class TestTransparentSearchableEncryptionWithZoneWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixin, TestTransparentSearchableEncryptionWithZone): pass From a4209e30a1897eac79ab1a2549565938286c1b8c Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Fri, 5 Aug 2022 15:51:04 +0200 Subject: [PATCH 10/13] zhars/use_kms_per_client Added keystore_encryption_type flag --- cmd/acra-addzone/acra-addzone.go | 17 +++-- cmd/acra-backup/acra-backup.go | 6 +- cmd/acra-keymaker/acra-keymaker.go | 18 ++--- cmd/acra-keys/keys/generate.go | 2 +- cmd/acra-keys/keys/generate_test.go | 3 + cmd/acra-keys/keys/keystore.go | 44 +++++------- cmd/acra-keys/keys/migrate-keys.go | 10 +-- cmd/acra-keys/keys/read-keys_test.go | 18 +++++ .../acra-poisonrecordmaker.go | 16 ++--- cmd/acra-rollback/acra-rollback.go | 13 ++-- cmd/acra-rotate/acra-rotate.go | 13 ++-- cmd/acra-server/acra-server.go | 12 ++-- cmd/acra-translator/acra-translator.go | 13 ++-- configs/acra-addzone.yaml | 6 +- configs/acra-backup.yaml | 15 +--- configs/acra-keymaker.yaml | 6 +- configs/acra-keys.yaml | 48 +++++++++---- configs/acra-poisonrecordmaker.yaml | 15 +--- configs/acra-rollback.yaml | 6 +- configs/acra-rotate.yaml | 6 +- configs/acra-server.yaml | 6 +- configs/acra-translator.yaml | 6 +- keystore/keyloader/env_loader.go | 2 + keystore/keyloader/hashicorp/vault_cli.go | 16 ++--- keystore/keyloader/hashicorp/vault_loader.go | 10 +-- keystore/keyloader/keyloader.go | 50 ++++++-------- keystore/keyloader/keyloader_cli.go | 69 +++++++++++++++++++ keystore/keyloader/kms/kms_cli.go | 28 +++----- tests/test.py | 42 ++++++----- 29 files changed, 290 insertions(+), 226 deletions(-) create mode 100644 keystore/keyloader/keyloader_cli.go diff --git a/cmd/acra-addzone/acra-addzone.go b/cmd/acra-addzone/acra-addzone.go index ac1240852..962e95fdc 100644 --- a/cmd/acra-addzone/acra-addzone.go +++ b/cmd/acra-addzone/acra-addzone.go @@ -36,8 +36,6 @@ import ( "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" - "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" - "github.com/cossacklabs/acra/keystore/keyloader/kms" baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" @@ -61,8 +59,7 @@ func main() { outputDir := flag.String("keys_output_dir", keystore.DefaultKeyDirShort, "Folder where will be saved generated zone keys") flag.Bool("fs_keystore_enable", true, "Use filesystem keystore (deprecated, ignored)") - kms.RegisterCLIParameters() - hashicorp.RegisterVaultCLIParameters() + keyloader.RegisterCLIParameters() cmd.RegisterRedisKeyStoreParameters() verbose := flag.Bool("v", false, "Log to stderr all INFO, WARNING and ERROR logs") @@ -80,7 +77,7 @@ func main() { logging.SetLogLevel(logging.LogVerbose) } - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(hashicorp.GetVaultCLIParameters(), kms.GetCLIParameters()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(keyloader.GetCLIParameters().KeystoreEncryptorType) if err != nil { log.WithError(err).Errorln("Can't initialize ACRA_MASTER_KEY loader") os.Exit(1) @@ -114,8 +111,10 @@ func main() { func openKeyStoreV1(output string, loader keyloader.MasterKeyLoader) keystore.KeyMaking { var keyStoreEncryptor keystore.KeyEncryptor - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, err := kmsOptions.NewKeyManager() + + var keyLoaderParams = keyloader.GetCLIParameters() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, err := keyLoaderParams.GetKMSParameters().NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") os.Exit(1) @@ -154,8 +153,8 @@ func openKeyStoreV1(output string, loader keyloader.MasterKeyLoader) keystore.Ke os.Exit(1) } - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, _ := kmsOptions.NewKeyManager() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, _ := keyLoaderParams.GetKMSParameters().NewKeyManager() return baseKMS.NewKeyMakingWrapper(keyStore, keyManager) } diff --git a/cmd/acra-backup/acra-backup.go b/cmd/acra-backup/acra-backup.go index a033dd625..3d92556a1 100644 --- a/cmd/acra-backup/acra-backup.go +++ b/cmd/acra-backup/acra-backup.go @@ -28,7 +28,6 @@ import ( "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" - "github.com/cossacklabs/acra/keystore/keyloader/kms" "github.com/cossacklabs/acra/logging" "github.com/cossacklabs/acra/utils" @@ -58,8 +57,7 @@ func main() { action := flag.String("action", "", fmt.Sprintf("%s|%s values are accepted", actionImport, actionExport)) file := flag.String("file", "", fmt.Sprintf("path to file which will be used for %s|%s action", actionImport, actionExport)) - kms.RegisterCLIParameters() - cmd.RegisterRedisKeyStoreParameters() + keyloader.RegisterCLIParameters() hashicorp.RegisterVaultCLIParameters() err := cmd.Parse(DefaultConfigPath, ServiceName) @@ -85,7 +83,7 @@ func main() { storage = &filesystem.DummyStorage{} } - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(hashicorp.GetVaultCLIParameters(), kms.GetCLIParameters()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(keyloader.GetCLIParameters().KeystoreEncryptorType) if err != nil { log.WithError(err).Errorln("Can't initialize ACRA_MASTER_KEY loader") os.Exit(1) diff --git a/cmd/acra-keymaker/acra-keymaker.go b/cmd/acra-keymaker/acra-keymaker.go index 3b99e6284..d31b6f8be 100644 --- a/cmd/acra-keymaker/acra-keymaker.go +++ b/cmd/acra-keymaker/acra-keymaker.go @@ -35,7 +35,6 @@ import ( "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" - "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" "github.com/cossacklabs/acra/keystore/keyloader/kms" baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" @@ -72,8 +71,7 @@ func main() { tlsClientCert := flag.String("tls_cert", "", "Path to TLS certificate to use as client_id identifier") tlsIdentifierExtractorType := flag.String("tls_identifier_extractor_type", network.IdentifierExtractorTypeDistinguishedName, fmt.Sprintf("Decide which field of TLS certificate to use as ClientID (%s). Default is %s.", strings.Join(network.IdentifierExtractorTypesList, "|"), network.IdentifierExtractorTypeDistinguishedName)) - kms.RegisterCLIParameters() - hashicorp.RegisterVaultCLIParameters() + keyloader.RegisterCLIParameters() logging.SetLogLevel(logging.LogVerbose) err := cmd.Parse(DefaultConfigPath, ServiceName) @@ -151,7 +149,7 @@ func main() { os.Exit(1) } - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSType != "" { + if kmsOptions := keyloader.GetCLIParameters().GetKMSParameters(); kmsOptions.KMSType != "" { keyManager, err := kmsOptions.NewKeyManager() if err != nil { log.WithError(err).WithField("path", *masterKey).Errorln("Failed to initializer kms KeyManager") @@ -189,7 +187,7 @@ func main() { } } - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(hashicorp.GetVaultCLIParameters(), kms.GetCLIParameters()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(keyloader.GetCLIParameters().KeystoreEncryptorType) if err != nil { log.WithError(err).Errorln("Can't initialize ACRA_MASTER_KEY loader") os.Exit(1) @@ -288,8 +286,10 @@ func main() { func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoader) keystore.KeyMaking { var keyStoreEncryptor keystore.KeyEncryptor - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, err := kmsOptions.NewKeyManager() + + var keyLoaderParams = keyloader.GetCLIParameters() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, err := keyLoaderParams.GetKMSParameters().NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") os.Exit(1) @@ -332,8 +332,8 @@ func openKeyStoreV1(output, outputPublic string, loader keyloader.MasterKeyLoade os.Exit(1) } - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, _ := kmsOptions.NewKeyManager() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, _ := keyLoaderParams.GetKMSParameters().NewKeyManager() return baseKMS.NewKeyMakingWrapper(keyStore, keyManager) } return keyStore diff --git a/cmd/acra-keys/keys/generate.go b/cmd/acra-keys/keys/generate.go index 5fbc9ffea..ff10858e4 100644 --- a/cmd/acra-keys/keys/generate.go +++ b/cmd/acra-keys/keys/generate.go @@ -298,7 +298,7 @@ func (g *GenerateKeySubcommand) Execute() { } } - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(g.CommonKeyStoreParameters.VaultCLIOptions(), g.CommonKeyStoreParameters.KMSCLIOptions()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(g.CommonKeyStoreParameters.KeyLoaderCLIOptions().KeystoreEncryptorType) if err != nil { return } diff --git a/cmd/acra-keys/keys/generate_test.go b/cmd/acra-keys/keys/generate_test.go index 884c90516..fa8617929 100644 --- a/cmd/acra-keys/keys/generate_test.go +++ b/cmd/acra-keys/keys/generate_test.go @@ -52,6 +52,9 @@ func TestRotateSymmetricZoneKey(t *testing.T) { generateCmd := &GenerateKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, + keyLoaderOptions: keyloader.CLIOptions{ + KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, + }, }, zoneID: zoneID, rotateZoneSym: true, diff --git a/cmd/acra-keys/keys/keystore.go b/cmd/acra-keys/keys/keystore.go index 33618fd1e..6bb0841b9 100644 --- a/cmd/acra-keys/keys/keystore.go +++ b/cmd/acra-keys/keys/keystore.go @@ -25,8 +25,6 @@ import ( "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" - "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" - "github.com/cossacklabs/acra/keystore/keyloader/kms" baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" "github.com/cossacklabs/acra/keystore/v2/keystore/api" @@ -49,8 +47,7 @@ type KeyStoreParameters interface { RedisConfigured() bool RedisOptions() *redis.Options - VaultCLIOptions() hashicorp.VaultCLIOptions - KMSCLIOptions() kms.CLIOptions + KeyLoaderCLIOptions() keyloader.CLIOptions } // CommonKeyStoreParameters is a mix-in of command line parameters for keystore construction. @@ -58,9 +55,8 @@ type CommonKeyStoreParameters struct { keyDir string keyDirPublic string - redisOptions cmd.RedisOptions - vaultOptions hashicorp.VaultCLIOptions - kmsOptions kms.CLIOptions + redisOptions cmd.RedisOptions + keyLoaderOptions keyloader.CLIOptions } // KeyDir returns path to key directory. @@ -86,14 +82,9 @@ func (p *CommonKeyStoreParameters) RedisOptions() *redis.Options { return p.redisOptions.KeysOptions() } -// VaultCLIOptions returns Hashicorp Vault configuration options for ACRA_MASTER_KEY loading. -func (p *CommonKeyStoreParameters) VaultCLIOptions() hashicorp.VaultCLIOptions { - return p.vaultOptions -} - -// KMSCLIOptions returns KMS configuration options for ACRA_MASTER_KEY loading. -func (p *CommonKeyStoreParameters) KMSCLIOptions() kms.CLIOptions { - return p.kmsOptions +// KeyLoaderCLIOptions returns common keyloader CLI configuration options for ACRA_MASTER_KEY loading. +func (p *CommonKeyStoreParameters) KeyLoaderCLIOptions() keyloader.CLIOptions { + return p.keyLoaderOptions } // RegisterRedisWithPrefix registers redis options in given flag set, using additional prefix. @@ -101,17 +92,16 @@ func (p *CommonKeyStoreParameters) RegisterRedisWithPrefix(flags *flag.FlagSet, p.redisOptions.RegisterKeyStoreParameters(flags, prefix, description) } -// RegisterVaultWithPrefix registers HashiCorp vault options in given flag set, using additional prefix. -func (p *CommonKeyStoreParameters) RegisterVaultWithPrefix(flags *flag.FlagSet, prefix, description string) { - p.vaultOptions.RegisterCLIParameters(flags, prefix, description) +// RegisterKeyLoaderCLItWithPrefix registers keyloader CLI options in given flag set, using additional prefix. +func (p *CommonKeyStoreParameters) RegisterKeyLoaderCLItWithPrefix(flags *flag.FlagSet, prefix, description string) { + p.keyLoaderOptions.RegisterCLIParameters(flags, prefix, description) } // Register registers keystore flags with the given flag set. func (p *CommonKeyStoreParameters) Register(flags *flag.FlagSet) { p.RegisterPrefixed(flags, DefaultKeyDirectory, "", "") p.redisOptions.RegisterKeyStoreParameters(flags, "", "") - p.vaultOptions.RegisterCLIParameters(flags, "", "") - p.kmsOptions.RegisterCLIParameters(flags, "", "") + p.keyLoaderOptions.RegisterCLIParameters(flags, "", "") } // RegisterPrefixed registers keystore flags with the given flag set, using given prefix and description. @@ -125,7 +115,7 @@ func (p *CommonKeyStoreParameters) RegisterPrefixed(flags *flag.FlagSet, default // OpenKeyStoreForReading opens a keystore suitable for reading keys. func OpenKeyStoreForReading(params KeyStoreParameters) (keystore.ServerKeyStore, error) { - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(params.VaultCLIOptions(), params.KMSCLIOptions()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(params.KeyLoaderCLIOptions().KeystoreEncryptorType) if err != nil { return nil, err } @@ -138,7 +128,7 @@ func OpenKeyStoreForReading(params KeyStoreParameters) (keystore.ServerKeyStore, // OpenKeyStoreForWriting opens a keystore suitable for modifications. func OpenKeyStoreForWriting(params KeyStoreParameters) (keyStore keystore.KeyMaking, err error) { - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(params.VaultCLIOptions(), params.KMSCLIOptions()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(params.KeyLoaderCLIOptions().KeystoreEncryptorType) if err != nil { return nil, err } @@ -151,7 +141,7 @@ func OpenKeyStoreForWriting(params KeyStoreParameters) (keyStore keystore.KeyMak // OpenKeyStoreForExport opens a keystore suitable for export operations. func OpenKeyStoreForExport(params KeyStoreParameters) (api.KeyStore, error) { - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(params.VaultCLIOptions(), params.KMSCLIOptions()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(params.KeyLoaderCLIOptions().KeystoreEncryptorType) if err != nil { return nil, err } @@ -165,7 +155,7 @@ func OpenKeyStoreForExport(params KeyStoreParameters) (api.KeyStore, error) { // OpenKeyStoreForImport opens a keystore suitable for import operations. func OpenKeyStoreForImport(params KeyStoreParameters) (api.MutableKeyStore, error) { - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(params.VaultCLIOptions(), params.KMSCLIOptions()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(params.KeyLoaderCLIOptions().KeystoreEncryptorType) if err != nil { return nil, err } @@ -179,8 +169,10 @@ func OpenKeyStoreForImport(params KeyStoreParameters) (api.MutableKeyStore, erro func openKeyStoreV1(params KeyStoreParameters, loader keyloader.MasterKeyLoader) (*filesystem.KeyStore, error) { var keyStoreEncryptor keystore.KeyEncryptor - if kmsOptions := params.KMSCLIOptions(); kmsOptions.KMSKeystoreEncryptor { - keyManager, err := kmsOptions.NewKeyManager() + + var keyLoaderParams = keyloader.GetCLIParameters() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, err := keyLoaderParams.GetKMSParameters().NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") os.Exit(1) diff --git a/cmd/acra-keys/keys/migrate-keys.go b/cmd/acra-keys/keys/migrate-keys.go index 9ced2b782..3d4322e83 100644 --- a/cmd/acra-keys/keys/migrate-keys.go +++ b/cmd/acra-keys/keys/migrate-keys.go @@ -116,8 +116,8 @@ func (m *MigrateKeysSubcommand) RegisterFlags() { m.flagSet.BoolVar(&m.force, "force", false, "write to output keystore even if it exists") m.src.RegisterRedisWithPrefix(m.flagSet, "src_", "old keystore, source") m.dst.RegisterRedisWithPrefix(m.flagSet, "dst_", "new keystore, destination") - m.src.RegisterVaultWithPrefix(m.flagSet, "src_", "old keystore, source ACRA_MASTER_KEY") - m.dst.RegisterVaultWithPrefix(m.flagSet, "dst_", "new keystore, destination ACRA_MASTER_KEY") + m.src.RegisterKeyLoaderCLItWithPrefix(m.flagSet, "src_", "old keystore, source ACRA_MASTER_KEY") + m.dst.RegisterKeyLoaderCLItWithPrefix(m.flagSet, "dst_", "new keystore, destination ACRA_MASTER_KEY") m.flagSet.Usage = func() { fmt.Fprintf(os.Stderr, "Command \"%s\": migrate keystore to a different format\n", CmdMigrateKeys) @@ -175,12 +175,12 @@ func (m *MigrateKeysSubcommand) Execute() { return } - keyLoaderV1, err := keyloader.GetInitializedMasterKeyLoaderWithEnv(SrcMasterKeyVarName, m.src.VaultCLIOptions()) + keyLoaderV1, err := keyloader.GetInitializedMasterKeyLoaderWithEnv(SrcMasterKeyVarName, m.src.keyLoaderOptions.KeystoreEncryptorType) if err != nil { return } - keyLoaderV2, err := keyloader.GetInitializedMasterKeyLoaderWithEnv(DstMasterKeyVarName, m.dst.VaultCLIOptions()) + keyLoaderV2, err := keyloader.GetInitializedMasterKeyLoaderWithEnv(DstMasterKeyVarName, m.dst.keyLoaderOptions.KeystoreEncryptorType) if err != nil { return } @@ -226,7 +226,7 @@ func MigrateV1toV2(srcV1 filesystem.KeyExport, dstV2 keystoreV2.KeyFileImportV1) log.Tracef("Importing %d keys from keystore v1", expected) for _, key := range keys { - log := log.WithField("purpose", key.KeyContext.Purpose).WithField("id", keystore.GetKeyContextFromContext(key.KeyContext)) + log := log.WithField("purpose", key.KeyContext.Purpose).WithField("id", string(keystore.GetKeyContextFromContext(key.KeyContext))) err := dstV2.ImportKeyFileV1(srcV1, key) if err != nil { log.WithError(err).Warn("Failed to import key") diff --git a/cmd/acra-keys/keys/read-keys_test.go b/cmd/acra-keys/keys/read-keys_test.go index 7288a0448..3b3d35736 100644 --- a/cmd/acra-keys/keys/read-keys_test.go +++ b/cmd/acra-keys/keys/read-keys_test.go @@ -49,6 +49,9 @@ func TestReadCMD_FS_V2(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, + keyLoaderOptions: keyloader.CLIOptions{ + KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, + }, }, contextID: clientID, readKeyKind: KeyStoragePublic, @@ -71,6 +74,9 @@ func TestReadCMD_FS_V2(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, + keyLoaderOptions: keyloader.CLIOptions{ + KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, + }, }, contextID: clientID, readKeyKind: KeySymmetric, @@ -93,6 +99,9 @@ func TestReadCMD_FS_V2(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, + keyLoaderOptions: keyloader.CLIOptions{ + KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, + }, }, contextID: zoneID, readKeyKind: KeyZoneSymmetric, @@ -134,6 +143,9 @@ func TestReadCMD_FS_V1(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, + keyLoaderOptions: keyloader.CLIOptions{ + KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, + }, }, contextID: clientID, readKeyKind: KeyStoragePublic, @@ -156,6 +168,9 @@ func TestReadCMD_FS_V1(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, + keyLoaderOptions: keyloader.CLIOptions{ + KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, + }, }, contextID: clientID, readKeyKind: KeySymmetric, @@ -178,6 +193,9 @@ func TestReadCMD_FS_V1(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, + keyLoaderOptions: keyloader.CLIOptions{ + KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, + }, }, contextID: zoneID, readKeyKind: KeyZoneSymmetric, diff --git a/cmd/acra-poisonrecordmaker/acra-poisonrecordmaker.go b/cmd/acra-poisonrecordmaker/acra-poisonrecordmaker.go index f8bd39c03..b619966ae 100644 --- a/cmd/acra-poisonrecordmaker/acra-poisonrecordmaker.go +++ b/cmd/acra-poisonrecordmaker/acra-poisonrecordmaker.go @@ -35,7 +35,6 @@ import ( "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" - "github.com/cossacklabs/acra/keystore/keyloader/kms" baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" @@ -65,8 +64,7 @@ func main() { dataLength := flag.Int("data_length", poison.UseDefaultDataLength, fmt.Sprintf("Length of random data for data block in acrastruct. -1 is random in range 1..%v", poison.DefaultDataLength)) recordType := flag.String("type", RecordTypeAcraStruct, fmt.Sprintf("Type of poison record: \"%s\" | \"%s\"\n", RecordTypeAcraStruct, RecordTypeAcraBlock)) - kms.RegisterCLIParameters() - cmd.RegisterRedisKeyStoreParameters() + keyloader.RegisterCLIParameters() hashicorp.RegisterVaultCLIParameters() logging.SetLogLevel(logging.LogDiscard) @@ -78,7 +76,7 @@ func main() { os.Exit(1) } - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(hashicorp.GetVaultCLIParameters(), kms.GetCLIParameters()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(keyloader.GetCLIParameters().KeystoreEncryptorType) if err != nil { log.WithError(err).Errorln("Can't initialize ACRA_MASTER_KEY loader") os.Exit(1) @@ -109,8 +107,10 @@ func main() { func openKeyStoreV1(output string, loader keyloader.MasterKeyLoader) keystore.PoisonKeyStorageAndGenerator { var keyStoreEncryptor keystore.KeyEncryptor - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, err := kmsOptions.NewKeyManager() + + var keyLoaderParams = keyloader.GetCLIParameters() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, err := keyLoaderParams.GetKMSParameters().NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") os.Exit(1) @@ -149,8 +149,8 @@ func openKeyStoreV1(output string, loader keyloader.MasterKeyLoader) keystore.Po os.Exit(1) } - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, _ := kmsOptions.NewKeyManager() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, _ := keyLoaderParams.GetKMSParameters().NewKeyManager() return baseKMS.NewKeyMakingWrapper(keyStoreV1, keyManager) } return keyStoreV1 diff --git a/cmd/acra-rollback/acra-rollback.go b/cmd/acra-rollback/acra-rollback.go index e4c98c77b..867fe8930 100644 --- a/cmd/acra-rollback/acra-rollback.go +++ b/cmd/acra-rollback/acra-rollback.go @@ -37,8 +37,6 @@ import ( "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" - "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" - "github.com/cossacklabs/acra/keystore/keyloader/kms" baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" @@ -172,8 +170,7 @@ func main() { useMysql := flag.Bool("mysql_enable", false, "Handle MySQL connections") usePostgresql := flag.Bool("postgresql_enable", false, "Handle Postgresql connections") - kms.RegisterCLIParameters() - hashicorp.RegisterVaultCLIParameters() + keyloader.RegisterCLIParameters() logging.SetLogLevel(logging.LogVerbose) err := cmd.Parse(defaultConfigPath, serviceName) @@ -226,7 +223,7 @@ func main() { os.Exit(1) } - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(hashicorp.GetVaultCLIParameters(), kms.GetCLIParameters()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(keyloader.GetCLIParameters().KeystoreEncryptorType) if err != nil { log.WithError(err).Errorln("Can't initialize ACRA_MASTER_KEY loader") os.Exit(1) @@ -316,8 +313,10 @@ func main() { func openKeyStoreV1(keysDir string, loader keyloader.MasterKeyLoader) keystore.DecryptionKeyStore { var keyStoreEncryptor keystore.KeyEncryptor - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, err := kmsOptions.NewKeyManager() + + var keyLoaderParams = keyloader.GetCLIParameters() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, err := keyLoaderParams.GetKMSParameters().NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") os.Exit(1) diff --git a/cmd/acra-rotate/acra-rotate.go b/cmd/acra-rotate/acra-rotate.go index 62dd4d300..68a07bf55 100644 --- a/cmd/acra-rotate/acra-rotate.go +++ b/cmd/acra-rotate/acra-rotate.go @@ -28,8 +28,6 @@ import ( "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" - "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" - "github.com/cossacklabs/acra/keystore/keyloader/kms" baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" @@ -51,8 +49,10 @@ var ( func openKeyStoreV1(dirPath string, loader keyloader.MasterKeyLoader) keystore.ServerKeyStore { var keyStoreEncryptor keystore.KeyEncryptor - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, err := kmsOptions.NewKeyManager() + + var keyLoaderParams = keyloader.GetCLIParameters() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, err := keyLoaderParams.GetKMSParameters().NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") os.Exit(1) @@ -145,8 +145,7 @@ func main() { dryRun := flag.Bool("dry-run", false, "perform rotation without saving rotated AcraStructs and keys") logging.SetLogLevel(logging.LogVerbose) - kms.RegisterCLIParameters() - hashicorp.RegisterVaultCLIParameters() + keyloader.RegisterCLIParameters() err := cmd.Parse(DefaultConfigPath, ServiceName) if err != nil { @@ -155,7 +154,7 @@ func main() { os.Exit(1) } - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(hashicorp.GetVaultCLIParameters(), kms.GetCLIParameters()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(keyloader.GetCLIParameters().KeystoreEncryptorType) if err != nil { log.WithError(err).Errorln("Can't initialize ACRA_MASTER_KEY loader") os.Exit(1) diff --git a/cmd/acra-server/acra-server.go b/cmd/acra-server/acra-server.go index 9abc4b27e..bd7347ba6 100644 --- a/cmd/acra-server/acra-server.go +++ b/cmd/acra-server/acra-server.go @@ -55,8 +55,6 @@ import ( "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" - "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" - "github.com/cossacklabs/acra/keystore/keyloader/kms" baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystemV2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" @@ -181,8 +179,7 @@ func realMain() error { enableAuditLog := flag.Bool("audit_log_enable", false, "Enable audit log functionality") - hashicorp.RegisterVaultCLIParameters() - kms.RegisterCLIParameters() + keyloader.RegisterCLIParameters() cmd.RegisterTracingCmdParameters() cmd.RegisterJaegerCmdParameters() logging.RegisterCLIArgs() @@ -289,7 +286,7 @@ func realMain() error { serverConfig.SetServiceName(ServiceName) serverConfig.SetConfigPath(cmd.ConfigPath(DefaultConfigPath)) - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(hashicorp.GetVaultCLIParameters(), kms.GetCLIParameters()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(keyloader.GetCLIParameters().KeystoreEncryptorType) if err != nil { log.WithError(err).Errorln("Can't initialize ACRA_MASTER_KEY loader") return err @@ -872,10 +869,9 @@ func waitReadPipe(timeoutDuration time.Duration) error { func openKeyStoreV1(output string, cacheSize int, loader keyloader.MasterKeyLoader) (keystore.ServerKeyStore, error) { var keyStoreEncryptor keystore.KeyEncryptor - // TODO: consider creating new flag exactly for KMS keystore - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, err := kmsOptions.NewKeyManager() + if keyLoaderParams := keyloader.GetCLIParameters(); keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, err := keyLoaderParams.GetKMSParameters().NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") return nil, err diff --git a/cmd/acra-translator/acra-translator.go b/cmd/acra-translator/acra-translator.go index 8044e751f..7242014ed 100644 --- a/cmd/acra-translator/acra-translator.go +++ b/cmd/acra-translator/acra-translator.go @@ -43,8 +43,6 @@ import ( "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/keystore/filesystem" "github.com/cossacklabs/acra/keystore/keyloader" - "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" - "github.com/cossacklabs/acra/keystore/keyloader/kms" baseKMS "github.com/cossacklabs/acra/keystore/kms" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" filesystem2 "github.com/cossacklabs/acra/keystore/v2/keystore/filesystem" @@ -136,8 +134,7 @@ func realMain() error { useClientIDFromConnection := flag.Bool("acratranslator_client_id_from_connection_enable", false, "Use clientID from TLS certificates or secure session handshake instead directly passed values in gRPC methods") enableAuditLog := flag.Bool("audit_log_enable", false, "Enable audit log functionality") - kms.RegisterCLIParameters() - hashicorp.RegisterVaultCLIParameters() + keyloader.RegisterCLIParameters() cmd.RegisterTracingCmdParameters() cmd.RegisterJaegerCmdParameters() logging.RegisterCLIArgs() @@ -200,7 +197,7 @@ func realMain() error { cmd.SetupTracing(ServiceName) - keyLoader, err := keyloader.GetInitializedMasterKeyLoader(hashicorp.GetVaultCLIParameters(), kms.GetCLIParameters()) + keyLoader, err := keyloader.GetInitializedMasterKeyLoader(keyloader.GetCLIParameters().KeystoreEncryptorType) if err != nil { log.WithError(err).Errorln("Can't initialize ACRA_MASTER_KEY loader") return err @@ -655,8 +652,10 @@ func waitReadPipe(timeoutDuration time.Duration) error { func openKeyStoreV1(keysDir string, cacheSize int, loader keyloader.MasterKeyLoader) (keystore.ServerKeyStore, keystore.TranslationKeyStore, error) { var keyStoreEncryptor keystore.KeyEncryptor - if kmsOptions := kms.GetCLIParameters(); kmsOptions.KMSKeystoreEncryptor { - keyManager, err := kmsOptions.NewKeyManager() + + var keyLoaderParams = keyloader.GetCLIParameters() + if keyLoaderParams.KeystoreEncryptorType == keyloader.KeystoreStrategyKMSPerClient { + keyManager, err := keyLoaderParams.GetKMSParameters().NewKeyManager() if err != nil { log.WithError(err).Errorln("Failed to initializer kms KeyManager") os.Exit(1) diff --git a/configs/acra-addzone.yaml b/configs/acra-addzone.yaml index 6db022a54..e0ed3b7c0 100644 --- a/configs/acra-addzone.yaml +++ b/configs/acra-addzone.yaml @@ -14,12 +14,12 @@ generate_markdown_args_table: false # Folder where will be saved generated zone keys keys_output_dir: .acrakeys +# Keystore encryptor strategy; : kms_type: diff --git a/configs/acra-backup.yaml b/configs/acra-backup.yaml index f63c6dcc5..6674901bd 100644 --- a/configs/acra-backup.yaml +++ b/configs/acra-backup.yaml @@ -20,27 +20,18 @@ keys_private_dir: .acrakeys # Folder with public keys. Leave empty if keys stored in same folder as keys_private_dir keys_public_dir: +# Keystore encryptor strategy; : kms_type: # Logging format: plaintext, json or CEF logging_format: plaintext -# Number of Redis database for keys -redis_db_keys: 0 - -# : used to connect to Redis -redis_host_port: - -# Password to Redis database -redis_password: - # Connection string (http://x.x.x.x:yyyy) for loading ACRA_MASTER_KEY from HashiCorp Vault vault_connection_api_string: diff --git a/configs/acra-keymaker.yaml b/configs/acra-keymaker.yaml index 0c2210d90..0923dab81 100644 --- a/configs/acra-keymaker.yaml +++ b/configs/acra-keymaker.yaml @@ -38,15 +38,15 @@ keys_public_output_dir: .acrakeys # set keystore format: v1 (current), v2 (new) keystore: +# Keystore encryptor strategy; : kms_key_policy: create -# Use KMS for keystore encryption -kms_keystore_encryptor: false - # KMS type for using: kms_type: diff --git a/configs/acra-keys.yaml b/configs/acra-keys.yaml index b4279e29e..83066743a 100644 --- a/configs/acra-keys.yaml +++ b/configs/acra-keys.yaml @@ -17,12 +17,12 @@ keys_dir: .acrakeys # path to key directory for public keys keys_dir_public: +# Keystore encryptor strategy; : kms_type: @@ -77,6 +77,15 @@ dst_keys_dir_public: # keystore format to use: v1 (current), v2 (new) dst_keystore: +# Keystore encryptor strategy; : +dst_kms_type: + # Number of Redis database for keys (new keystore, destination) dst_redis_db_keys: 0 @@ -86,22 +95,22 @@ dst_redis_host_port: # Password to Redis database (new keystore, destination) dst_redis_password: -# Connection string (http://x.x.x.x:yyyy) for loading ACRA_MASTER_KEY from HashiCorp Vault (new keystore, destination ACRA_MASTER_KEY) +# Connection string (http://x.x.x.x:yyyy) for loading ACRA_MASTER_KEY from HashiCorp Vault ( (new keystore, destination ACRA_MASTER_KEY)) dst_vault_connection_api_string: -# KV Secret Path (secret/) for reading ACRA_MASTER_KEY from HashiCorp Vault (new keystore, destination ACRA_MASTER_KEY) +# KV Secret Path (secret/) for reading ACRA_MASTER_KEY from HashiCorp Vault ( (new keystore, destination ACRA_MASTER_KEY)) dst_vault_secrets_path: secret/ -# Path to CA certificate for HashiCorp Vault certificate validation (new keystore, destination ACRA_MASTER_KEY) +# Path to CA certificate for HashiCorp Vault certificate validation ( (new keystore, destination ACRA_MASTER_KEY)) dst_vault_tls_ca_path: -# Path to client TLS certificate for reading ACRA_MASTER_KEY from HashiCorp Vault (new keystore, destination ACRA_MASTER_KEY) +# Path to client TLS certificate for reading ACRA_MASTER_KEY from HashiCorp Vault ( (new keystore, destination ACRA_MASTER_KEY)) dst_vault_tls_client_cert: -# Path to private key of the client TLS certificate for reading ACRA_MASTER_KEY from HashiCorp Vault (new keystore, destination ACRA_MASTER_KEY) +# Path to private key of the client TLS certificate for reading ACRA_MASTER_KEY from HashiCorp Vault ( (new keystore, destination ACRA_MASTER_KEY)) dst_vault_tls_client_key: -# Use TLS to encrypt transport with HashiCorp Vault (new keystore, destination ACRA_MASTER_KEY) +# Use TLS to encrypt transport with HashiCorp Vault ( (new keystore, destination ACRA_MASTER_KEY)) dst_vault_tls_transport_enable: false # write to output keystore even if it exists @@ -116,6 +125,15 @@ src_keys_dir_public: # keystore format to use: v1 (current), v2 (new) src_keystore: +# Keystore encryptor strategy; : +src_kms_type: + # Number of Redis database for keys (old keystore, source) src_redis_db_keys: 0 @@ -125,22 +143,22 @@ src_redis_host_port: # Password to Redis database (old keystore, source) src_redis_password: -# Connection string (http://x.x.x.x:yyyy) for loading ACRA_MASTER_KEY from HashiCorp Vault (old keystore, source ACRA_MASTER_KEY) +# Connection string (http://x.x.x.x:yyyy) for loading ACRA_MASTER_KEY from HashiCorp Vault ( (old keystore, source ACRA_MASTER_KEY)) src_vault_connection_api_string: -# KV Secret Path (secret/) for reading ACRA_MASTER_KEY from HashiCorp Vault (old keystore, source ACRA_MASTER_KEY) +# KV Secret Path (secret/) for reading ACRA_MASTER_KEY from HashiCorp Vault ( (old keystore, source ACRA_MASTER_KEY)) src_vault_secrets_path: secret/ -# Path to CA certificate for HashiCorp Vault certificate validation (old keystore, source ACRA_MASTER_KEY) +# Path to CA certificate for HashiCorp Vault certificate validation ( (old keystore, source ACRA_MASTER_KEY)) src_vault_tls_ca_path: -# Path to client TLS certificate for reading ACRA_MASTER_KEY from HashiCorp Vault (old keystore, source ACRA_MASTER_KEY) +# Path to client TLS certificate for reading ACRA_MASTER_KEY from HashiCorp Vault ( (old keystore, source ACRA_MASTER_KEY)) src_vault_tls_client_cert: -# Path to private key of the client TLS certificate for reading ACRA_MASTER_KEY from HashiCorp Vault (old keystore, source ACRA_MASTER_KEY) +# Path to private key of the client TLS certificate for reading ACRA_MASTER_KEY from HashiCorp Vault ( (old keystore, source ACRA_MASTER_KEY)) src_vault_tls_client_key: -# Use TLS to encrypt transport with HashiCorp Vault (old keystore, source ACRA_MASTER_KEY) +# Use TLS to encrypt transport with HashiCorp Vault ( (old keystore, source ACRA_MASTER_KEY)) src_vault_tls_transport_enable: false # read private key of the keypair diff --git a/configs/acra-poisonrecordmaker.yaml b/configs/acra-poisonrecordmaker.yaml index a391b3d19..61655a96e 100644 --- a/configs/acra-poisonrecordmaker.yaml +++ b/configs/acra-poisonrecordmaker.yaml @@ -14,24 +14,15 @@ generate_markdown_args_table: false # Folder from which will be loaded keys keys_dir: .acrakeys +# Keystore encryptor strategy; : kms_type: -# Number of Redis database for keys -redis_db_keys: 0 - -# : used to connect to Redis -redis_host_port: - -# Password to Redis database -redis_password: - # Type of poison record: "acrastruct" | "acrablock" type: acrastruct diff --git a/configs/acra-rollback.yaml b/configs/acra-rollback.yaml index c572f8047..c1c1d07d7 100644 --- a/configs/acra-rollback.yaml +++ b/configs/acra-rollback.yaml @@ -26,12 +26,12 @@ insert: # Folder from which the keys will be loaded keys_dir: .acrakeys +# Keystore encryptor strategy; : kms_type: diff --git a/configs/acra-rotate.yaml b/configs/acra-rotate.yaml index c2ee68b87..75e619d91 100644 --- a/configs/acra-rotate.yaml +++ b/configs/acra-rotate.yaml @@ -20,12 +20,12 @@ generate_markdown_args_table: false # Folder from which the keys will be loaded keys_dir: .acrakeys +# Keystore encryptor strategy; : kms_type: diff --git a/configs/acra-server.yaml b/configs/acra-server.yaml index 8cba99814..eff6fed64 100644 --- a/configs/acra-server.yaml +++ b/configs/acra-server.yaml @@ -86,12 +86,12 @@ keystore_cache_on_start_enable: true # Maximum number of keys stored in in-memory LRU cache in encrypted form. 0 - no limits, -1 - turn off cache. Default is 1000 keystore_cache_size: 1000 +# Keystore encryptor strategy; : kms_type: diff --git a/configs/acra-translator.yaml b/configs/acra-translator.yaml index a2c830d2f..e65c18ee8 100644 --- a/configs/acra-translator.yaml +++ b/configs/acra-translator.yaml @@ -50,12 +50,12 @@ keystore_cache_on_start_enable: true # Maximum number of keys stored in in-memory LRU cache in encrypted form. 0 - no limits, -1 - turn off cache. Default is 1000 keystore_cache_size: 1000 +# Keystore encryptor strategy; : kms_type: diff --git a/keystore/keyloader/env_loader.go b/keystore/keyloader/env_loader.go index e31fe7a3d..c16c7cf6b 100644 --- a/keystore/keyloader/env_loader.go +++ b/keystore/keyloader/env_loader.go @@ -3,6 +3,7 @@ package keyloader import ( "github.com/cossacklabs/acra/keystore" keystoreV2 "github.com/cossacklabs/acra/keystore/v2/keystore" + log "github.com/sirupsen/logrus" ) // EnvLoader unifying structure for implementation env MasterKeyLoader @@ -12,6 +13,7 @@ type EnvLoader struct { // NewEnvLoader return key loader using env variable func NewEnvLoader(env string) EnvLoader { + log.Infof("Initializing default env %s loader", env) return EnvLoader{ MasterKeyEnv: env, } diff --git a/keystore/keyloader/hashicorp/vault_cli.go b/keystore/keyloader/hashicorp/vault_cli.go index 3c46a3d9b..efd17ff06 100644 --- a/keystore/keyloader/hashicorp/vault_cli.go +++ b/keystore/keyloader/hashicorp/vault_cli.go @@ -1,13 +1,16 @@ package hashicorp import ( + "errors" "flag" - "github.com/cossacklabs/acra/keystore/keyloader" "github.com/hashicorp/vault/api" log "github.com/sirupsen/logrus" ) +// ErrEmptyConnectionURL error displaying empty Hashicorp Vault connection URL +var ErrEmptyConnectionURL = errors.New("empty Hashicorp Vault connection URL provided") + const ( defaultVaultSecretsPath = "secret/" vaultConnectionStringFlag = "vault_connection_api_string" @@ -55,15 +58,10 @@ func (options *VaultCLIOptions) TLSConfig() *api.TLSConfig { } } -// GetVaultCLIParameters returns a copy of VaultCLIOptions parsed from the command line. -func GetVaultCLIParameters() *VaultCLIOptions { - return &vaultOptions -} - -// New create MasterKeyLoader from VaultCLIOptions - implementation of keyloader.CliMasterKeyLoaderCreator interface -func (options VaultCLIOptions) New() (keyloader.MasterKeyLoader, error) { +// NewMasterKeyLoader create MasterKeyLoader from VaultCLIOptions +func (options *VaultCLIOptions) NewMasterKeyLoader() (*VaultLoader, error) { if options.Address == "" { - return nil, nil + return nil, ErrEmptyConnectionURL } log.Infoln("Initializing connection to HashiCorp Vault for ACRA_MASTER_KEY loading") diff --git a/keystore/keyloader/hashicorp/vault_loader.go b/keystore/keyloader/hashicorp/vault_loader.go index f575c21be..22b21bf66 100644 --- a/keystore/keyloader/hashicorp/vault_loader.go +++ b/keystore/keyloader/hashicorp/vault_loader.go @@ -62,27 +62,27 @@ type ( ) // NewVaultLoader read VAULT_API_TOKEN env, decode it and return initialized VaultLoader -func NewVaultLoader(config *api.Config, secretPath string) (VaultLoader, error) { +func NewVaultLoader(config *api.Config, secretPath string) (*VaultLoader, error) { b64value := os.Getenv(vaultAPIToken) if len(b64value) == 0 { log.Warnf("%v environment variable is not set", vaultAPIToken) - return VaultLoader{}, ErrEmptyAPIToken + return nil, ErrEmptyAPIToken } decodeValue, err := base64.StdEncoding.DecodeString(b64value) if err != nil { log.WithError(err).Warnf("Failed to decode %s", vaultAPIToken) - return VaultLoader{}, err + return nil, err } client, err := api.NewClient(config) if err != nil { - return VaultLoader{}, err + return nil, err } vaultToken := strings.Trim(string(decodeValue), "\n") client.SetToken(vaultToken) - return VaultLoader{ + return &VaultLoader{ client: client, secretPath: secretPath, }, nil diff --git a/keystore/keyloader/keyloader.go b/keystore/keyloader/keyloader.go index a7d352a8a..de7cb2a3c 100644 --- a/keystore/keyloader/keyloader.go +++ b/keystore/keyloader/keyloader.go @@ -1,14 +1,13 @@ package keyloader import ( + "errors" + "github.com/cossacklabs/acra/keystore" - log "github.com/sirupsen/logrus" ) -// CliMasterKeyLoaderCreator represent interface for all creators of MasterKeyLoader -type CliMasterKeyLoaderCreator interface { - New() (MasterKeyLoader, error) -} +// ErrUnsupportedMasterKeyLoaderStrategy error displaying unsupported MasterKeyLoader strategy +var ErrUnsupportedMasterKeyLoaderStrategy = errors.New("unsupported MasterKeyLoader strategy provided") // MasterKeyLoader interface for loading ACRA_MASTER_KEYs from different sources. type MasterKeyLoader interface { @@ -16,34 +15,29 @@ type MasterKeyLoader interface { LoadMasterKeys() (encryption []byte, signature []byte, err error) } -// GetInitializedMasterKeyLoader returns initialized MasterKeyLoader interface depending on hashicorp vault params +// GetInitializedMasterKeyLoader returns initialized MasterKeyLoader interface depending on incoming load key strategy // with predefined ACRA_MASTER_KEY env name -func GetInitializedMasterKeyLoader(creators ...CliMasterKeyLoaderCreator) (keyLoader MasterKeyLoader, err error) { - return initMasterKeyLoaderWithEnv(keystore.AcraMasterKeyVarName, creators...) +func GetInitializedMasterKeyLoader(loadStrategy string) (keyLoader MasterKeyLoader, err error) { + return initMasterKeyLoaderWithEnv(keystore.AcraMasterKeyVarName, loadStrategy) } -// GetInitializedMasterKeyLoaderWithEnv returns initialized MasterKeyLoader interface depending on hashicorp vault params and env name -func GetInitializedMasterKeyLoaderWithEnv(envVarName string, creators ...CliMasterKeyLoaderCreator) (keyLoader MasterKeyLoader, err error) { - return initMasterKeyLoaderWithEnv(envVarName, creators...) +// GetInitializedMasterKeyLoaderWithEnv returns initialized MasterKeyLoader interface depending on incoming load key strategy +func GetInitializedMasterKeyLoaderWithEnv(envVarName string, loadStrategy string) (keyLoader MasterKeyLoader, err error) { + return initMasterKeyLoaderWithEnv(envVarName, loadStrategy) } -// initMasterKeyLoaderWithEnv returns initialized MasterKeyLoader interface depending on incoming params, -// via provided CliMasterKeyLoaderCreator +// initMasterKeyLoaderWithEnv returns initialized MasterKeyLoader interface depending on incoming load key strategy // otherwise EnvLoader with env name will be returned. -func initMasterKeyLoaderWithEnv(envVarName string, creators ...CliMasterKeyLoaderCreator) (keyLoader MasterKeyLoader, err error) { - log.Infof("Initializing ACRA_MASTER_KEY loader...") - - for _, creator := range creators { - masterKeyLoader, err := creator.New() - if err != nil { - return nil, err - } - - if masterKeyLoader != nil { - return masterKeyLoader, nil - } +func initMasterKeyLoaderWithEnv(envVarName string, loadStrategy string) (MasterKeyLoader, error) { + cliParams := GetCLIParameters() + switch loadStrategy { + case KeystoreStrategyKMSMasterKey: + return cliParams.GetKMSParameters().NewMasterKeyLoader() + case KeystoreStrategyHashicorpVaultMasterKey: + return cliParams.GetVaultCLIParameters().NewMasterKeyLoader() + case KeystoreStrategyMasterKey: + return NewEnvLoader(envVarName), nil + default: + return nil, ErrUnsupportedMasterKeyLoaderStrategy } - - log.Infof("Initialized default env %s loader", envVarName) - return NewEnvLoader(envVarName), nil } diff --git a/keystore/keyloader/keyloader_cli.go b/keystore/keyloader/keyloader_cli.go new file mode 100644 index 000000000..2e6b7c34b --- /dev/null +++ b/keystore/keyloader/keyloader_cli.go @@ -0,0 +1,69 @@ +package keyloader + +import ( + "flag" + "fmt" + "strings" + + "github.com/cossacklabs/acra/keystore/keyloader/hashicorp" + "github.com/cossacklabs/acra/keystore/keyloader/kms" +) + +// represent all possible keyloader strategies +const ( + KeystoreStrategyMasterKey = "master_key" + KeystoreStrategyKMSMasterKey = "kms_master_key" + KeystoreStrategyHashicorpVaultMasterKey = "vault_master_key" + KeystoreStrategyKMSPerClient = "kms_per_client" +) + +// SupportedKeystoreStrategies contains all possible values for flag `--keystore_encryption_type` +var SupportedKeystoreStrategies = []string{ + KeystoreStrategyMasterKey, + KeystoreStrategyKMSMasterKey, + KeystoreStrategyHashicorpVaultMasterKey, + KeystoreStrategyKMSPerClient, +} + +// CLIOptions keep command-line options related to KMS ACRA_MASTER_KEY loading. +type CLIOptions struct { + kmsOptions kms.CLIOptions + vaultOptions hashicorp.VaultCLIOptions + KeystoreEncryptorType string +} + +var cliOptions CLIOptions + +// RegisterCLIParameters registers CLI parameters for reading ACRA_MASTER_KEY from KMS. +func RegisterCLIParameters() { + cliOptions.RegisterCLIParameters(flag.CommandLine, "", "") +} + +// RegisterCLIParameters look up for vault_connection_api_string, if none exists, vault_connection_api_string and vault_secrets_path +// will be added to provided flags. +func (options *CLIOptions) RegisterCLIParameters(flags *flag.FlagSet, prefix string, description string) { + if description != "" { + description = " (" + description + ")" + } + if flags.Lookup(prefix+"kms_type") == nil { + flags.StringVar(&options.KeystoreEncryptorType, prefix+"keystore_encryption_type", KeystoreStrategyMasterKey, fmt.Sprintf("Keystore encryptor strategy; : <%s", strings.Join(SupportedKeystoreStrategies, "|")+description)) + } + + options.vaultOptions.RegisterCLIParameters(flags, prefix, description) + options.kmsOptions.RegisterCLIParameters(flags, prefix, description) +} + +// GetCLIParameters returns a copy of CLIOptions parsed from the command line. +func GetCLIParameters() *CLIOptions { + return &cliOptions +} + +// GetVaultCLIParameters returns a copy of VaultCLIOptions parsed from the command line. +func (options *CLIOptions) GetVaultCLIParameters() *hashicorp.VaultCLIOptions { + return &options.vaultOptions +} + +// GetKMSParameters returns a copy of CLIOptions parsed from the command line. +func (options *CLIOptions) GetKMSParameters() *kms.CLIOptions { + return &options.kmsOptions +} diff --git a/keystore/keyloader/kms/kms_cli.go b/keystore/keyloader/kms/kms_cli.go index 0c11681ee..b0868142d 100644 --- a/keystore/keyloader/kms/kms_cli.go +++ b/keystore/keyloader/kms/kms_cli.go @@ -1,15 +1,18 @@ package kms import ( + "errors" "flag" "fmt" - "github.com/cossacklabs/acra/keystore/kms" "strings" - "github.com/cossacklabs/acra/keystore/keyloader" + "github.com/cossacklabs/acra/keystore/kms" log "github.com/sirupsen/logrus" ) +// ErrUnknownKMSType error displaying unknown KMS type provided by flags +var ErrUnknownKMSType = errors.New("unknown KMS type provided") + // AcraMasterKeyKEKID represent ID/alias of encryption key used for MasterKey loading const AcraMasterKeyKEKID = "acra_master_key" @@ -31,9 +34,8 @@ var SupportedPolicies = []string{ // CLIOptions keep command-line options related to KMS ACRA_MASTER_KEY loading. type CLIOptions struct { - KMSType string - CredentialsPath string - KMSKeystoreEncryptor bool + KMSType string + CredentialsPath string } var cliOptions CLIOptions @@ -52,21 +54,11 @@ func (options *CLIOptions) RegisterCLIParameters(flags *flag.FlagSet, prefix str if flags.Lookup(prefix+"kms_type") == nil { flags.StringVar(&options.KMSType, prefix+"kms_type", "", fmt.Sprintf("KMS type for using: <%s>", strings.Join(supportedTypes, "|")+description)) flags.StringVar(&options.CredentialsPath, prefix+"kms_credentials_path", "", "KMS credentials JSON file path"+description) - flags.BoolVar(&options.KMSKeystoreEncryptor, "kms_keystore_encryptor", false, "Use KMS for keystore encryption") } } -// GetCLIParameters returns a copy of CLIOptions parsed from the command line. -func GetCLIParameters() *CLIOptions { - return &cliOptions -} - -// New create MasterKeyLoader from kms.CLIOptions - implementation of keyloader.CliMasterKeyLoaderCreator interface -func (options CLIOptions) New() (keyloader.MasterKeyLoader, error) { - if options.KMSType == "" { - return nil, nil - } - +// NewMasterKeyLoader create MasterKeyLoader from kms.CLIOptions +func (options *CLIOptions) NewMasterKeyLoader() (*Loader, error) { keyManager, err := options.NewKeyManager() if err != nil { return nil, err @@ -81,7 +73,7 @@ func (options *CLIOptions) NewKeyManager() (kms.KeyManager, error) { createKeyManager, ok := kms.GetKeyManagerCreator(options.KMSType) if !ok { log.Errorf("Unknown KMS type provided %s", options.KMSType) - return nil, nil + return nil, ErrUnknownKMSType } keyManager, err := createKeyManager(options.CredentialsPath) diff --git a/tests/test.py b/tests/test.py index 32e9a83ff..66e39baa7 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1254,6 +1254,7 @@ def test_generate_master_key_with_kms_create(self): [os.path.join(BINARY_OUTPUT_FOLDER, 'acra-keymaker'), '--keystore={}'.format(KEYSTORE_VERSION), '--generate_master_key={}'.format(master_key_file.name), '--kms_type=aws', + '--keystore_encryption_type=kms_master_key', '--kms_credentials_path={}'.format(self.config_file.name)]) resp = self.kms_client.list_aliases() @@ -1271,6 +1272,7 @@ def test_generate_master_key_with_kms_create(self): [os.path.join(BINARY_OUTPUT_FOLDER, 'acra-keymaker'), '--keystore={}'.format(KEYSTORE_VERSION), '--generate_master_key={}'.format(master_key_file.name), '--kms_type=aws', + '--keystore_encryption_type=kms_master_key', '--kms_credentials_path={}'.format(self.config_file.name)], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as exp: self.assertIn("alias/acra_master_key already exists", str(exp.output)) @@ -1374,10 +1376,11 @@ def put_master_key_by_version(self, path, version, mount_point=None): mount_point=mount_point, ) - def get_vault_cli_args(self, mount_path=None, secret_path=None): + def get_vault_cli_args(self, mount_path=None, secret_path=None, keystore_encryption_type='vault_master_key'): args = { 'vault_connection_api_string': self.vault_client.url, - 'vault_secrets_path': '{0}/{1}'.format(mount_path, secret_path) + 'vault_secrets_path': '{0}/{1}'.format(mount_path, secret_path), + 'keystore_encryption_type': keystore_encryption_type } if TEST_SSL_VAULT: @@ -2338,7 +2341,7 @@ def get_kms_configuration_path(self): return self.config_file.name -class KMSEncryptorMixin: +class KMSPerClientEncryptorMixin: # using local list of zones to store KMS created zones that can be used instead of global ZONES list ZONES = [] poison_record = None @@ -2350,6 +2353,7 @@ def setUp(self): 'kms_keystore_encryptor' : 'true', 'kms_type': self.get_kms_type(), 'kms_credentials_path': self.get_kms_configuration_path(), + 'keystore_encryption_type': "kms_per_client", } assert create_client_keypair_from_certificate(TEST_TLS_CLIENT_CERT, keys_dir=self.keys_dir, extra_kwargs=extra_args) == 0 assert create_client_keypair_from_certificate(TEST_TLS_CLIENT_2_CERT, keys_dir=self.keys_dir, extra_kwargs=extra_args) == 0 @@ -2358,12 +2362,12 @@ def setUp(self): self.ZONES.append(json.loads(subprocess.check_output( [os.path.join(BINARY_OUTPUT_FOLDER, 'acra-addzone'), '--keys_output_dir={}'.format(self.keys_dir), - '--kms_keystore_encryptor=true', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format(self.get_kms_configuration_path())], + '--keystore_encryption_type=kms_per_client', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format(self.get_kms_configuration_path())], cwd=os.getcwd(), timeout=PROCESS_CALL_TIMEOUT).decode('utf-8'))) self.ZONES.append(json.loads(subprocess.check_output( [os.path.join(BINARY_OUTPUT_FOLDER, 'acra-addzone'), '--keys_output_dir={}'.format(self.keys_dir), - '--kms_keystore_encryptor=true', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format(self.get_kms_configuration_path())], + '--keystore_encryption_type=kms_per_client', '--kms_type={}'.format(self.get_kms_type()), '--kms_credentials_path={}'.format(self.get_kms_configuration_path())], cwd=os.getcwd(), timeout=PROCESS_CALL_TIMEOUT).decode('utf-8'))) super().setUp() @@ -2373,21 +2377,21 @@ def get_poison_records(self): def fork_acra(self, popen_kwargs: dict=None, **acra_kwargs: dict): acra_kwargs['kms_type'] = self.get_kms_type() acra_kwargs['kms_credentials_path'] = self.get_kms_configuration_path() - acra_kwargs['kms_keystore_encryptor'] = 'true' + acra_kwargs['keystore_encryption_type'] = 'kms_per_client' acra_kwargs['keys_dir'] = self.keys_dir - return super(KMSEncryptorMixin, self).fork_acra(popen_kwargs, **acra_kwargs) + return super(KMSPerClientEncryptorMixin, self).fork_acra(popen_kwargs, **acra_kwargs) def fork_translator(self, translator_kwargs, popen_kwargs=None): args = { 'kms_type': self.get_kms_type(), 'kms_credentials_path': self.get_kms_configuration_path(), - 'kms_keystore_encryptor': 'true', + 'keystore_encryption_type': 'kms_per_client', 'keys_dir': self.keys_dir, 'logging_format': 'text', } translator_kwargs.update(args) - return super(KMSEncryptorMixin, self).fork_translator(translator_kwargs, popen_kwargs) + return super(KMSPerClientEncryptorMixin, self).fork_translator(translator_kwargs, popen_kwargs) class TestEnableCachedOnStartupTest(HexFormatTest): @@ -2418,7 +2422,7 @@ def testClientIDRead(self): super().testClientIDRead() -class TestEnableCachedOnStartupAWSKMSKeystore(TestEnableCachedOnStartupTest, KMSAWSType, KMSEncryptorMixin): +class TestEnableCachedOnStartupAWSKMSKeystore(TestEnableCachedOnStartupTest, KMSAWSType, KMSPerClientEncryptorMixin): # just passed test to check if cache on start is working with KMS def testReadAcrastructInAcrastruct(self): pass @@ -2892,7 +2896,7 @@ def testShutdownTranslatorgRPC(self): self.assertEqual(exc.exception.args[0], WAIT_CONNECTION_ERROR_MESSAGE) -class TestPoisonRecordShutdownWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixin, TestPoisonRecordShutdown): +class TestPoisonRecordShutdownWithAWSKMSKeystore(KMSAWSType, KMSPerClientEncryptorMixin, TestPoisonRecordShutdown): def get_poison_record_data(self): return self.get_poison_records() @@ -3249,11 +3253,11 @@ def fork_translator(self, translator_kwargs, popen_kwargs=None): return super().fork_translator(translator_kwargs, popen_kwargs) def read_rotation_public_key(self, extra_kwargs: dict = None): - args = self.vault_client.get_vault_cli_args(self.DEFAULT_MOUNT_PATH,self.secret_path) + args = self.vault_client.get_vault_cli_args(self.DEFAULT_MOUNT_PATH, self.secret_path, keystore_encryption_type='master_key') return super().read_rotation_public_key(extra_kwargs=args) def create_keypair(self, extra_kwargs: dict = None): - args = self.vault_client.get_vault_cli_args(self.DEFAULT_MOUNT_PATH,self.secret_path) + args = self.vault_client.get_vault_cli_args(self.DEFAULT_MOUNT_PATH, self.secret_path, keystore_encryption_type='master_key') return super().create_keypair(extra_kwargs=args) def tearDown(self): @@ -3292,7 +3296,8 @@ def create_configuration_file(self): def fork_acra(self, popen_kwargs: dict = None, **acra_kwargs: dict): args = { 'kms_credentials_path': self.config_file.name, - 'kms_type': 'aws' + 'kms_type': 'aws', + 'keystore_encryption_type': 'kms_master_key' } os.environ[ACRA_MASTER_KEY_VAR_NAME] = self.master_key_ciphertext acra_kwargs.update(args) @@ -3301,7 +3306,8 @@ def fork_acra(self, popen_kwargs: dict = None, **acra_kwargs: dict): def fork_translator(self, translator_kwargs, popen_kwargs=None): args = { 'kms_credentials_path': self.config_file, - 'kms_type': 'aws' + 'kms_type': 'aws', + 'keystore_encryption_type': 'kms_master_key' } os.environ[ACRA_MASTER_KEY_VAR_NAME] = self.master_key_ciphertext translator_kwargs.update(args) @@ -4883,7 +4889,7 @@ class TestTranslatorDisableCachedOnStartupWithAWSKMS(AWSKMSMasterKeyLoaderMixin, pass -class TestTranslatorDisableCachedOnStartupWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixin, TestTranslatorDisableCachedOnStartup): +class TestTranslatorDisableCachedOnStartupWithAWSKMSKeystore(KMSAWSType, KMSPerClientEncryptorMixin, TestTranslatorDisableCachedOnStartup): pass @@ -5947,7 +5953,7 @@ def update_data(self, context): self.executor2.execute_prepared_statement(query, parameters) -class TestPostgresqlBinaryPreparedTransparentEncryptionWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixin, TestPostgresqlBinaryPreparedTransparentEncryption): +class TestPostgresqlBinaryPreparedTransparentEncryptionWithAWSKMSKeystore(KMSAWSType, KMSPerClientEncryptorMixin, TestPostgresqlBinaryPreparedTransparentEncryption): pass @@ -7103,7 +7109,7 @@ class TestTransparentSearchableEncryptionWithZoneWithAWSKMSMasterKeyLoader(AWSKM pass -class TestTransparentSearchableEncryptionWithZoneWithAWSKMSKeystore(KMSAWSType, KMSEncryptorMixin, TestTransparentSearchableEncryptionWithZone): +class TestTransparentSearchableEncryptionWithZoneWithAWSKMSKeystore(KMSAWSType, KMSPerClientEncryptorMixin, TestTransparentSearchableEncryptionWithZone): pass From 6bd313bae1ff51e0c19349a1c4d9f5d02acd99aa Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Fri, 5 Aug 2022 16:28:54 +0200 Subject: [PATCH 11/13] zhars/use_kms_per_client Fixed unit tests --- cmd/acra-keys/keys/generate_test.go | 3 --- cmd/acra-keys/keys/read-keys_test.go | 18 ------------------ keystore/keyloader/keyloader.go | 9 +-------- keystore/kms/aws/client.go | 2 +- tests/test.py | 1 - 5 files changed, 2 insertions(+), 31 deletions(-) diff --git a/cmd/acra-keys/keys/generate_test.go b/cmd/acra-keys/keys/generate_test.go index fa8617929..884c90516 100644 --- a/cmd/acra-keys/keys/generate_test.go +++ b/cmd/acra-keys/keys/generate_test.go @@ -52,9 +52,6 @@ func TestRotateSymmetricZoneKey(t *testing.T) { generateCmd := &GenerateKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, - keyLoaderOptions: keyloader.CLIOptions{ - KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, - }, }, zoneID: zoneID, rotateZoneSym: true, diff --git a/cmd/acra-keys/keys/read-keys_test.go b/cmd/acra-keys/keys/read-keys_test.go index 3b3d35736..7288a0448 100644 --- a/cmd/acra-keys/keys/read-keys_test.go +++ b/cmd/acra-keys/keys/read-keys_test.go @@ -49,9 +49,6 @@ func TestReadCMD_FS_V2(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, - keyLoaderOptions: keyloader.CLIOptions{ - KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, - }, }, contextID: clientID, readKeyKind: KeyStoragePublic, @@ -74,9 +71,6 @@ func TestReadCMD_FS_V2(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, - keyLoaderOptions: keyloader.CLIOptions{ - KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, - }, }, contextID: clientID, readKeyKind: KeySymmetric, @@ -99,9 +93,6 @@ func TestReadCMD_FS_V2(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, - keyLoaderOptions: keyloader.CLIOptions{ - KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, - }, }, contextID: zoneID, readKeyKind: KeyZoneSymmetric, @@ -143,9 +134,6 @@ func TestReadCMD_FS_V1(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, - keyLoaderOptions: keyloader.CLIOptions{ - KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, - }, }, contextID: clientID, readKeyKind: KeyStoragePublic, @@ -168,9 +156,6 @@ func TestReadCMD_FS_V1(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, - keyLoaderOptions: keyloader.CLIOptions{ - KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, - }, }, contextID: clientID, readKeyKind: KeySymmetric, @@ -193,9 +178,6 @@ func TestReadCMD_FS_V1(t *testing.T) { readCmd := &ReadKeySubcommand{ CommonKeyStoreParameters: CommonKeyStoreParameters{ keyDir: dirName, - keyLoaderOptions: keyloader.CLIOptions{ - KeystoreEncryptorType: keyloader.KeystoreStrategyMasterKey, - }, }, contextID: zoneID, readKeyKind: KeyZoneSymmetric, diff --git a/keystore/keyloader/keyloader.go b/keystore/keyloader/keyloader.go index de7cb2a3c..dc7209256 100644 --- a/keystore/keyloader/keyloader.go +++ b/keystore/keyloader/keyloader.go @@ -1,14 +1,9 @@ package keyloader import ( - "errors" - "github.com/cossacklabs/acra/keystore" ) -// ErrUnsupportedMasterKeyLoaderStrategy error displaying unsupported MasterKeyLoader strategy -var ErrUnsupportedMasterKeyLoaderStrategy = errors.New("unsupported MasterKeyLoader strategy provided") - // MasterKeyLoader interface for loading ACRA_MASTER_KEYs from different sources. type MasterKeyLoader interface { LoadMasterKey() (key []byte, err error) @@ -35,9 +30,7 @@ func initMasterKeyLoaderWithEnv(envVarName string, loadStrategy string) (MasterK return cliParams.GetKMSParameters().NewMasterKeyLoader() case KeystoreStrategyHashicorpVaultMasterKey: return cliParams.GetVaultCLIParameters().NewMasterKeyLoader() - case KeystoreStrategyMasterKey: - return NewEnvLoader(envVarName), nil default: - return nil, ErrUnsupportedMasterKeyLoaderStrategy + return NewEnvLoader(envVarName), nil } } diff --git a/keystore/kms/aws/client.go b/keystore/kms/aws/client.go index 77fed3936..4f40ff3c6 100644 --- a/keystore/kms/aws/client.go +++ b/keystore/kms/aws/client.go @@ -106,7 +106,7 @@ func (e *KMSClient) ListAliases(ctx context.Context, keyID *string) ([]types.Ali input.KeyId = keyID } - aliases := make([]types.AliasListEntry, 0) + aliases := make([]types.AliasListEntry, 0, 8) for { result, err := e.client.ListAliases(ctx, input) if err != nil { diff --git a/tests/test.py b/tests/test.py index 66e39baa7..2c6aba2f2 100644 --- a/tests/test.py +++ b/tests/test.py @@ -2350,7 +2350,6 @@ def setUp(self): self.keys_dir = tempfile.TemporaryDirectory().name extra_args = { - 'kms_keystore_encryptor' : 'true', 'kms_type': self.get_kms_type(), 'kms_credentials_path': self.get_kms_configuration_path(), 'keystore_encryption_type': "kms_per_client", From e33262e3563614554188b955eb3dafccc5370e51 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Mon, 8 Aug 2022 09:37:55 +0200 Subject: [PATCH 12/13] zhars/use_kms_per_client Fixed tests/updated CHANGELOG file --- CHANGELOG_DEV.md | 1 + tests/test.py | 8 +------- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/CHANGELOG_DEV.md b/CHANGELOG_DEV.md index f3be2879f..2aa2dc3a0 100644 --- a/CHANGELOG_DEV.md +++ b/CHANGELOG_DEV.md @@ -1,6 +1,7 @@ # 0.94.0 - 2022-08-03 - Implement KMS Keystore encryptor - Extend `acra-keymaker`, `acra-addzone` and `acra-poisonrecord` tools with ability to create key encryption keys on KMS +- Introduce new flag `keystore_encryption_type` for all keystore related acra tools # 0.94.0 - 2022-07-08 - Extend encryptor config struct with database settings section diff --git a/tests/test.py b/tests/test.py index 2c6aba2f2..5d242b070 100644 --- a/tests/test.py +++ b/tests/test.py @@ -2401,27 +2401,21 @@ def checkSkip(self): self.skipTest("test only for keystore Version v1") def setUp(self): - self.cached_dir = tempfile.TemporaryDirectory() - # fill temp dir with all keys - copy_tree(KEYS_FOLDER.name, self.cached_dir.name) super().setUp() def fork_acra(self, popen_kwargs: dict=None, **acra_kwargs: dict): acra_kwargs['keystore_cache_on_start_enable'] = 'true' - acra_kwargs['keys_dir'] = self.cached_dir.name return super(TestEnableCachedOnStartupTest, self).fork_acra( popen_kwargs, **acra_kwargs) def testReadAcrastructInAcrastruct(self): - self.cached_dir.cleanup() super().testReadAcrastructInAcrastruct() def testClientIDRead(self): - self.cached_dir.cleanup() super().testClientIDRead() -class TestEnableCachedOnStartupAWSKMSKeystore(TestEnableCachedOnStartupTest, KMSAWSType, KMSPerClientEncryptorMixin): +class TestEnableCachedOnStartupAWSKMSKeystore(KMSAWSType, KMSPerClientEncryptorMixin, TestEnableCachedOnStartupTest): # just passed test to check if cache on start is working with KMS def testReadAcrastructInAcrastruct(self): pass From d40c0657ea3a87a6633eae233325e697d488f275 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Mon, 8 Aug 2022 12:30:45 +0200 Subject: [PATCH 13/13] zhars/use_kms_per_client Fixed after review --- cmd/acra-keys/keys/migrate-keys.go | 4 ++-- configs/acra-addzone.yaml | 2 +- configs/acra-backup.yaml | 2 +- configs/acra-keymaker.yaml | 2 +- configs/acra-keys.yaml | 6 +++--- configs/acra-poisonrecordmaker.yaml | 2 +- configs/acra-rollback.yaml | 2 +- configs/acra-rotate.yaml | 2 +- configs/acra-server.yaml | 2 +- configs/acra-translator.yaml | 2 +- keystore/keyloader/keyloader_cli.go | 2 +- keystore/keystore.go | 14 ++++++++++++++ tests/test.py | 8 ++++---- 13 files changed, 32 insertions(+), 18 deletions(-) diff --git a/cmd/acra-keys/keys/migrate-keys.go b/cmd/acra-keys/keys/migrate-keys.go index 3d4322e83..baf21b3ee 100644 --- a/cmd/acra-keys/keys/migrate-keys.go +++ b/cmd/acra-keys/keys/migrate-keys.go @@ -226,10 +226,10 @@ func MigrateV1toV2(srcV1 filesystem.KeyExport, dstV2 keystoreV2.KeyFileImportV1) log.Tracef("Importing %d keys from keystore v1", expected) for _, key := range keys { - log := log.WithField("purpose", key.KeyContext.Purpose).WithField("id", string(keystore.GetKeyContextFromContext(key.KeyContext))) err := dstV2.ImportKeyFileV1(srcV1, key) if err != nil { - log.WithError(err).Warn("Failed to import key") + log.WithField("purpose", key.KeyContext.Purpose).WithField("id", key.KeyContext).WithError(err). + Warn("Failed to import key") continue } actual++ diff --git a/configs/acra-addzone.yaml b/configs/acra-addzone.yaml index e0ed3b7c0..950f60a56 100644 --- a/configs/acra-addzone.yaml +++ b/configs/acra-addzone.yaml @@ -14,7 +14,7 @@ generate_markdown_args_table: false # Folder where will be saved generated zone keys keys_output_dir: .acrakeys -# Keystore encryptor strategy; :