class Chef::EncryptedAttribute

Main EncryptedAttribute class.

This class contains both static and instance level public methods. Internally, all work with {EncryptedMash} object instances.

# Class Methods

The *class methods* (or static methods) are normally used **from Chef cookbooks**.

The attributes create with the class methods are encrypted **only for the local node** by default.

The static `*_on_node` methods can be used, although they have not been designed for this purpose (have not been tested).

They are # documented in the {Chef::EncryptedAttribute::API} class.

# Instance Methods

The *instance methods* are normally used **by other libraries or gems**. For example, the knife extensions included in this gem uses these methods.

The instance methods will grant encrypted attribute access **only to the remote node** by default.

Usually only the `_from_node/_on_node` instance methods will be used.

@see EncryptedAttribute::API

Constants

VERSION

`chef-encrypted-attributes` gem version.

Public Class Methods

new(c = nil) click to toggle source

Chef::EncryptedAttribute constructor.

@param c [Config, Hash] configuration to use.

# File lib/chef/encrypted_attribute.rb, line 76
def initialize(c = nil)
  config(c)
end

Public Instance Methods

config(arg = nil) click to toggle source

Sets or gets the encrypted attribute configuration.

Reads the default configuration from `Chef::Config`.

When setting using a {Chef::EncryptedAttribute::Config} class, all the configuration options will be replaced.

When setting using a Hash, only the provided keys will be replaced.

@param arg [Config, Hash] the configuration to set. @return [Config] the read or set configuration object.

# File lib/chef/encrypted_attribute.rb, line 92
def config(arg = nil)
  @config ||= EncryptedAttribute::Config.new(
    Chef::Config[:encrypted_attributes]
  )
  @config.update!(arg) unless arg.nil?
  @config
end
create(value, keys = nil) click to toggle source

Creates an encrypted attribute from a Hash.

Only the **keys passed as parameter and the configured keys** will be able to decrypt the attribute, so beware of including your local key if you need to decrypt it in the future.

@param value [Hash, Array, String, Fixnum, …] the value to encrypt in

clear.

@param keys [String, OpenSSL::PKey::RSA] public keys that will be able to

decrypt the attribute.

@raise [ArgumentError] if user list is wrong. @return [EncryptedMash] encrypted attribute value. This is usually what is

saved in the node attributes.

@raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute

format is wrong or does not exist.

@raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute

format is not supported or unknown.

@raise [EncryptionFailure] if there are encryption errors. @raise [MessageAuthenticationFailure] if HMAC calculation error. @raise [InvalidPublicKey] if it is not a valid RSA public key. @raise [InvalidKey] if the RSA key format is wrong. @raise [InsufficientPrivileges] if you lack enough privileges to read

the keys from the Chef Server.

@raise [ClientNotFound] if client does not exist. @raise [Net::HTTPServerException] for Chef Server HTTP errors. @raise [RequirementsFailure] if the specified encrypted attribute

version cannot be used.

@raise [SearchFailure] if there is a Chef search error. @raise [SearchFatalError] if the Chef search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong.

# File lib/chef/encrypted_attribute.rb, line 170
def create(value, keys = nil)
  decrypted = { 'content' => value }

  enc_attr = EncryptedMash.create(config.version)
  enc_attr.encrypt(decrypted, target_keys(keys))
end
create_on_node(name, attr_ary, value) click to toggle source

Creates an encrypted attribute on a remote node.

The remote node will always be able to decrypt it. The local node will not be able to decrypt it by default, you must remember to include the key in the configuration.

@param name [String] node name. @param attr_ary [Array<String>] node attribute path as Array. @param value [Hash, Array, String, Fixnum, …] the value to encrypt. @return [EncryptedMash] encrypted attribute value. @raise [ArgumentError] if the attribute path format or the user list is

wrong.

@raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute

format is wrong or does not exist.

@raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute

format is not supported or unknown.

@raise [EncryptionFailure] if there are encryption errors. @raise [MessageAuthenticationFailure] if HMAC calculation error. @raise [InvalidPublicKey] if it is not a valid RSA public key. @raise [InvalidKey] if the RSA key format is wrong. @raise [InsufficientPrivileges] if you lack enough privileges to read

the keys from the Chef Server.

@raise [ClientNotFound] if client does not exist. @raise [Net::HTTPServerException] for Chef Server HTTP errors. @raise [RequirementsFailure] if the specified encrypted attribute

version cannot be used.

@raise [SearchFailure] if there is a Chef search error. @raise [SearchFatalError] if the Chef search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong.

# File lib/chef/encrypted_attribute.rb, line 206
def create_on_node(name, attr_ary, value)
  # read the client public key
  node_public_key = RemoteClients.get_public_key(name)

  # create the encrypted attribute
  enc_attr = create(value, [node_public_key])

  # save encrypted attribute
  remote_node = RemoteNode.new(name)
  remote_node.save_attribute(attr_ary, enc_attr)
end
load(enc_hs, key = nil) click to toggle source

Decrypts an encrypted attribute from a local node attribute.

@param enc_hs [Mash] the encrypted hash as read from the node attributes. @param key [String, OpenSSL::PKey::RSA] private key to use in the

decryption process, uses the local node key by default.

@return [Hash, Array, String, …] decrypted attribute value. @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute

format is wrong.

@raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute

format is not supported or unknown.
# File lib/chef/encrypted_attribute.rb, line 110
def load(enc_hs, key = nil)
  enc_attr = EncryptedMash.json_create(enc_hs)
  decrypted = enc_attr.decrypt(key || local_key)
  decrypted['content'] # TODO: check this Hash
end
load_from_node(name, attr_ary, key = nil) click to toggle source

Decrypts a encrypted attribute from a remote node.

@param name [String] node name. @param attr_ary [Array<String>] node attribute path as Array. @param key [String, OpenSSL::PKey::RSA] private key to use in the

decryption process, uses the local key by default.

@return [Hash, Array, String, …] decrypted attribute value. @raise [ArgumentError] if the attribute path format is wrong. @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute

format is wrong.

@raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute

format is not supported or unknown.

@raise [SearchFailure] if there is a Chef search error. @raise [SearchFatalError] if the Chef search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong.

# File lib/chef/encrypted_attribute.rb, line 131
def load_from_node(name, attr_ary, key = nil)
  remote_node = RemoteNode.new(name)
  enc_hs =
    remote_node.load_attribute(
      attr_ary, config.search_max_rows, config.partial_search
    )
  load(enc_hs, key)
end
update(enc_hs, keys = nil) click to toggle source

Updates the keys for which a local attribute is encrypted.

In case new keys are added or some keys are removed, the attribute will be re-created again.

Only the **keys passed as parameter and the configured keys** will be able to decrypt the attribute, so beware of including your local key if you need to decrypt it in the future.

Uses the local key to decrypt the attribute, so the local key should be able to read the attribute. At least before updating.

@param enc_hs [Mash] encrypted attribute. This parameter value will be

modified on updates.

@param keys [Array<String, OpenSSL::PKey::RSA> public keys that should be

able to read the attribute.

@return [Boolean] Returns `true` if the encrypted attribute (the Mash

parameter) has been updated.

@raise [ArgumentError] if user list is wrong. @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute

format is wrong or does not exist.

@raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute

format is not supported or unknown.

@raise [EncryptionFailure] if there are encryption errors. @raise [MessageAuthenticationFailure] if HMAC calculation error. @raise [InvalidPublicKey] if it is not a valid RSA public key. @raise [InvalidKey] if the RSA key format is wrong. @raise [InsufficientPrivileges] if you lack enough privileges to read

the keys from the Chef Server.

@raise [ClientNotFound] if client does not exist. @raise [Net::HTTPServerException] for Chef Server HTTP errors. @raise [RequirementsFailure] if the specified encrypted attribute

version cannot be used.

@raise [SearchFailure] if there is a Chef search error. @raise [SearchFatalError] if the Chef search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong. @see config

# File lib/chef/encrypted_attribute.rb, line 255
def update(enc_hs, keys = nil)
  old_enc_attr = EncryptedMash.json_create(enc_hs)
  if old_enc_attr.needs_update?(target_keys(keys))
    hs = old_enc_attr.decrypt(local_key)
    new_enc_attr = create(hs['content'], keys) # TODO: check this Hash
    enc_hs.replace(new_enc_attr)
    true
  else
    false
  end
end
update_on_node(name, attr_ary) click to toggle source

Updates the keys for which a remote attribute is encrypted.

In case new keys are added or some keys are removed, the attribute will be re-created again.

Only the **remote node and the configured keys** will be able to decrypt the attribute, so beware of including your local key if you need to decrypt it in the future.

Uses the local key to decrypt the attribute, so the local key should be able to read the attribute. At least before updating.

@param name [String] node name. @param attr_ary [Array<String>] node attribute path as Array. @return [Boolean] Returns `true` if the remote encrypted attribute has

been updated.

@raise [ArgumentError] if the attribute path format or the user list is

wrong.

@raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute

format is wrong or does not exist.

@raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute

format is not supported or unknown.

@raise [EncryptionFailure] if there are encryption errors. @raise [MessageAuthenticationFailure] if HMAC calculation error. @raise [InvalidPublicKey] if it is not a valid RSA public key. @raise [InvalidKey] if the RSA key format is wrong. @raise [InsufficientPrivileges] if you lack enough privileges to read

the keys from the Chef Server.

@raise [ClientNotFound] if client does not exist. @raise [Net::HTTPServerException] for Chef Server HTTP errors. @raise [RequirementsFailure] if the specified encrypted attribute

version cannot be used.

@raise [SearchFailure] if there is a Chef search error. @raise [SearchFatalError] if the Chef search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong. @see config

# File lib/chef/encrypted_attribute.rb, line 303
def update_on_node(name, attr_ary)
  # read the client public key
  node_public_key = RemoteClients.get_public_key(name)

  # update the encrypted attribute
  remote_node = RemoteNode.new(name)
  enc_hs =
    remote_node.load_attribute(
      attr_ary, config.search_max_rows, config.partial_search
    )
  updated = update(enc_hs, [node_public_key])

  # save encrypted attribute
  if updated
    # TODO: Node is accessed twice (RemoteNode#load_attribute above)
    remote_node.save_attribute(attr_ary, enc_hs)
  end
  updated
end

Protected Instance Methods

local_key() click to toggle source

Gets the local private key.

@return [OpenSSL::PKey::RSA.new] local private (and public) key object.

# File lib/chef/encrypted_attribute.rb, line 399
def local_key
  LocalNode.new.key
end
remote_client_keys() click to toggle source

Gets remote client public keys using the *client search* query included in the configuration.

@return [Array<String>] list of client public keys. @raise [ClientNotFound] if client does not exist. @raise [Net::HTTPServerException] for Chef Server HTTP errors. @raise [SearchFailure] if there is a Chef search error. @raise [SearchFatalError] if the Chef search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong. @see config

# File lib/chef/encrypted_attribute.rb, line 335
def remote_client_keys
  RemoteClients.search_public_keys(
    config.client_search, config.search_max_rows, config.partial_search
  )
end
remote_node_keys() click to toggle source

Gets remote node public keys using the *node search* query included in the configuration.

@return [Array<String>] list of node public keys. @raise [InsufficientPrivileges] if you lack enough privileges to read

the keys from the Chef Server.

@raise [ClientNotFound] if client does not exist. @raise [Net::HTTPServerException] for Chef Server HTTP errors. @raise [SearchFailure] if there is a Chef search error. @raise [SearchFatalError] if the Chef search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong. @see config

# File lib/chef/encrypted_attribute.rb, line 353
def remote_node_keys
  RemoteNodes.search_public_keys(
    config.node_search, config.search_max_rows, config.partial_search
  )
end
remote_user_keys() click to toggle source

Gets remote user keys using the configured user list.

@return [Array<String>] list of user public keys. @raise [ArgumentError] if user list is wrong. @see config

# File lib/chef/encrypted_attribute.rb, line 364
def remote_user_keys
  RemoteUsers.get_public_keys(config.users)
end
target_keys(keys = nil) click to toggle source

Gets the public keys that should be able to read the attribute based on the configuration.

This includes keys passed as parameter, configured keys, remote_client_keys, remote_node_keys and remote_user_keys.

@param keys [Array<String>] list of public keys to include in addition to

the configured.

@return [Array<String>] list of user public keys. @raise [ArgumentError] if user list is wrong. @raise [InsufficientPrivileges] if you lack enough privileges to read

the keys from the Chef Server.

@raise [ClientNotFound] if client does not exist. @raise [Net::HTTPServerException] for Chef Server HTTP errors. @raise [SearchFailure] if there is a Chef search error. @raise [SearchFatalError] if the Chef search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong. @see config @see remote_client_keys @see remote_node_keys @see remote_user_keys

# File lib/chef/encrypted_attribute.rb, line 389
def target_keys(keys = nil)
  target_keys =
    config.keys + remote_client_keys + remote_node_keys + remote_user_keys
  target_keys += keys if keys.is_a?(Array)
  target_keys
end