module Vault::Transit

Constants

DEFAULT_ENCODING

The default encoding.

@return [String]

DEV_WARNING

The warning string to print when running in development mode.

VERSION

Attributes

client[R]

Public Class Methods

decrypt(key, ciphertext, client = self.client) click to toggle source

Decrypt the given ciphertext data using the provided key.

@param [String] key

the key to decrypt at

@param [String] ciphertext

the ciphertext to decrypt

@param [Vault::Client] client

the Vault client to use

@return [String]

the decrypted plaintext text
# File lib/vault/transit.rb, line 60
def decrypt(key, ciphertext, client = self.client)
  if ciphertext.nil? || ciphertext.empty?
    return ciphertext
  end

  key  = key.to_s if !key.is_a?(String)

  with_retries_and_reauthentication do
    if self.enabled?
      result = self.vault_decrypt(key, ciphertext, client)
    else
      result = self.memory_decrypt(key, ciphertext, client)
    end

    return self.force_encoding(result)
  end
end
encrypt(key, plaintext, client = self.client) click to toggle source

Encrypt the given plaintext data using the provided key.

@param [String] key

the key to encrypt at

@param [String] plaintext

the plaintext to encrypt

@param [Vault::Client] client

the Vault client to use

@return [String]

the encrypted cipher text
# File lib/vault/transit.rb, line 89
def encrypt(key, plaintext, client = self.client)
  if plaintext.nil? || plaintext.empty?
    return plaintext
  end

  key  = key.to_s if !key.is_a?(String)

  with_retries_and_reauthentication do
    if self.enabled?
      result = self.vault_encrypt(key, plaintext, client)
    else
      result = self.memory_encrypt(key, plaintext, client)
    end

    return self.force_encoding(result)
  end
end
method_missing(m, *args, &block) click to toggle source

Delegate all methods to the client object, essentially making the module object behave like a {Vault::Client}.

Calls superclass method
# File lib/vault/transit.rb, line 36
def method_missing(m, *args, &block)
  if client.respond_to?(m)
    client.public_send(m, *args, &block)
  else
    super
  end
end
respond_to_missing?(m, include_private = false) click to toggle source

Delegating `respond_to` to the {Vault::Client}.

Calls superclass method
# File lib/vault/transit.rb, line 45
def respond_to_missing?(m, include_private = false)
  client.respond_to?(m, include_private) || super
end
rewrap(key, ciphertext, client = self.client) click to toggle source

Rewrap the given ciphertext data using the provided key.

@param [String] key

the key to rewrap at

@param [String] ciphertext

the ciphertext to rewrap

@param [Vault::Client] client

the Vault client to use

@return [String]

the rewrapped ciphertext text
# File lib/vault/transit.rb, line 118
def rewrap(key, ciphertext, client = self.client)
  if ciphertext.nil? || ciphertext.empty?
    return ciphertext
  end

  key  = key.to_s unless key.is_a?(String)
  route  = File.join("transit", "rewrap", key)

  with_retries_and_reauthentication do
    if self.enabled?
      secret = client.logical.write(route,
        ciphertext: ciphertext,
      )
      result = secret.data[:ciphertext]
    else
      result = ciphertext
    end
    return self.force_encoding(result)
  end
end
rotate(key, client = self.client) click to toggle source

Rotate the key to a new version

@param [String] key

the key to rotate

@param [Vault::Client] client

the Vault client to use
# File lib/vault/transit.rb, line 146
def rotate(key, client = self.client)
  key  = key.to_s unless key.is_a?(String)
  route  = File.join("transit", "keys", key, "rotate")

  with_retries_and_reauthentication do
    if self.enabled?
      client.logical.write(route)
    end
  end
end
set_min_decryption_version(key, min_decryption_version, client = self.client) click to toggle source

Set the minimum decryption version a using the provided key.

@param [String] key

the key to configure

@param [int] min_decryption_version

the new minimum decryption version

@param [Vault::Client] client

the Vault client to use
# File lib/vault/transit.rb, line 166
def set_min_decryption_version(key, min_decryption_version, client = self.client)
  key  = key.to_s unless key.is_a?(String)

  with_retries_and_reauthentication do
    if self.enabled?
      route = File.join("transit", "keys", key, "config")
      client.logical.write(route,
        min_decryption_version: min_decryption_version,
      )
    end
  end
end
setup!() click to toggle source
# File lib/vault/transit.rb, line 24
def setup!
  ::Vault.setup!
  @client = ::Vault.client
  @client.class.instance_eval do
    include ::Vault::Transit::Configurable
  end

  self
end

Protected Class Methods

force_encoding(str) click to toggle source

Forces the encoding into the default Rails encoding and returns the newly encoded string. @return [String]

# File lib/vault/transit.rb, line 237
def force_encoding(str)
  encoding = ::Rails.application.config.encoding if defined? ::Rails
  encoding ||= DEFAULT_ENCODING
  str.force_encoding(encoding).encode(encoding)
end
memory_decrypt(key, ciphertext, client) click to toggle source

Perform in-memory decryption. This is useful for testing and development.

# File lib/vault/transit.rb, line 182
def memory_decrypt(key, ciphertext, client)
  log_warning(DEV_WARNING)

  return nil if ciphertext.nil?

  cipher = OpenSSL::Cipher::AES.new(128, :CBC)
  cipher.decrypt
  cipher.key = memory_key_for(key)
  ciphertext = ciphertext.gsub("vault:v0:", "")
  return cipher.update(Base64.strict_decode64(ciphertext)) + cipher.final
end
memory_encrypt(key, plaintext, client) click to toggle source

Perform in-memory encryption. This is useful for testing and development.

# File lib/vault/transit.rb, line 195
def memory_encrypt(key, plaintext, client)
  log_warning(DEV_WARNING)

  return nil if plaintext.nil?

  cipher = OpenSSL::Cipher::AES.new(128, :CBC)
  cipher.encrypt
  cipher.key = memory_key_for(key)
  return "vault:v0:" + Base64.strict_encode64(cipher.update(plaintext) + cipher.final)
end
memory_key_for(key) click to toggle source

The symmetric key for the given params. @return [String]

# File lib/vault/transit.rb, line 230
def memory_key_for(key)
  return Base64.strict_encode64(key.ljust(32, "x"))
end
vault_decrypt(key, ciphertext, client) click to toggle source

Perform decryption using Vault. This will raise exceptions if Vault is unavailable.

# File lib/vault/transit.rb, line 208
def vault_decrypt(key, ciphertext, client)
  return nil if ciphertext.nil?

  route  = File.join("transit", "decrypt", key)
  secret = client.logical.write(route, ciphertext: ciphertext)
  return Base64.strict_decode64(secret.data[:plaintext])
end
vault_encrypt(key, plaintext, client) click to toggle source

Perform encryption using Vault. This will raise exceptions if Vault is unavailable.

# File lib/vault/transit.rb, line 218
def vault_encrypt(key, plaintext, client)
  return nil if plaintext.nil?

  route  = File.join("transit", "encrypt", key)
  secret = client.logical.write(route,
    plaintext: Base64.strict_encode64(plaintext),
  )
  return secret.data[:ciphertext]
end

Private Class Methods

log_warning(msg) click to toggle source
# File lib/vault/transit.rb, line 245
def log_warning(msg)
  if defined?(::Rails) && ::Rails.logger != nil
    ::Rails.logger.warn { msg }
  end
end
permission_denied?(error) click to toggle source
# File lib/vault/transit.rb, line 251
def permission_denied?(error)
  error.errors.include? "permission denied"
end
reauthenticate!() click to toggle source
# File lib/vault/transit.rb, line 255
def reauthenticate!
  return nil unless ENV["VAULT_APP_ID"] && ENV["VAULT_SYSTEM_ID"]
  secret = ::Vault::Transit.auth.app_id(ENV["VAULT_APP_ID"], ENV["VAULT_SYSTEM_ID"])
  ::Vault::Transit.token = secret.auth.client_token
end
with_reauthentication(client = self.client) { || ... } click to toggle source
# File lib/vault/transit.rb, line 261
def with_reauthentication(client = self.client, &block)
  retries ||= 0
  reauthenticate! if self.enabled? && self.token.nil?
  yield
rescue ::Vault::HTTPError => error
  raise unless permission_denied?(error)

  reauthenticate!
  retry if (retries += 1) < 2
  raise
end
with_retries(client = self.client) { || ... } click to toggle source
# File lib/vault/transit.rb, line 273
def with_retries(client = self.client, &block)
  exceptions = [Vault::HTTPConnectionError, Vault::HTTPServerError]
  options = {
    attempts: self.retry_attempts,
    base:     self.retry_base,
    max_wait: self.retry_max_wait,
  }

  client.with_retries(*exceptions, options) do |i, e|
    if !e.nil?
      log_warning "[vault-transit] (#{i}) An error occurred when trying to " \
        "communicate with Vault: #{e.message}"
    end

    yield
  end
end
with_retries_and_reauthentication(client = self.client) { || ... } click to toggle source
# File lib/vault/transit.rb, line 291
def with_retries_and_reauthentication(client = self.client, &block)
  with_reauthentication do
    with_retries do
      yield
    end
  end
end