Class: LogStash::Filters::CipherKms
- Inherits:
-
Base
- Object
- Base
- LogStash::Filters::CipherKms
- Defined in:
- lib/logstash/filters/cipher_kms.rb
Overview
This filter parses a source and apply a cipher or decipher before storing it in the target. It uses AWS KMS to generate the envelope key for ciphering/deciphering.
Constant Summary collapse
- KMS_RUBY_CIPHER_MAP =
Mapping between AWS KMS available ciphers and correlated Ruby ciphers
{"AES_128" => "AES-128-cbc", "AES_256" => "AES-256-cbc"}.freeze
Instance Method Summary collapse
- #cipher_process(key, data) ⇒ Object
- #cipher_set_required_params(key) ⇒ Object
- #decrypt(data, metadata) ⇒ Object
- #encrypt(data) ⇒ Object
- #filter(event) ⇒ Object
- #init_cipher ⇒ Object
- #register ⇒ Object
- #rotate_cipher_if_needed ⇒ Object
- #set_cipher_crypt_mode ⇒ Object
- #validate_config ⇒ Object
Instance Method Details
#cipher_process(key, data) ⇒ Object
210 211 212 213 214 |
# File 'lib/logstash/filters/cipher_kms.rb', line 210 def cipher_process(key, data) cipher_set_required_params(key) result = @cipher.update(data) + @cipher.final result end |
#cipher_set_required_params(key) ⇒ Object
205 206 207 208 |
# File 'lib/logstash/filters/cipher_kms.rb', line 205 def cipher_set_required_params(key) @cipher.key = key @cipher.iv = @random_iv end |
#decrypt(data, metadata) ⇒ Object
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/logstash/filters/cipher_kms.rb', line 186 def decrypt(data, ) kms_response = @kms.decrypt(ciphertext_blob: Base64.strict_decode64(), encryption_context: @encryption_context) data = Base64.strict_decode64(data) if @base64 @random_iv = data.byteslice(0, @iv_random_length) data = data.byteslice(@iv_random_length..data.length) result = cipher_process(kms_response.plaintext, data) result.force_encoding('utf-8') begin result = JSON.parse(result) rescue JSON::ParserError # ignored, return as is end result end |
#encrypt(data) ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/logstash/filters/cipher_kms.rb', line 167 def encrypt(data) @random_iv = OpenSSL::Random.random_bytes(@iv_random_length) kms_response = @kms.generate_data_key(key_id: @key_id, key_spec: @algorithm, encryption_context: @encryption_context) # => returns a ciphertext and a plaintext key begin data = JSON.generate(data) rescue JSON::GeneratorError # ignored, use as is end result = cipher_process(kms_response.plaintext, data) # Prepend padding and base64 encoding if configured result = @random_iv + result unless @random_iv.nil? result = Base64.strict_encode64(result).encode('utf-8') if @base64 = Base64.strict_encode64(kms_response.ciphertext_blob).encode('utf-8') [result, ] end |
#filter(event) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/logstash/filters/cipher_kms.rb', line 134 def filter(event) # If decrypt or encrypt fails, we keep it it intact. begin if event.get(@source).blank? @logger.debug("Event to filter, event 'source' field: " + @source + ' was nil or blank, doing nothing.') return end @logger.debug('Event to filter', event: event) data = event.get(@source) if @mode == 'encrypt' result, = encrypt(data) event.set(@crypt_metadata, ) elsif @mode == 'decrypt' result = decrypt(data, event.get(@crypt_metadata)) event.remove(@crypt_metadata) end @total_cipher_uses += 1 unless result.nil? event.set(@target, result) filter_matched(event) end rescue => e @logger.warn('Exception caught on cipher filter', event: event, error: e) # force a re-initialize on error to be safe init_cipher ensure rotate_cipher_if_needed end end |
#init_cipher ⇒ Object
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/logstash/filters/cipher_kms.rb', line 223 def init_cipher @logger.debug('Encryption Context: ' + @encryption_context.to_s, plugin: self.class.name) credentials = nil if !@access_key_id.blank? && !@secret_access_key.blank? credentials = Aws::Credentials.new(@access_key_id, @secret_access_key) @logger.debug('Using Static Credentials', plugin: self.class.name) elsif !@aws_shared_credentials_path.blank? || !@aws_profile.blank? credentials = Aws::SharedCredentials.new(path: @aws_shared_credentials_path, profile_name: @aws_profile) @logger.debug('Using Shared Credentials', plugin: self.class.name) elsif @aws_instance_profile credentials = Aws::InstanceProfileCredentials.new @logger.debug('Using Instance Profile Credentials', plugin: self.class.name) elsif @aws_ecs_credentials credentials = Aws::ECSCredentials.new @logger.debug('Using ECS Credentials', plugin: self.class.name) end @kms = Aws::KMS::Client.new(region: @region, credentials: credentials) @total_cipher_uses = 0 @cipher = OpenSSL::Cipher.new(KMS_RUBY_CIPHER_MAP[@algorithm]) set_cipher_crypt_mode @logger.debug('Cipher initialisation done', mode: @mode, iv_random_length: @iv_random_length, algorithm: @algorithm, base64: @base64, max_cipher_reuse: @max_cipher_reuse) end |
#register ⇒ Object
128 129 130 131 132 |
# File 'lib/logstash/filters/cipher_kms.rb', line 128 def register require 'base64' if @base64 validate_config init_cipher end |
#rotate_cipher_if_needed ⇒ Object
216 217 218 219 220 221 |
# File 'lib/logstash/filters/cipher_kms.rb', line 216 def rotate_cipher_if_needed if !@max_cipher_reuse.nil? && @total_cipher_uses >= @max_cipher_reuse @logger.debug('max_cipher_reuse[' + @max_cipher_reuse.to_s + '] reached, total_cipher_uses = ' + @total_cipher_uses.to_s) init_cipher end end |
#set_cipher_crypt_mode ⇒ Object
252 253 254 255 256 257 258 |
# File 'lib/logstash/filters/cipher_kms.rb', line 252 def set_cipher_crypt_mode if @mode == 'encrypt' @cipher.encrypt elsif @mode == 'decrypt' @cipher.decrypt end end |
#validate_config ⇒ Object
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/logstash/filters/cipher_kms.rb', line 260 def validate_config @encryption_context.each do |_, value| unless value.is_a?(String) raise LogStash::ConfigurationError, 'Values in encryption_context must be strings, aborting.' end end unless KMS_RUBY_CIPHER_MAP.key?(@algorithm) raise LogStash::ConfigurationError, 'You can only use one of the following algorithms: ' + KMS_RUBY_CIPHER_MAP.keys.to_s + ', aborting.' end unless @mode == 'encrypt' || @mode == 'decrypt' @logger.error('Invalid cipher mode. Valid values are \"encrypt\" or \"decrypt\"', mode: @mode) raise LogStash::ConfigurationError, 'Invalid cipher mode. Valid values are \"encrypt\" or \"decrypt\", aborting.' end true end |