diff options
author | tyler-ball <tyleraball@gmail.com> | 2014-09-11 08:48:52 -0700 |
---|---|---|
committer | tyler-ball <tyleraball@gmail.com> | 2014-09-29 08:31:08 -0700 |
commit | 71f7c6e463220cf492d5ac38c2cfbeb96defbeba (patch) | |
tree | facfe12a1494a8bdc9369f3ebaff90fb916b3084 /lib/chef/knife/data_bag_secret_options.rb | |
parent | 1852c179a7f84a42b591689eb5076defa90c7431 (diff) | |
download | chef-71f7c6e463220cf492d5ac38c2cfbeb96defbeba.tar.gz |
Addressing review comments - better naming, comments, removed some TODOs
Diffstat (limited to 'lib/chef/knife/data_bag_secret_options.rb')
-rw-r--r-- | lib/chef/knife/data_bag_secret_options.rb | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/lib/chef/knife/data_bag_secret_options.rb b/lib/chef/knife/data_bag_secret_options.rb new file mode 100644 index 0000000000..8b9c947bac --- /dev/null +++ b/lib/chef/knife/data_bag_secret_options.rb @@ -0,0 +1,139 @@ +# +# Author:: Tyler Ball (<tball@opscode.com>) +# Copyright:: Copyright (c) 2014 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'mixlib/cli' +require 'chef/config' + +class Chef + class Knife + module DataBagSecretOptions + include Mixlib::CLI + + def self.included(base) + base.option :secret, + :short => "-s SECRET", + :long => "--secret ", + :description => "The secret key to use to encrypt data bag item values. Can also be defaulted in your knife.rb with the key 'secret'", + :proc => Proc.new { |s| Chef::Config[:knife][:secret] = s } + + base.option :secret_file, + :long => "--secret-file SECRET_FILE", + :description => "A file containing the secret key to use to encrypt data bag item values. Can also be defaulted in your knife.rb with the key 'secret_file'", + :proc => Proc.new { |sf| Chef::Config[:knife][:secret_file] = sf } + + base.option :encrypt, + :long => "--encrypt", + :description => "If 'secret' or 'secret_file' is present in your knife.rb, then encrypt data bags using it", + :boolean => true, + :default => false + end + + ## + # Determine if the user has specified an appropriate secret for encrypting data bag items. + # @returns boolean + def encryption_secret_provided? + validate_secrets + + return true if config[:secret] || config[:secret_file] + + if config[:encrypt] + unless has_secret? || has_secret_file? + ui.fatal("No secret or secret_file specified in config, unable to encrypt item.") + exit(1) + else + return true + end + end + return false + end + + def read_secret + # Moving the non 'compile-time' requires into here to speed up knife command loading + # IE, if we are not running 'knife data bag *' we don't need to load 'chef/encrypted_data_bag_item' + require 'chef/encrypted_data_bag_item' + + if config[:secret] + config[:secret] + elsif config[:secret_file] + Chef::EncryptedDataBagItem.load_secret(config[:secret_file]) + elsif secret = knife_config[:secret] || Chef::Config[:secret] + secret + else + secret_file = knife_config[:secret_file] || Chef::Config[:secret_file] + Chef::EncryptedDataBagItem.load_secret(secret_file) + end + end + + def validate_secrets + if config[:secret] && config[:secret_file] + ui.fatal("Please specify only one of --secret, --secret-file") + exit(1) + end + + if has_secret? && has_secret_file? + ui.fatal("Please specify only one of 'secret' or 'secret_file' in your config") + exit(1) + end + end + + def has_secret? + knife_config[:secret] || Chef::Config[:secret] + end + + def has_secret_file? + knife_config[:secret_file] || Chef::Config[:secret_file] + end + + # TODO duplicated from data_query.rb, also needs test coverage when it is extracted + # Tries to autodetect if the item's raw hash appears to be encrypted. + def encrypted?(raw_data) + data = raw_data.reject { |k, _| k == "id" } # Remove the "id" key. + # Assume hashes containing only the "id" key are not encrypted. + # Otherwise, remove the keys that don't appear to be encrypted and compare + # the result with the hash. If some entry has been removed, then some entry + # doesn't appear to be encrypted and we assume the entire hash is not encrypted. + data.empty? ? false : data.reject { |_, v| !looks_like_encrypted?(v) } == data + end + + private + + def knife_config + Chef::Config.key?(:knife) ? Chef::Config[:knife] : {} + end + + # Checks if data looks like it has been encrypted by + # Chef::EncryptedDataBagItem::Encryptor::VersionXEncryptor. Returns + # true only when there is an exact match between the VersionXEncryptor + # keys and the hash's keys. + def looks_like_encrypted?(data) + return false unless data.is_a?(Hash) && data.has_key?("version") + case data["version"] + when 1 + Chef::EncryptedDataBagItem::Encryptor::Version1Encryptor.encryptor_keys.sort == data.keys.sort + when 2 + Chef::EncryptedDataBagItem::Encryptor::Version2Encryptor.encryptor_keys.sort == data.keys.sort + when 3 + Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor.encryptor_keys.sort == data.keys.sort + else + false # version means something else... assume not encrypted. + end + end + + end + end +end |