module Chef::EncryptedAttribute::API
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](docs.chef.io/recipes.html) or [Resources](docs.chef.io/resource.html).
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` 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` 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] (en.wikipedia.org/wiki/Galois/Counter_Mode) 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 ofChef
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.
@see Config
For example, to disable Partial Search globally:
“`ruby Chef::Config[:partial_search] = false
# ftp_pass = Chef::EncryptedAttribute.load(node['myapp']
['ftp_password']) # … “`
To disable Partial Search locally:
“`ruby ftp_pass = Chef::EncryptedAttribute.load
(
node['myapp']['ftp_password'], :partial_search => false
) “`
To use protocol version 2 globally, which uses [GCM] (en.wikipedia.org/wiki/Galois/Counter_Mode):
“`ruby Chef::Config[: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] (README.md#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).
@see CacheLru
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:
“`ruby 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:
“`ruby 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) “`
@see RemoteClients.cache
@see RemoteNodes.cache
@see RemoteUsers.cache
@see RemoteNode.cache
Public Instance Methods
Creates a new {Config} object.
Reads the default configuration from `Chef::Config`.
When the parameter is a {Chef::EncryptedAttribute::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.
@param arg [Config, Hash] the configuration to set. @return [Config] the read or set configuration object. @api private
# 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
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.
@param 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).
@param c [Config, Hash] a configuration hash. For example:
`{ :client_search => "admin:true" }`.
@return [EncryptedMash] encrypted attribute value. This is usually what
is saved in the node attributes.
@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.
# 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
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.
@param name [String] the node name. @param attr_ary [Array<String>] the attribute path as *array of
strings*. For example: `%w(myapp ftp_password)`.
@param 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).
@param c [Config, Hash] a configuration hash. For example:
`{ :client_search => 'admin:true' }`.
@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/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
Prints a Chef
debug message.
@param msg [String] message to print. @return void @api private
# File lib/chef/encrypted_attribute/api.rb, line 177 def debug(msg) Chef::Log.debug("Chef::EncryptedAttribute: #{msg}") end
Checks whether an encrypted attribute exists.
@param 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']`.
@return [Boolean] `true` if an encrypted attribute is found, `false` if
not.
# 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
Checks whether an encrypted attribute exists in a remote node.
@param name [String] the node name. @param attr_ary [Array<String>] the attribute path as *array of
strings*. For example: `%w(myapp ftp_password)`.
@param c [Config, Hash] a configuration hash. For example:
`{ :partial_search => false }`.
@return [Boolean] `true` if an encrypted attribute is found, `false` if
not.
@raise [ArgumentError] if the attribute path format is wrong.
# 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
Checks whether an encrypted attribute exists in a remote node.
@param [Mixed] args {#exist?} arguments. @return [Boolean] `true` if an encrypted attribute is found, `false` if
not.
@deprecated Use {#exist?} instead.
# 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
Checks whether an encrypted attribute exists in a remote node.
@param [Mixed] args {#exist_on_node?} arguments. @return [Boolean] `true` if an encrypted attribute is found, `false` if
not.
@deprecated Use {#exist_on_node?} instead.
# 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
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.
@param enc_hs [Mash] an encrypted hash, usually a node attribute. For
example: `node['myapp']['ftp_password']`.
@param c [Config, Hash] a configuration hash. For example:
`{ :partial_search => false }`.
@return [Hash, Array, String, …] the attribute in clear text,
decrypted.
@raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute
format is wrong.
@raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute
format is not supported or unknown.
# 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
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.
@param name [String] the node name. @param attr_ary [Array<String>] the attribute path as *array of
strings*. For example: `%w(myapp ftp_password)`.
@param c [Config, Hash] a configuration hash. For example:
`{ :partial_search => false }`.
@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/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
Gets local node object.
@return [LocalNode] local node object. @api private
# File lib/chef/encrypted_attribute/api.rb, line 194 def local_node LocalNode.new end
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.
@param 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']`.
@param c [Config, Hash] a configuration hash. Surely you want this
`#update` method to use the same `config` that the `#create` call.
@return [Boolean] `true` if the encrypted attribute has been updated,
`false` if not.
@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.
# 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
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.
@param name [String] the node name. @param attr_ary [Array<String>] the attribute path as *array of
strings*. For example: `%w(myapp ftp_password)`.
@param c [Config, Hash] a configuration hash. Surely you want this
`#update_on_node` method to use the same `config` that the `#create` call.
@return [Boolean] `true` if the encrypted attribute has been updated,
`false` if not.
@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/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
Prints a Chef
warning message.
@param msg [String] message to print. @return void @api private
# File lib/chef/encrypted_attribute/api.rb, line 186 def warn(msg) Chef::Log.warn(msg) end