class SymmetricEncryption::Keystore::Aws
Support AWS Key
Management Service (KMS)
Terms:
Aws Amazon Web Services. CMK Customer Master Key. Master key to encrypt and decrypt data, specifically the DEK in this case. Stored in AWS, cannot be exported. DEK Data Encryption Key. Key used to encrypt local data. Encrypted with the CMK and stored locally. KMS Key Management Service. For generating and storing the CMK. Used to encrypt and decrypt the DEK.
Recommended reading:
Concepts:
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html
Overview:
https://docs.aws.amazon.com/kms/latest/developerguide/overview.html
Process:
-
Create a customer master key (CMK) along with an alias for use by Symmetric Encryption.
- Note: CMK is region specific. - Stored exclusively in AWS KMS, cannot be exported.
-
Generate and encrypt a data encryption key (DEK).
- CMK is used to encrypt the DEK. - Encrypted DEK is stored locally. - Encrypted DEK is region specific. - DEK can be shared, but then must be re-encrypted in each region. - Shared DEK across regions for database replication. - List of regions to publish DEK to during generation / key-rotation. - DEK must be encrypted with CMK in each region consecutively.
Warning:
If access to the AWS KMS is ever lost, then it is not possible to decrypt any encrypted data. Examples: - Loss of access to AWS accounts. - Loss of region(s) in which master keys are stored.
Attributes
key_files[R]
master_key_alias[R]
region[R]
Public Class Methods
generate_data_key(cipher_name:, app_name:, environment:, key_path:, version: 0, regions: Utils::Aws::AWS_US_REGIONS, dek: nil, **_args)
click to toggle source
Returns [Hash] a new keystore configuration after generating the data key.
Increments the supplied version number by 1.
Sample Hash layout returned: {
cipher_name: aes-256-cbc, version: 8, keystore: :aws, master_key_alias: 'alias/symmetric-encryption/application/production', key_files: [ {region: blah1, file_name: "~/symmetric-encryption/application_production_blah1_v6.encrypted_key"}, {region: blah2, file_name: "~/symmetric-encryption/application_production_blah2_v6.encrypted_key"}, ], iv: 'T80pYzD0E6e/bJCdjZ6TiQ=='
}
# File lib/symmetric_encryption/keystore/aws.rb, line 73 def self.generate_data_key(cipher_name:, app_name:, environment:, key_path:, version: 0, regions: Utils::Aws::AWS_US_REGIONS, dek: nil, **_args) # TODO: Also support generating environment variables instead of files. version >= 255 ? (version = 1) : (version += 1) regions = Array(regions).dup master_key_alias = master_key_alias(app_name, environment) # File per region for holding the encrypted data key key_files = regions.collect do |region| file_name = "#{app_name}_#{environment}_#{region}_v#{version}.encrypted_key" {region: region, file_name: ::File.join(key_path, file_name)} end keystore = new(key_files: key_files, master_key_alias: master_key_alias) unless dek data_key = keystore.aws(regions.first).generate_data_key(cipher_name) dek = Key.new(key: data_key, cipher_name: cipher_name) end keystore.write(dek.key) { keystore: :aws, cipher_name: dek.cipher_name, version: version, master_key_alias: master_key_alias, key_files: key_files, iv: dek.iv } end
master_key_alias(app_name, environment)
click to toggle source
Alias pointing to the active version of the master key for that region.
# File lib/symmetric_encryption/keystore/aws.rb, line 109 def self.master_key_alias(app_name, environment) @master_key_alias ||= "alias/symmetric-encryption/#{app_name}/#{environment}" end
new(key_files:, master_key_alias:, region: nil, key_encrypting_key: nil)
click to toggle source
Stores the Encryption key in a file. Secures the Encryption key by encrypting it with a key encryption key.
# File lib/symmetric_encryption/keystore/aws.rb, line 115 def initialize(key_files:, master_key_alias:, region: nil, key_encrypting_key: nil) @key_files = key_files @master_key_alias = master_key_alias @region = region || ENV["AWS_REGION"] || ENV["AWS_DEFAULT_REGION"] || ::Aws.config[:region] if key_encrypting_key raise(SymmetricEncryption::ConfigError, "AWS KMS keystore encrypts the key itself, so does not support supplying a key_encrypting_key") end end
Public Instance Methods
aws(region)
click to toggle source
# File lib/symmetric_encryption/keystore/aws.rb, line 150 def aws(region) Utils::Aws.new(region: region, master_key_alias: master_key_alias) end
read()
click to toggle source
Reads the data key environment variable, if present, otherwise a file. Decrypts the key using the master key for this region.
# File lib/symmetric_encryption/keystore/aws.rb, line 127 def read key_file = key_files.find { |i| i[:region] == region } raise(SymmetricEncryption::ConfigError, "region: #{region} not available in the supplied key_files") unless key_file file_name = key_file[:file_name] encrypted_data_key = read_file_and_decode(file_name) aws(region).decrypt(encrypted_data_key) end
write(data_key)
click to toggle source
Encrypt and write the data key to the file for each region.
# File lib/symmetric_encryption/keystore/aws.rb, line 138 def write(data_key) key_files.each do |key_file| region = key_file[:region] file_name = key_file[:file_name] raise(ArgumentError, "region and file_name are mandatory for each key_file entry") unless region && file_name encrypted_data_key = aws(region).encrypt(data_key) write_encoded_to_file(file_name, encrypted_data_key) end end