New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement KMS AWS master key loading #552
Merged
Merged
Changes from 2 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
7020be7
zhars/implement_kms_aws_master_key_loading
Zhaars cdd0c52
zhars/implement_kms_aws_master_key_loading
Zhaars a05c993
zhars/implement_kms_aws_master_key_loading
Zhaars 0518f9c
zhars/implement_kms_aws_master_key_loading
Zhaars f17fc2f
zhars/implement_kms_aws_master_key_loading
Zhaars 5cc8f21
zhars/implement_kms_aws_master_key_loading
Zhaars File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package kms | ||
|
||
import ( | ||
"flag" | ||
|
||
"github.com/cossacklabs/acra/keystore/keyloader" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
const ( | ||
kmsKeyURIFlag = "master_key_encryption_key_uri" | ||
) | ||
|
||
// CLIOptions keep command-line options related to KMS ACRA_MASTER_KEY loading. | ||
type CLIOptions struct { | ||
KeyIdentifierURI string | ||
CredentialsPath 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+kmsKeyURIFlag) == nil { | ||
flags.StringVar(&options.KeyIdentifierURI, prefix+kmsKeyURIFlag, "", "KMS Key identifier in Tink's format"+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) | ||
} | ||
} | ||
|
||
// 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.KeyIdentifierURI == "" { | ||
return nil, nil | ||
} | ||
|
||
log.Infoln("Using KMS for ACRA_MASTER_KEY loading...") | ||
return NewLoader(options.CredentialsPath, options.KeyIdentifierURI) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package kms | ||
|
||
import ( | ||
"crypto/subtle" | ||
|
||
"github.com/cossacklabs/acra/keystore" | ||
keystoreCE "github.com/cossacklabs/acra/keystore" | ||
"github.com/cossacklabs/acra/keystore/kms" | ||
"github.com/cossacklabs/acra/keystore/kms/aws" | ||
keystoreV2CE "github.com/cossacklabs/acra/keystore/v2/keystore" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
// Loader is implementation of MasterKeyLoader for kms | ||
type Loader struct { | ||
keyID kms.KeyIdentifier | ||
encryptor kms.Encryptor | ||
} | ||
|
||
// NewLoader create new kms MasterKeyLoader | ||
func NewLoader(credentialPath, keyIdentifierURI string) (*Loader, error) { | ||
keyID, err := kms.NewKeyIdentifierFromURI(keyIdentifierURI) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var encryptor kms.Encryptor | ||
switch keyID.Prefix() { | ||
case aws.KeyIdentifierPrefix: | ||
encryptor, err = aws.NewEncryptor(credentialPath) | ||
} | ||
if err != nil { | ||
log.WithError(err).Errorf("Failed to initialize %s MasterKeyLoader", encryptor.Source()) | ||
return nil, err | ||
} | ||
|
||
log.Infof("Initialized %s MasterKeyLoader", encryptor.Source()) | ||
return &Loader{ | ||
keyID: keyID, | ||
encryptor: encryptor, | ||
}, nil | ||
} | ||
|
||
// LoadMasterKey implementation kms MasterKeyLoader for loading AcraMasterKey for keystore v1 | ||
func (loader *Loader) LoadMasterKey() ([]byte, error) { | ||
rawKey, err := loader.decryptWithKMSKey(loader.keyID) | ||
if err != nil { | ||
log.WithError(err).Warnf("Failed to decrypt ACRA_MASTER_KEY with KMS keyID %s", loader.keyID.ID()) | ||
return nil, err | ||
} | ||
|
||
if err := keystoreCE.ValidateMasterKey(rawKey); err != nil { | ||
log.WithError(err).Warn("Decrypted key is invalid") | ||
return nil, err | ||
} | ||
|
||
return rawKey, nil | ||
} | ||
|
||
// LoadMasterKeys implementation kms MasterKeyLoader for loading AcraMasterKey for keystore v2 | ||
func (loader *Loader) LoadMasterKeys() (encryption []byte, signature []byte, err error) { | ||
rawKey, err := loader.decryptWithKMSKey(loader.keyID) | ||
if err != nil { | ||
log.WithError(err).Warnf("Failed to decrypt ACRA_MASTER_KEY with KMS keyID %s", loader.keyID.ID()) | ||
return nil, nil, err | ||
} | ||
|
||
keys := &keystoreV2CE.SerializedKeys{} | ||
err = keys.Unmarshal(rawKey) | ||
if err != nil { | ||
log.WithError(err).Warn("Failed to parse KMS decrypted key as SerializedKeys") | ||
return nil, nil, err | ||
} | ||
|
||
if subtle.ConstantTimeCompare(keys.Encryption, keys.Signature) == 1 { | ||
log.Warn("ACRA_MASTER_KEYs must not be the same") | ||
return nil, nil, keystoreV2CE.ErrEqualMasterKeys | ||
} | ||
|
||
err = keystoreCE.ValidateMasterKey(keys.Encryption) | ||
if err != nil { | ||
log.WithError(err).Warn("Invalid encryption key") | ||
return nil, nil, err | ||
} | ||
err = keystoreCE.ValidateMasterKey(keys.Signature) | ||
if err != nil { | ||
log.WithError(err).Warn("Invalid signature key") | ||
return nil, nil, err | ||
} | ||
|
||
return keys.Encryption, keys.Signature, nil | ||
} | ||
|
||
func (loader *Loader) decryptWithKMSKey(keyID kms.KeyIdentifier) ([]byte, error) { | ||
cipherMasterKey, err := keystore.GetMasterKeyFromEnvironmentVariable(keystore.AcraMasterKeyVarName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
masterKey, err := loader.encryptor.Decrypt(keyID.ID(), cipherMasterKey) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return masterKey, nil | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm, it push me to think about registries. what about to use here registries approach as we did it for CryptoHandlers? Turn implementation on with build tag and register it at start? for example:
Then we can use :
Then our AWS implementation should have:
With such approach we can extend with enterprise KMS support.
Additionally, we can make default loader that do nothing with master key (current approach where env variable store result master key. this implementation will return master key as is in Encrypt/Decrypt methods) and registered by default and +1 with AWS. Then current loader will be consistent with new approach
And +1 pros for that it be able to cut AWS support and all dependencies via build tags even in CE version. If client doesn't need AWS KMS support, it can compile it with build tag that turns off it. Result binary size and amount of dependencies will be less.