Module: Chef::EncryptedAttribute::API

Included in:
Chef::EncryptedAttribute
Defined in:
lib/chef/encrypted_attribute/api.rb

Overview

Main EncryptedAttribute class methods API module.

All these methods are available as static methods in the Chef::EncryptedAttribute class.

These methods are intended to be used from Chef Recipes or Resources.

The attributes created by these 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).

This module uses the Chef::EncryptedAttribute instance methods internally.

Configuration

All the methods read the default configuration from the Chef::Config[:encrypted_attributes] hash. Most of methods also support setting some configuration parameters as last argument. Both the global and the method argument configuration will be merged.

If the configuration value to be merged is an array or a hash (for example keys), the method argument configuration value has preference over the global configuration. arrays and hashes are not merged.

Both Chef::Config[:encrypted_attributes] and method's config parameter should be a hash which may have any of the following keys:

  • :version - EncryptedMash format version to use, by default 1 is used which is recommended. The version 2 uses GCM and probably should be considered the most secure, but it is disabled by default because it has some more requirements: Ruby >= 2 and OpenSSL >= 1.0.1.
  • :partial_search - Whether to use Chef Server partial search, enabled by default. It may not work in some old versions of Chef Server.
  • :client_search - Search query for clients allowed to read the encrypted attribute. Can be a simple string or an array of queries to be OR-ed.
  • :node_search - Search query for nodes allowed to read the encrypted attribute. Can be a simple string or an array of queries to be OR-ed.
  • :search_max_rows - Maximum nodes returned by the internal chef searches. This number should be above the maximum expected nodes in the Chef Server. Defaults to 1000 nodes.
  • :users - Array of user names to be allowed to read the encrypted attribute(s). "*" to allow access to all users. Keep in mind that only admin clients or admin users are allowed to read user public keys. It is not recommended to use this from cookbooks unless you know what you are doing.
  • :keys - raw RSA public keys to be allowed to read encrypted attributes(s), in PEM (string) format. Can be client public keys, user public keys or any other RSA public key.

For example, to disable Partial Search globally:

Chef::Config[:encrypted_attributes][:partial_search] = false

# ftp_pass = Chef::EncryptedAttribute.load(node['myapp']['ftp_password'])
# ...

To disable Partial Search locally:

ftp_pass = Chef::EncryptedAttribute.load(
  node['myapp']['ftp_password'], :partial_search => false
)

To use protocol version 2 globally, which uses GCM:

Chef::Config[:encrypted_attributes][:version] = 2
# ...

If you want to use knife to work with encrypted attributes, surely you will need to save your Chef User public keys in a Data Bag (there is no need to encrypt them because they are public) and add them to the :keys configuration option. See the Example Using User Keys Data Bag in the README for more information on this.

Caches

This API uses some LRU caches to avoid making many requests to the Chef Server. All the caches are global and has the following methods:

  • max_size - Gets or sets the cache maximum item size.
  • clear - To empty the cache.
  • [] - To read a cache value (used internally).
  • []= - To set a cache value (used internally).

This are the currently available caches:

  • Chef::EncryptedAttribute::RemoteClients.cache - Caches the :client_search query results (max_size: 1024).
  • Chef::EncryptedAttribute::RemoteNodes.cache - Caches the :node_search query results (max_size: 1024).
  • Chef::EncryptedAttribute::RemoteUsers.cache - Caches the Chef Users public keys (max_size: 1024).
  • Chef::EncryptedAttribute::RemoteNode.cache - Caches the node (encrypted) attributes. Disabled by default (max_size: 0).

Clear All the Caches

You can clear all the caches with the following code:

Chef::EncryptedAttribute::RemoteClients.cache.clear
Chef::EncryptedAttribute::RemoteNodes.cache.clear
Chef::EncryptedAttribute::RemoteUsers.cache.clear
Chef::EncryptedAttribute::RemoteNode.cache.clear

Disable All the Caches

You can disable all the caches with the following code:

Chef::EncryptedAttribute::RemoteClients.cache.max_size(0)
Chef::EncryptedAttribute::RemoteNodes.cache.max_size(0)
Chef::EncryptedAttribute::RemoteUsers.cache.max_size(0)
Chef::EncryptedAttribute::RemoteNode.cache.max_size(0)

Instance Method Summary collapse

Instance Method Details

#config(arg) ⇒ Config

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Creates a new Config object.

Reads the default configuration from Chef::Config[:encrypted_attributes].

When the parameter is a Config class, all the configuration options will be replaced.

When the parameter is a Hash, only the provided keys will be replaced.

The local node public key will always be added to the provided configuration keys.

Parameters:

  • arg (Config, Hash)

    the configuration to set.

Returns:

  • (Config)

    the read or set configuration object.



214
215
216
217
218
219
220
# File 'lib/chef/encrypted_attribute/api.rb', line 214

def config(arg)
  config =
    EncryptedAttribute::Config.new(Chef::Config[:encrypted_attributes])
  config.update!(arg)
  config.keys(config.keys + [local_node.public_key])
  config
end

#create(value, c = {}) ⇒ EncryptedMash

Creates an encrypted attribute.

The returned value should be saved in a node attribute, like node.normal[...] = Chef::EncryptedAttribute.create(...).

The local node will always be able to decrypt the attribute.

An exception is thrown if any error arises in the encryption process.

Parameters:

  • value (Hash, Array, String, ...)

    the value to be encrypted. Can be a boolean, a number, a string, an array or a hash (the value will be converted to JSON internally).

  • c (Config, Hash) (defaults to: {})

    a configuration hash. For example: { :client_search => "admin:true" }.

Returns:

  • (EncryptedMash)

    encrypted attribute value. This is usually what is saved in the node attributes.

Raises:



312
313
314
315
316
317
318
# File 'lib/chef/encrypted_attribute/api.rb', line 312

def create(value, c = {})
  debug('Creating Encrypted Attribute.')
  enc_attr = EncryptedAttribute.new(config(c))
  result = enc_attr.create(value)
  debug('Encrypted Attribute created.')
  result
end

#create_on_node(name, attr_ary, value, c = {}) ⇒ EncryptedMash

Creates an encrypted attribute on a remote node.

Both the local node and the remote node will be able to decrypt the attribute.

This method requires admin privileges. So in most cases, cannot be used from cookbooks.

An exception is thrown if any error arises in the encryption process.

Parameters:

  • name (String)

    the node name.

  • attr_ary (Array<String>)

    the attribute path as array of strings. For example: %w(myapp ftp_password).

  • value (Hash, Array, String, Fixnum, ...)

    the value to be encrypted. Can be a boolean, a number, a string, an array or a hash (the value will be converted to JSON internally).

  • c (Config, Hash) (defaults to: {})

    a configuration hash. For example: { :client_search => 'admin:true' }.

Returns:

Raises:



358
359
360
361
362
363
364
365
366
# File 'lib/chef/encrypted_attribute/api.rb', line 358

def create_on_node(name, attr_ary, value, c = {})
  debug(
    "Creating Remote Encrypted Attribute on #{name}: #{attr_ary.inspect}"
  )
  enc_attr = EncryptedAttribute.new(config(c))
  result = enc_attr.create_on_node(name, attr_ary, value)
  debug('Encrypted Remote Attribute created.')
  result
end

#debug(msg) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Prints a Chef debug message.

Parameters:

  • msg (String)

    message to print.

Returns:

  • void



177
178
179
# File 'lib/chef/encrypted_attribute/api.rb', line 177

def debug(msg)
  Chef::Log.debug("Chef::EncryptedAttribute: #{msg}")
end

#exist?(hs) ⇒ Boolean

Checks whether an encrypted attribute exists.

Parameters:

  • hs (Mash)

    an encrypted hash, usually a node attribute. The attribute type can be specified but is not necessary. For example: node['myapp']['ftp_password'].

Returns:

  • (Boolean)

    true if an encrypted attribute is found, false if not.



473
474
475
476
477
478
# File 'lib/chef/encrypted_attribute/api.rb', line 473

def exist?(hs)
  debug("Checking if Encrypted Attribute exists here: #{hs.inspect}")
  result = EncryptedMash.exist?(hs)
  debug("Encrypted Attribute #{result ? '' : 'not '}found.")
  result
end

#exist_on_node?(name, attr_ary, c = {}) ⇒ Boolean

Checks whether an encrypted attribute exists in a remote node.

Parameters:

  • name (String)

    the node name.

  • attr_ary (Array<String>)

    the attribute path as array of strings. For example: %w(myapp ftp_password).

  • c (Config, Hash) (defaults to: {})

    a configuration hash. For example: { :partial_search => false }.

Returns:

  • (Boolean)

    true if an encrypted attribute is found, false if not.

Raises:

  • (ArgumentError)

    if the attribute path format is wrong.



501
502
503
504
505
506
507
508
509
510
511
# File 'lib/chef/encrypted_attribute/api.rb', line 501

def exist_on_node?(name, attr_ary, c = {})
  debug("Checking if Remote Encrypted Attribute exists on #{name}")
  remote_node = RemoteNode.new(name)
  config_merged = config(c)
  node_attr =
    remote_node.load_attribute(
      attr_ary, config_merged.search_max_rows,
      config_merged.partial_search
    )
  Chef::EncryptedAttribute.exist?(node_attr)
end

#exists?(*args) ⇒ Boolean

Deprecated.

Use #exist? instead.

Checks whether an encrypted attribute exists in a remote node.

Parameters:

  • args (Mixed)

    #exist? arguments.

Returns:

  • (Boolean)

    true if an encrypted attribute is found, false if not.



486
487
488
489
# File 'lib/chef/encrypted_attribute/api.rb', line 486

def exists?(*args)
  warn("#{name}.exists? is deprecated in favor of #{name}.exist?.")
  exist?(*args)
end

#exists_on_node?(*args) ⇒ Boolean

Deprecated.

Use #exist_on_node? instead.

Checks whether an encrypted attribute exists in a remote node.

Parameters:

Returns:

  • (Boolean)

    true if an encrypted attribute is found, false if not.



519
520
521
522
523
524
525
# File 'lib/chef/encrypted_attribute/api.rb', line 519

def exists_on_node?(*args)
  warn(
    "#{name}.exists_on_node? is deprecated in favor of "\
    "#{name}.exist_on_node?."
  )
  exist_on_node?(*args)
end

#load(enc_hs, c = {}) ⇒ Hash, ...

Reads an encrypted attribute from a hash, usually a node attribute.

Uses the local private key to decrypt the attribute.

An exception is thrown if the attribute cannot be decrypted or no encrypted attribute is found.

Parameters:

  • enc_hs (Mash)

    an encrypted hash, usually a node attribute. For example: node['myapp']['ftp_password'].

  • c (Config, Hash) (defaults to: {})

    a configuration hash. For example: { :partial_search => false }.

Returns:

  • (Hash, Array, String, ...)

    the attribute in clear text, decrypted.

Raises:



239
240
241
242
243
244
245
# File 'lib/chef/encrypted_attribute/api.rb', line 239

def load(enc_hs, c = {})
  debug("Loading Local Encrypted Attribute from: #{enc_hs.inspect}")
  enc_attr = EncryptedAttribute.new(config(c))
  result = enc_attr.load(enc_hs)
  debug('Local Encrypted Attribute loaded.')
  result
end

#load_from_node(name, attr_ary, c = {}) ⇒ Hash, ...

Reads an encrypted attribute from a remote node.

Uses the local private key to decrypt the attribute.

An exception is thrown if the attribute cannot be decrypted or no encrypted attribute is found.

Parameters:

  • name (String)

    the node name.

  • attr_ary (Array<String>)

    the attribute path as array of strings. For example: %w(myapp ftp_password).

  • c (Config, Hash) (defaults to: {})

    a configuration hash. For example: { :partial_search => false }.

Returns:

  • (Hash, Array, String, ...)

    decrypted attribute value.

Raises:



268
269
270
271
272
273
274
275
276
# File 'lib/chef/encrypted_attribute/api.rb', line 268

def load_from_node(name, attr_ary, c = {})
  debug(
    "Loading Remote Encrypted Attribute from #{name}: #{attr_ary.inspect}"
  )
  enc_attr = EncryptedAttribute.new(config(c))
  result = enc_attr.load_from_node(name, attr_ary)
  debug('Remote Encrypted Attribute loaded.')
  result
end

#local_nodeLocalNode

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Gets local node object.

Returns:



194
195
196
# File 'lib/chef/encrypted_attribute/api.rb', line 194

def local_node
  LocalNode.new
end

#update(enc_hs, c = {}) ⇒ Boolean

Updates who can read the attribute. This is intended to be used to update to the new nodes returned by :client_search and :node_search or perhaps global configuration changes.

For example, in case new nodes are added or some are removed, and the clients returned by :client_search or :node_search are different, this #update method will decrypt the attribute and encrypt it again for the new nodes (or remove the old ones).

If an update is made, the shared secrets are regenerated.

Both the local node and the remote node will be able to decrypt the attribute.

An exception is thrown if there is any error in the updating process.

Parameters:

  • enc_hs (Mash)

    This must be a node encrypted attribute, this attribute will be updated, so it is mandatory to specify the type (usually normal). For example: node.normal['myapp']['ftp_password'].

  • c (Config, Hash) (defaults to: {})

    a configuration hash. Surely you want this #update method to use the same config that the #create call.

Returns:

  • (Boolean)

    true if the encrypted attribute has been updated, false if not.

Raises:



410
411
412
413
414
415
416
417
418
419
420
# File 'lib/chef/encrypted_attribute/api.rb', line 410

def update(enc_hs, c = {})
  debug("Updating Encrypted Attribute: #{enc_hs.inspect}")
  enc_attr = EncryptedAttribute.new(config(c))
  result = enc_attr.update(enc_hs)
  if result
    debug('Encrypted Attribute updated.')
  else
    debug('Encrypted Attribute not updated.')
  end
  result
end

#update_on_node(name, attr_ary, c = {}) ⇒ Boolean

Updates who can decrypt the remote attribute.

This method requires admin privileges. So in most cases, cannot be used from cookbooks.

An exception is thrown if there is any error in the updating process.

Parameters:

  • name (String)

    the node name.

  • attr_ary (Array<String>)

    the attribute path as array of strings. For example: %w(myapp ftp_password).

  • c (Config, Hash) (defaults to: {})

    a configuration hash. Surely you want this #update_on_node method to use the same config that the #create call.

Returns:

  • (Boolean)

    true if the encrypted attribute has been updated, false if not.

Raises:



456
457
458
459
460
461
462
463
464
# File 'lib/chef/encrypted_attribute/api.rb', line 456

def update_on_node(name, attr_ary, c = {})
  debug(
    "Updating Remote Encrypted Attribute on #{name}: #{attr_ary.inspect}"
  )
  enc_attr = EncryptedAttribute.new(config(c))
  result = enc_attr.update_on_node(name, attr_ary)
  debug("Encrypted Remote Attribute #{result ? '' : 'not '}updated.")
  result
end

#warn(msg) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Prints a Chef warning message.

Parameters:

  • msg (String)

    message to print.

Returns:

  • void



186
187
188
# File 'lib/chef/encrypted_attribute/api.rb', line 186

def warn(msg)
  Chef::Log.warn(msg)
end